mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 08:45:50 +00:00
fix: 修复 USER_TYPE=ant 时 TUI 无法启动的问题
反编译版本中 global.d.ts 声明的全局函数运行时未定义, 通过显式 import、stub 组件和全局 polyfill 修复。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
12
DEV-LOG.md
Normal file
12
DEV-LOG.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# DEV-LOG
|
||||||
|
|
||||||
|
## USER_TYPE=ant TUI 修复 (2026-04-02)
|
||||||
|
|
||||||
|
`global.d.ts` 声明的全局函数在反编译版本运行时未定义,导致 `USER_TYPE=ant` 时 TUI 崩溃。
|
||||||
|
|
||||||
|
修复方式:显式 import / 本地 stub / 全局 stub / 新建 stub 文件。涉及文件:
|
||||||
|
`cli.tsx`, `model.ts`, `context.ts`, `effort.ts`, `thinking.ts`, `undercover.ts`, `Spinner.tsx`, `AntModelSwitchCallout.tsx`(新建), `UndercoverAutoCallout.tsx`(新建)
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- `USER_TYPE=ant` 启用 alt-screen 全屏模式,中心区域满屏是预期行为
|
||||||
|
- `global.d.ts` 中剩余未 stub 的全局函数(`getAntModels` 等)遇到 `X is not defined` 时按同样模式处理
|
||||||
12
src/components/AntModelSwitchCallout.tsx
Normal file
12
src/components/AntModelSwitchCallout.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Stub — ant-only component, not available in decompiled build
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function AntModelSwitchCallout(_props: {
|
||||||
|
onDone: (selection: string, modelAlias?: string) => void;
|
||||||
|
}): React.ReactElement | null {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldShowModelSwitchCallout(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { c as _c } from "react/compiler-runtime";
|
import { c as _c } from 'react/compiler-runtime';
|
||||||
// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered
|
// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered
|
||||||
import { Box, Text } from '../ink.js';
|
import { Box, Text } from '../ink.js';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
@@ -46,7 +46,15 @@ type Props = {
|
|||||||
pauseStartTimeRef: React.RefObject<number | null>;
|
pauseStartTimeRef: React.RefObject<number | null>;
|
||||||
spinnerTip?: string;
|
spinnerTip?: string;
|
||||||
responseLengthRef: React.RefObject<number>;
|
responseLengthRef: React.RefObject<number>;
|
||||||
apiMetricsRef?: React.RefObject<Array<{ ttftMs: number; firstTokenTime: number; lastTokenTime: number; responseLengthBaseline: number; endResponseLength: number }>>;
|
apiMetricsRef?: React.RefObject<
|
||||||
|
Array<{
|
||||||
|
ttftMs: number;
|
||||||
|
firstTokenTime: number;
|
||||||
|
lastTokenTime: number;
|
||||||
|
responseLengthBaseline: number;
|
||||||
|
endResponseLength: number;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
overrideColor?: keyof Theme | null;
|
overrideColor?: keyof Theme | null;
|
||||||
overrideShimmerColor?: keyof Theme | null;
|
overrideShimmerColor?: keyof Theme | null;
|
||||||
overrideMessage?: string | null;
|
overrideMessage?: string | null;
|
||||||
@@ -57,6 +65,9 @@ type Props = {
|
|||||||
leaderIsIdle?: boolean;
|
leaderIsIdle?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Polyfill ant-only global functions that are normally injected by the bundler.
|
||||||
|
const computeTtftText = (metrics: ApiMetricEntry[]): string => '';
|
||||||
|
|
||||||
// Thin wrapper: branches on isBriefOnly so the two variants have independent
|
// Thin wrapper: branches on isBriefOnly so the two variants have independent
|
||||||
// hook call chains. Without this split, toggling /brief mid-render would
|
// hook call chains. Without this split, toggling /brief mid-render would
|
||||||
// violate Rules of Hooks (the inner variant calls ~10 more hooks).
|
// violate Rules of Hooks (the inner variant calls ~10 more hooks).
|
||||||
@@ -68,14 +79,22 @@ export function SpinnerWithVerb(props: Props): React.ReactNode {
|
|||||||
// teammate view needs the real spinner (which shows teammate status).
|
// teammate view needs the real spinner (which shows teammate status).
|
||||||
const viewingAgentTaskId = useAppState(s_0 => s_0.viewingAgentTaskId);
|
const viewingAgentTaskId = useAppState(s_0 => s_0.viewingAgentTaskId);
|
||||||
// Hoisted to mount-time — this component re-renders at animation framerate.
|
// Hoisted to mount-time — this component re-renders at animation framerate.
|
||||||
const briefEnvEnabled = feature('KAIROS') || feature('KAIROS_BRIEF') ?
|
const briefEnvEnabled =
|
||||||
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
feature('KAIROS') || feature('KAIROS_BRIEF')
|
||||||
useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_BRIEF), []) : false;
|
? // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
||||||
|
useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_BRIEF), [])
|
||||||
|
: false;
|
||||||
|
|
||||||
// Runtime gate mirrors isBriefEnabled() but inlined — importing from
|
// Runtime gate mirrors isBriefEnabled() but inlined — importing from
|
||||||
// BriefTool.ts would leak tool-name strings into external builds. Single
|
// BriefTool.ts would leak tool-name strings into external builds. Single
|
||||||
// spinner instance → hooks stay unconditional (two subs, negligible).
|
// spinner instance → hooks stay unconditional (two subs, negligible).
|
||||||
if ((feature('KAIROS') || feature('KAIROS_BRIEF')) && (getKairosActive() || getUserMsgOptIn() && (briefEnvEnabled || getFeatureValue_CACHED_MAY_BE_STALE('tengu_kairos_brief', false))) && isBriefOnly && !viewingAgentTaskId) {
|
if (
|
||||||
|
(feature('KAIROS') || feature('KAIROS_BRIEF')) &&
|
||||||
|
(getKairosActive() ||
|
||||||
|
(getUserMsgOptIn() && (briefEnvEnabled || getFeatureValue_CACHED_MAY_BE_STALE('tengu_kairos_brief', false)))) &&
|
||||||
|
isBriefOnly &&
|
||||||
|
!viewingAgentTaskId
|
||||||
|
) {
|
||||||
return <BriefSpinner mode={props.mode} overrideMessage={props.overrideMessage} />;
|
return <BriefSpinner mode={props.mode} overrideMessage={props.overrideMessage} />;
|
||||||
}
|
}
|
||||||
return <SpinnerWithVerbInner {...props} />;
|
return <SpinnerWithVerbInner {...props} />;
|
||||||
@@ -87,13 +106,14 @@ function SpinnerWithVerbInner({
|
|||||||
pauseStartTimeRef,
|
pauseStartTimeRef,
|
||||||
spinnerTip,
|
spinnerTip,
|
||||||
responseLengthRef,
|
responseLengthRef,
|
||||||
|
apiMetricsRef,
|
||||||
overrideColor,
|
overrideColor,
|
||||||
overrideShimmerColor,
|
overrideShimmerColor,
|
||||||
overrideMessage,
|
overrideMessage,
|
||||||
spinnerSuffix,
|
spinnerSuffix,
|
||||||
verbose,
|
verbose,
|
||||||
hasActiveTools = false,
|
hasActiveTools = false,
|
||||||
leaderIsIdle = false
|
leaderIsIdle = false,
|
||||||
}: Props): React.ReactNode {
|
}: Props): React.ReactNode {
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const reducedMotion = settings.prefersReducedMotion ?? false;
|
const reducedMotion = settings.prefersReducedMotion ?? false;
|
||||||
@@ -112,13 +132,13 @@ function SpinnerWithVerbInner({
|
|||||||
const selectedIPAgentIndex = useAppState(s_2 => s_2.selectedIPAgentIndex);
|
const selectedIPAgentIndex = useAppState(s_2 => s_2.selectedIPAgentIndex);
|
||||||
const viewSelectionMode = useAppState(s_3 => s_3.viewSelectionMode);
|
const viewSelectionMode = useAppState(s_3 => s_3.viewSelectionMode);
|
||||||
// Get foregrounded teammate (if viewing a teammate's transcript)
|
// Get foregrounded teammate (if viewing a teammate's transcript)
|
||||||
const foregroundedTeammate = viewingAgentTaskId ? getViewedTeammateTask({
|
const foregroundedTeammate = viewingAgentTaskId
|
||||||
|
? getViewedTeammateTask({
|
||||||
viewingAgentTaskId,
|
viewingAgentTaskId,
|
||||||
tasks
|
tasks,
|
||||||
}) : undefined;
|
})
|
||||||
const {
|
: undefined;
|
||||||
columns
|
const { columns } = useTerminalSize();
|
||||||
} = useTerminalSize();
|
|
||||||
const tasksV2 = useTasksV2();
|
const tasksV2 = useTasksV2();
|
||||||
|
|
||||||
// Track thinking status: 'thinking' | number (duration in ms) | null
|
// Track thinking status: 'thinking' | number (duration in ms) | null
|
||||||
@@ -168,7 +188,10 @@ function SpinnerWithVerbInner({
|
|||||||
|
|
||||||
// Leader's own verb (always the leader's, regardless of who is foregrounded)
|
// Leader's own verb (always the leader's, regardless of who is foregrounded)
|
||||||
const leaderVerb = overrideMessage ?? currentTodo?.activeForm ?? currentTodo?.subject ?? randomVerb;
|
const leaderVerb = overrideMessage ?? currentTodo?.activeForm ?? currentTodo?.subject ?? randomVerb;
|
||||||
const effectiveVerb = foregroundedTeammate && !foregroundedTeammate.isIdle ? foregroundedTeammate.spinnerVerb ?? randomVerb : leaderVerb;
|
const effectiveVerb =
|
||||||
|
foregroundedTeammate && !foregroundedTeammate.isIdle
|
||||||
|
? (foregroundedTeammate.spinnerVerb ?? randomVerb)
|
||||||
|
: leaderVerb;
|
||||||
const message = effectiveVerb + '…';
|
const message = effectiveVerb + '…';
|
||||||
|
|
||||||
// Track CLI activity when spinner is active
|
// Track CLI activity when spinner is active
|
||||||
@@ -203,7 +226,10 @@ function SpinnerWithVerbInner({
|
|||||||
// Stale read of the refs for showBtwTip below — we're off the 50ms clock
|
// Stale read of the refs for showBtwTip below — we're off the 50ms clock
|
||||||
// so this only updates when props/app state change, which is sufficient for
|
// so this only updates when props/app state change, which is sufficient for
|
||||||
// a coarse 30s threshold.
|
// a coarse 30s threshold.
|
||||||
const elapsedSnapshot = pauseStartTimeRef.current !== null ? pauseStartTimeRef.current - loadingStartTimeRef.current - totalPausedMsRef.current : Date.now() - loadingStartTimeRef.current - totalPausedMsRef.current;
|
const elapsedSnapshot =
|
||||||
|
pauseStartTimeRef.current !== null
|
||||||
|
? pauseStartTimeRef.current - loadingStartTimeRef.current - totalPausedMsRef.current
|
||||||
|
: Date.now() - loadingStartTimeRef.current - totalPausedMsRef.current;
|
||||||
|
|
||||||
// Leader token count for TeammateSpinnerTree — read raw (non-animated) from
|
// Leader token count for TeammateSpinnerTree — read raw (non-animated) from
|
||||||
// the ref. The tree is only shown when teammates are running; teammate
|
// the ref. The tree is only shown when teammates are running; teammate
|
||||||
@@ -220,7 +246,7 @@ function SpinnerWithVerbInner({
|
|||||||
// doesn't trigger re-renders; we pick up updates on the parent's ~25x/turn
|
// doesn't trigger re-renders; we pick up updates on the parent's ~25x/turn
|
||||||
// re-render cadence, same as the old ApiMetricsLine did.
|
// re-render cadence, same as the old ApiMetricsLine did.
|
||||||
let ttftText: string | null = null;
|
let ttftText: string | null = null;
|
||||||
if ((process.env.USER_TYPE) === 'ant' && apiMetricsRef?.current && apiMetricsRef.current.length > 0) {
|
if (process.env.USER_TYPE === 'ant' && apiMetricsRef?.current && apiMetricsRef.current.length > 0) {
|
||||||
ttftText = computeTtftText(apiMetricsRef.current);
|
ttftText = computeTtftText(apiMetricsRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,26 +254,49 @@ function SpinnerWithVerbInner({
|
|||||||
// show a static dim idle display instead of the animated spinner — otherwise
|
// show a static dim idle display instead of the animated spinner — otherwise
|
||||||
// useStalledAnimation detects no new tokens after 3s and turns the spinner red.
|
// useStalledAnimation detects no new tokens after 3s and turns the spinner red.
|
||||||
if (leaderIsIdle && hasRunningTeammates && !foregroundedTeammate) {
|
if (leaderIsIdle && hasRunningTeammates && !foregroundedTeammate) {
|
||||||
return <Box flexDirection="column" width="100%" alignItems="flex-start">
|
return (
|
||||||
|
<Box flexDirection="column" width="100%" alignItems="flex-start">
|
||||||
<Box flexDirection="row" flexWrap="wrap" marginTop={1} width="100%">
|
<Box flexDirection="row" flexWrap="wrap" marginTop={1} width="100%">
|
||||||
<Text dimColor>
|
<Text dimColor>
|
||||||
{TEARDROP_ASTERISK} Idle
|
{TEARDROP_ASTERISK} Idle
|
||||||
{!allIdle && ' · teammates running'}
|
{!allIdle && ' · teammates running'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{showSpinnerTree && <TeammateSpinnerTree selectedIndex={selectedIPAgentIndex} isInSelectionMode={viewSelectionMode === 'selecting-agent'} allIdle={allIdle} leaderTokenCount={leaderTokenCount} leaderIdleText="Idle" />}
|
{showSpinnerTree && (
|
||||||
</Box>;
|
<TeammateSpinnerTree
|
||||||
|
selectedIndex={selectedIPAgentIndex}
|
||||||
|
isInSelectionMode={viewSelectionMode === 'selecting-agent'}
|
||||||
|
allIdle={allIdle}
|
||||||
|
leaderTokenCount={leaderTokenCount}
|
||||||
|
leaderIdleText="Idle"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When viewing an idle teammate, show static idle display instead of animated spinner
|
// When viewing an idle teammate, show static idle display instead of animated spinner
|
||||||
if (foregroundedTeammate?.isIdle) {
|
if (foregroundedTeammate?.isIdle) {
|
||||||
const idleText = allIdle ? `${TEARDROP_ASTERISK} Worked for ${formatDuration(Date.now() - foregroundedTeammate.startTime)}` : `${TEARDROP_ASTERISK} Idle`;
|
const idleText = allIdle
|
||||||
return <Box flexDirection="column" width="100%" alignItems="flex-start">
|
? `${TEARDROP_ASTERISK} Worked for ${formatDuration(Date.now() - foregroundedTeammate.startTime)}`
|
||||||
|
: `${TEARDROP_ASTERISK} Idle`;
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column" width="100%" alignItems="flex-start">
|
||||||
<Box flexDirection="row" flexWrap="wrap" marginTop={1} width="100%">
|
<Box flexDirection="row" flexWrap="wrap" marginTop={1} width="100%">
|
||||||
<Text dimColor>{idleText}</Text>
|
<Text dimColor>{idleText}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{showSpinnerTree && hasRunningTeammates && <TeammateSpinnerTree selectedIndex={selectedIPAgentIndex} isInSelectionMode={viewSelectionMode === 'selecting-agent'} allIdle={allIdle} leaderVerb={leaderIsIdle ? undefined : leaderVerb} leaderIdleText={leaderIsIdle ? 'Idle' : undefined} leaderTokenCount={leaderTokenCount} />}
|
{showSpinnerTree && hasRunningTeammates && (
|
||||||
</Box>;
|
<TeammateSpinnerTree
|
||||||
|
selectedIndex={selectedIPAgentIndex}
|
||||||
|
isInSelectionMode={viewSelectionMode === 'selecting-agent'}
|
||||||
|
allIdle={allIdle}
|
||||||
|
leaderVerb={leaderIsIdle ? undefined : leaderVerb}
|
||||||
|
leaderIdleText={leaderIsIdle ? 'Idle' : undefined}
|
||||||
|
leaderTokenCount={leaderTokenCount}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time-based tip overrides: coarse thresholds so a stale ref read (we're
|
// Time-based tip overrides: coarse thresholds so a stale ref read (we're
|
||||||
@@ -257,7 +306,13 @@ function SpinnerWithVerbInner({
|
|||||||
const tipsEnabled = settings.spinnerTipsEnabled !== false;
|
const tipsEnabled = settings.spinnerTipsEnabled !== false;
|
||||||
const showClearTip = tipsEnabled && elapsedSnapshot > 1_800_000;
|
const showClearTip = tipsEnabled && elapsedSnapshot > 1_800_000;
|
||||||
const showBtwTip = tipsEnabled && elapsedSnapshot > 30_000 && !getGlobalConfig().btwUseCount;
|
const showBtwTip = tipsEnabled && elapsedSnapshot > 30_000 && !getGlobalConfig().btwUseCount;
|
||||||
const effectiveTip = contextTipsActive ? undefined : showClearTip && !nextTask ? 'Use /clear to start fresh when switching topics and free up context' : showBtwTip && !nextTask ? "Use /btw to ask a quick side question without interrupting Claude's current work" : spinnerTip;
|
const effectiveTip = contextTipsActive
|
||||||
|
? undefined
|
||||||
|
: showClearTip && !nextTask
|
||||||
|
? 'Use /clear to start fresh when switching topics and free up context'
|
||||||
|
: showBtwTip && !nextTask
|
||||||
|
? "Use /btw to ask a quick side question without interrupting Claude's current work"
|
||||||
|
: spinnerTip;
|
||||||
|
|
||||||
// Budget text (ant-only) — shown above the tip line
|
// Budget text (ant-only) — shown above the tip line
|
||||||
let budgetText: string | null = null;
|
let budgetText: string | null = null;
|
||||||
@@ -268,37 +323,77 @@ function SpinnerWithVerbInner({
|
|||||||
if (tokens >= budget) {
|
if (tokens >= budget) {
|
||||||
budgetText = `Target: ${formatNumber(tokens)} used (${formatNumber(budget)} min ${figures.tick})`;
|
budgetText = `Target: ${formatNumber(tokens)} used (${formatNumber(budget)} min ${figures.tick})`;
|
||||||
} else {
|
} else {
|
||||||
const pct = Math.round(tokens / budget * 100);
|
const pct = Math.round((tokens / budget) * 100);
|
||||||
const remaining = budget - tokens;
|
const remaining = budget - tokens;
|
||||||
const rate = elapsedSnapshot > 5000 && tokens >= 2000 ? tokens / elapsedSnapshot : 0;
|
const rate = elapsedSnapshot > 5000 && tokens >= 2000 ? tokens / elapsedSnapshot : 0;
|
||||||
const eta = rate > 0 ? ` \u00B7 ~${formatDuration(remaining / rate, {
|
const eta =
|
||||||
mostSignificantOnly: true
|
rate > 0
|
||||||
})}` : '';
|
? ` \u00B7 ~${formatDuration(remaining / rate, {
|
||||||
|
mostSignificantOnly: true,
|
||||||
|
})}`
|
||||||
|
: '';
|
||||||
budgetText = `Target: ${formatNumber(tokens)} / ${formatNumber(budget)} (${pct}%)${eta}`;
|
budgetText = `Target: ${formatNumber(tokens)} / ${formatNumber(budget)} (${pct}%)${eta}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return <Box flexDirection="column" width="100%" alignItems="flex-start">
|
return (
|
||||||
<SpinnerAnimationRow mode={mode} reducedMotion={reducedMotion} hasActiveTools={hasActiveTools} responseLengthRef={responseLengthRef} message={message} messageColor={messageColor} shimmerColor={shimmerColor} overrideColor={overrideColor} loadingStartTimeRef={loadingStartTimeRef} totalPausedMsRef={totalPausedMsRef} pauseStartTimeRef={pauseStartTimeRef} spinnerSuffix={spinnerSuffix} verbose={verbose} columns={columns} hasRunningTeammates={hasRunningTeammates} teammateTokens={teammateTokens} foregroundedTeammate={foregroundedTeammate} leaderIsIdle={leaderIsIdle} thinkingStatus={thinkingStatus} effortSuffix={effortSuffix} />
|
<Box flexDirection="column" width="100%" alignItems="flex-start">
|
||||||
{showSpinnerTree && hasRunningTeammates ? <TeammateSpinnerTree selectedIndex={selectedIPAgentIndex} isInSelectionMode={viewSelectionMode === 'selecting-agent'} allIdle={allIdle} leaderVerb={leaderIsIdle ? undefined : leaderVerb} leaderIdleText={leaderIsIdle ? 'Idle' : undefined} leaderTokenCount={leaderTokenCount} /> : showExpandedTodos && tasksV2 && tasksV2.length > 0 ? <Box width="100%" flexDirection="column">
|
<SpinnerAnimationRow
|
||||||
|
mode={mode}
|
||||||
|
reducedMotion={reducedMotion}
|
||||||
|
hasActiveTools={hasActiveTools}
|
||||||
|
responseLengthRef={responseLengthRef}
|
||||||
|
message={message}
|
||||||
|
messageColor={messageColor}
|
||||||
|
shimmerColor={shimmerColor}
|
||||||
|
overrideColor={overrideColor}
|
||||||
|
loadingStartTimeRef={loadingStartTimeRef}
|
||||||
|
totalPausedMsRef={totalPausedMsRef}
|
||||||
|
pauseStartTimeRef={pauseStartTimeRef}
|
||||||
|
spinnerSuffix={spinnerSuffix}
|
||||||
|
verbose={verbose}
|
||||||
|
columns={columns}
|
||||||
|
hasRunningTeammates={hasRunningTeammates}
|
||||||
|
teammateTokens={teammateTokens}
|
||||||
|
foregroundedTeammate={foregroundedTeammate}
|
||||||
|
leaderIsIdle={leaderIsIdle}
|
||||||
|
thinkingStatus={thinkingStatus}
|
||||||
|
effortSuffix={effortSuffix}
|
||||||
|
/>
|
||||||
|
{showSpinnerTree && hasRunningTeammates ? (
|
||||||
|
<TeammateSpinnerTree
|
||||||
|
selectedIndex={selectedIPAgentIndex}
|
||||||
|
isInSelectionMode={viewSelectionMode === 'selecting-agent'}
|
||||||
|
allIdle={allIdle}
|
||||||
|
leaderVerb={leaderIsIdle ? undefined : leaderVerb}
|
||||||
|
leaderIdleText={leaderIsIdle ? 'Idle' : undefined}
|
||||||
|
leaderTokenCount={leaderTokenCount}
|
||||||
|
/>
|
||||||
|
) : showExpandedTodos && tasksV2 && tasksV2.length > 0 ? (
|
||||||
|
<Box width="100%" flexDirection="column">
|
||||||
<MessageResponse>
|
<MessageResponse>
|
||||||
<TaskListV2 tasks={tasksV2} />
|
<TaskListV2 tasks={tasksV2} />
|
||||||
</MessageResponse>
|
</MessageResponse>
|
||||||
</Box> : nextTask || effectiveTip || budgetText ?
|
</Box>
|
||||||
|
) : nextTask || effectiveTip || budgetText ? (
|
||||||
// IMPORTANT: we need this width="100%" to avoid an Ink bug where the
|
// IMPORTANT: we need this width="100%" to avoid an Ink bug where the
|
||||||
// tip gets duplicated over and over while the spinner is running if
|
// tip gets duplicated over and over while the spinner is running if
|
||||||
// the terminal is very small. TODO: fix this in Ink.
|
// the terminal is very small. TODO: fix this in Ink.
|
||||||
<Box width="100%" flexDirection="column">
|
<Box width="100%" flexDirection="column">
|
||||||
{budgetText && <MessageResponse>
|
{budgetText && (
|
||||||
|
<MessageResponse>
|
||||||
<Text dimColor>{budgetText}</Text>
|
<Text dimColor>{budgetText}</Text>
|
||||||
</MessageResponse>}
|
</MessageResponse>
|
||||||
{(nextTask || effectiveTip) && <MessageResponse>
|
)}
|
||||||
<Text dimColor>
|
{(nextTask || effectiveTip) && (
|
||||||
{nextTask ? `Next: ${nextTask.subject}` : `Tip: ${effectiveTip}`}
|
<MessageResponse>
|
||||||
</Text>
|
<Text dimColor>{nextTask ? `Next: ${nextTask.subject}` : `Tip: ${effectiveTip}`}</Text>
|
||||||
</MessageResponse>}
|
</MessageResponse>
|
||||||
</Box> : null}
|
)}
|
||||||
</Box>;
|
</Box>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Brief/assistant mode spinner: single status line. PromptInput drops its
|
// Brief/assistant mode spinner: single status line. PromptInput drops its
|
||||||
@@ -316,10 +411,7 @@ type BriefSpinnerProps = {
|
|||||||
};
|
};
|
||||||
function BriefSpinner(t0) {
|
function BriefSpinner(t0) {
|
||||||
const $ = _c(31);
|
const $ = _c(31);
|
||||||
const {
|
const { mode, overrideMessage } = t0;
|
||||||
mode,
|
|
||||||
overrideMessage
|
|
||||||
} = t0;
|
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const reducedMotion = settings.prefersReducedMotion ?? false;
|
const reducedMotion = settings.prefersReducedMotion ?? false;
|
||||||
const [randomVerb] = useState(_temp4);
|
const [randomVerb] = useState(_temp4);
|
||||||
@@ -329,7 +421,7 @@ function BriefSpinner(t0) {
|
|||||||
let t2;
|
let t2;
|
||||||
if ($[0] !== mode) {
|
if ($[0] !== mode) {
|
||||||
t1 = () => {
|
t1 = () => {
|
||||||
const operationId = "spinner-" + mode;
|
const operationId = 'spinner-' + mode;
|
||||||
activityManager.startCLIActivity(operationId);
|
activityManager.startCLIActivity(operationId);
|
||||||
return () => {
|
return () => {
|
||||||
activityManager.endCLIActivity(operationId);
|
activityManager.endCLIActivity(operationId);
|
||||||
@@ -346,12 +438,12 @@ function BriefSpinner(t0) {
|
|||||||
useEffect(t1, t2);
|
useEffect(t1, t2);
|
||||||
const [, time] = useAnimationFrame(reducedMotion ? null : 120);
|
const [, time] = useAnimationFrame(reducedMotion ? null : 120);
|
||||||
const runningCount = useAppState(_temp6);
|
const runningCount = useAppState(_temp6);
|
||||||
const showConnWarning = connStatus === "reconnecting" || connStatus === "disconnected";
|
const showConnWarning = connStatus === 'reconnecting' || connStatus === 'disconnected';
|
||||||
const connText = connStatus === "reconnecting" ? "Reconnecting" : "Disconnected";
|
const connText = connStatus === 'reconnecting' ? 'Reconnecting' : 'Disconnected';
|
||||||
const dotFrame = Math.floor(time / 300) % 3;
|
const dotFrame = Math.floor(time / 300) % 3;
|
||||||
let t3;
|
let t3;
|
||||||
if ($[3] !== dotFrame || $[4] !== reducedMotion) {
|
if ($[3] !== dotFrame || $[4] !== reducedMotion) {
|
||||||
t3 = reducedMotion ? "\u2026 " : ".".repeat(dotFrame + 1).padEnd(3);
|
t3 = reducedMotion ? '\u2026 ' : '.'.repeat(dotFrame + 1).padEnd(3);
|
||||||
$[3] = dotFrame;
|
$[3] = dotFrame;
|
||||||
$[4] = reducedMotion;
|
$[4] = reducedMotion;
|
||||||
$[5] = t3;
|
$[5] = t3;
|
||||||
@@ -370,7 +462,8 @@ function BriefSpinner(t0) {
|
|||||||
const verbWidth = t4;
|
const verbWidth = t4;
|
||||||
let t5;
|
let t5;
|
||||||
if ($[8] !== reducedMotion || $[9] !== showConnWarning || $[10] !== time || $[11] !== verb || $[12] !== verbWidth) {
|
if ($[8] !== reducedMotion || $[9] !== showConnWarning || $[10] !== time || $[11] !== verb || $[12] !== verbWidth) {
|
||||||
const glimmerIndex = reducedMotion || showConnWarning ? -100 : computeGlimmerIndex(Math.floor(time / SHIMMER_INTERVAL_MS), verbWidth);
|
const glimmerIndex =
|
||||||
|
reducedMotion || showConnWarning ? -100 : computeGlimmerIndex(Math.floor(time / SHIMMER_INTERVAL_MS), verbWidth);
|
||||||
t5 = computeShimmerSegments(verb, glimmerIndex);
|
t5 = computeShimmerSegments(verb, glimmerIndex);
|
||||||
$[8] = reducedMotion;
|
$[8] = reducedMotion;
|
||||||
$[9] = showConnWarning;
|
$[9] = showConnWarning;
|
||||||
@@ -381,15 +474,9 @@ function BriefSpinner(t0) {
|
|||||||
} else {
|
} else {
|
||||||
t5 = $[13];
|
t5 = $[13];
|
||||||
}
|
}
|
||||||
const {
|
const { before, shimmer, after } = t5;
|
||||||
before,
|
const { columns } = useTerminalSize();
|
||||||
shimmer,
|
const rightText = runningCount > 0 ? `${runningCount} in background` : '';
|
||||||
after
|
|
||||||
} = t5;
|
|
||||||
const {
|
|
||||||
columns
|
|
||||||
} = useTerminalSize();
|
|
||||||
const rightText = runningCount > 0 ? `${runningCount} in background` : "";
|
|
||||||
let t6;
|
let t6;
|
||||||
if ($[14] !== connText || $[15] !== showConnWarning || $[16] !== verbWidth) {
|
if ($[14] !== connText || $[15] !== showConnWarning || $[16] !== verbWidth) {
|
||||||
t6 = showConnWarning ? stringWidth(connText) : verbWidth;
|
t6 = showConnWarning ? stringWidth(connText) : verbWidth;
|
||||||
@@ -403,8 +490,24 @@ function BriefSpinner(t0) {
|
|||||||
const leftWidth = t6 + 3;
|
const leftWidth = t6 + 3;
|
||||||
const pad = Math.max(1, columns - 2 - leftWidth - stringWidth(rightText));
|
const pad = Math.max(1, columns - 2 - leftWidth - stringWidth(rightText));
|
||||||
let t7;
|
let t7;
|
||||||
if ($[18] !== after || $[19] !== before || $[20] !== connText || $[21] !== dots || $[22] !== shimmer || $[23] !== showConnWarning) {
|
if (
|
||||||
t7 = showConnWarning ? <Text color="error">{connText + dots}</Text> : <>{before ? <Text dimColor={true}>{before}</Text> : null}{shimmer ? <Text>{shimmer}</Text> : null}{after ? <Text dimColor={true}>{after}</Text> : null}<Text dimColor={true}>{dots}</Text></>;
|
$[18] !== after ||
|
||||||
|
$[19] !== before ||
|
||||||
|
$[20] !== connText ||
|
||||||
|
$[21] !== dots ||
|
||||||
|
$[22] !== shimmer ||
|
||||||
|
$[23] !== showConnWarning
|
||||||
|
) {
|
||||||
|
t7 = showConnWarning ? (
|
||||||
|
<Text color="error">{connText + dots}</Text>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{before ? <Text dimColor={true}>{before}</Text> : null}
|
||||||
|
{shimmer ? <Text>{shimmer}</Text> : null}
|
||||||
|
{after ? <Text dimColor={true}>{after}</Text> : null}
|
||||||
|
<Text dimColor={true}>{dots}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
$[18] = after;
|
$[18] = after;
|
||||||
$[19] = before;
|
$[19] = before;
|
||||||
$[20] = connText;
|
$[20] = connText;
|
||||||
@@ -417,7 +520,12 @@ function BriefSpinner(t0) {
|
|||||||
}
|
}
|
||||||
let t8;
|
let t8;
|
||||||
if ($[25] !== pad || $[26] !== rightText) {
|
if ($[25] !== pad || $[26] !== rightText) {
|
||||||
t8 = rightText ? <><Text>{" ".repeat(pad)}</Text><Text color="subtle">{rightText}</Text></> : null;
|
t8 = rightText ? (
|
||||||
|
<>
|
||||||
|
<Text>{' '.repeat(pad)}</Text>
|
||||||
|
<Text color="subtle">{rightText}</Text>
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
$[25] = pad;
|
$[25] = pad;
|
||||||
$[26] = rightText;
|
$[26] = rightText;
|
||||||
$[27] = t8;
|
$[27] = t8;
|
||||||
@@ -426,7 +534,12 @@ function BriefSpinner(t0) {
|
|||||||
}
|
}
|
||||||
let t9;
|
let t9;
|
||||||
if ($[28] !== t7 || $[29] !== t8) {
|
if ($[28] !== t7 || $[29] !== t8) {
|
||||||
t9 = <Box flexDirection="row" width="100%" marginTop={1} paddingLeft={2}>{t7}{t8}</Box>;
|
t9 = (
|
||||||
|
<Box flexDirection="row" width="100%" marginTop={1} paddingLeft={2}>
|
||||||
|
{t7}
|
||||||
|
{t8}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
$[28] = t7;
|
$[28] = t7;
|
||||||
$[29] = t8;
|
$[29] = t8;
|
||||||
$[30] = t9;
|
$[30] = t9;
|
||||||
@@ -447,22 +560,20 @@ function _temp5(s) {
|
|||||||
return s.remoteConnectionStatus;
|
return s.remoteConnectionStatus;
|
||||||
}
|
}
|
||||||
function _temp4() {
|
function _temp4() {
|
||||||
return sample(getSpinnerVerbs()) ?? "Working";
|
return sample(getSpinnerVerbs()) ?? 'Working';
|
||||||
}
|
}
|
||||||
export function BriefIdleStatus() {
|
export function BriefIdleStatus() {
|
||||||
const $ = _c(9);
|
const $ = _c(9);
|
||||||
const connStatus = useAppState(_temp7);
|
const connStatus = useAppState(_temp7);
|
||||||
const runningCount = useAppState(_temp8);
|
const runningCount = useAppState(_temp8);
|
||||||
const {
|
const { columns } = useTerminalSize();
|
||||||
columns
|
const showConnWarning = connStatus === 'reconnecting' || connStatus === 'disconnected';
|
||||||
} = useTerminalSize();
|
const connText = connStatus === 'reconnecting' ? 'Reconnecting\u2026' : 'Disconnected';
|
||||||
const showConnWarning = connStatus === "reconnecting" || connStatus === "disconnected";
|
const leftText = showConnWarning ? connText : '';
|
||||||
const connText = connStatus === "reconnecting" ? "Reconnecting\u2026" : "Disconnected";
|
const rightText = runningCount > 0 ? `${runningCount} in background` : '';
|
||||||
const leftText = showConnWarning ? connText : "";
|
|
||||||
const rightText = runningCount > 0 ? `${runningCount} in background` : "";
|
|
||||||
if (!leftText && !rightText) {
|
if (!leftText && !rightText) {
|
||||||
let t0;
|
let t0;
|
||||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
if ($[0] === Symbol.for('react.memo_cache_sentinel')) {
|
||||||
t0 = <Box height={2} />;
|
t0 = <Box height={2} />;
|
||||||
$[0] = t0;
|
$[0] = t0;
|
||||||
} else {
|
} else {
|
||||||
@@ -481,7 +592,12 @@ export function BriefIdleStatus() {
|
|||||||
}
|
}
|
||||||
let t1;
|
let t1;
|
||||||
if ($[3] !== pad || $[4] !== rightText) {
|
if ($[3] !== pad || $[4] !== rightText) {
|
||||||
t1 = rightText ? <><Text>{" ".repeat(pad)}</Text><Text color="subtle">{rightText}</Text></> : null;
|
t1 = rightText ? (
|
||||||
|
<>
|
||||||
|
<Text>{' '.repeat(pad)}</Text>
|
||||||
|
<Text color="subtle">{rightText}</Text>
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
$[3] = pad;
|
$[3] = pad;
|
||||||
$[4] = rightText;
|
$[4] = rightText;
|
||||||
$[5] = t1;
|
$[5] = t1;
|
||||||
@@ -490,7 +606,14 @@ export function BriefIdleStatus() {
|
|||||||
}
|
}
|
||||||
let t2;
|
let t2;
|
||||||
if ($[6] !== t0 || $[7] !== t1) {
|
if ($[6] !== t0 || $[7] !== t1) {
|
||||||
t2 = <Box marginTop={1} paddingLeft={2}><Text>{t0}{t1}</Text></Box>;
|
t2 = (
|
||||||
|
<Box marginTop={1} paddingLeft={2}>
|
||||||
|
<Text>
|
||||||
|
{t0}
|
||||||
|
{t1}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
$[6] = t0;
|
$[6] = t0;
|
||||||
$[7] = t1;
|
$[7] = t1;
|
||||||
$[8] = t2;
|
$[8] = t2;
|
||||||
@@ -512,7 +635,7 @@ export function Spinner() {
|
|||||||
const [ref, time] = useAnimationFrame(reducedMotion ? null : 120);
|
const [ref, time] = useAnimationFrame(reducedMotion ? null : 120);
|
||||||
if (reducedMotion) {
|
if (reducedMotion) {
|
||||||
let t0;
|
let t0;
|
||||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
if ($[0] === Symbol.for('react.memo_cache_sentinel')) {
|
||||||
t0 = <Text color="text">●</Text>;
|
t0 = <Text color="text">●</Text>;
|
||||||
$[0] = t0;
|
$[0] = t0;
|
||||||
} else {
|
} else {
|
||||||
@@ -520,7 +643,11 @@ export function Spinner() {
|
|||||||
}
|
}
|
||||||
let t1;
|
let t1;
|
||||||
if ($[1] !== ref) {
|
if ($[1] !== ref) {
|
||||||
t1 = <Box ref={ref} flexWrap="wrap" height={1} width={2}>{t0}</Box>;
|
t1 = (
|
||||||
|
<Box ref={ref} flexWrap="wrap" height={1} width={2}>
|
||||||
|
{t0}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
$[1] = ref;
|
$[1] = ref;
|
||||||
$[2] = t1;
|
$[2] = t1;
|
||||||
} else {
|
} else {
|
||||||
@@ -540,7 +667,11 @@ export function Spinner() {
|
|||||||
}
|
}
|
||||||
let t2;
|
let t2;
|
||||||
if ($[5] !== ref || $[6] !== t1) {
|
if ($[5] !== ref || $[6] !== t1) {
|
||||||
t2 = <Box ref={ref} flexWrap="wrap" height={1} width={2}>{t1}</Box>;
|
t2 = (
|
||||||
|
<Box ref={ref} flexWrap="wrap" height={1} width={2}>
|
||||||
|
{t1}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
$[5] = ref;
|
$[5] = ref;
|
||||||
$[6] = t1;
|
$[6] = t1;
|
||||||
$[7] = t2;
|
$[7] = t2;
|
||||||
|
|||||||
9
src/components/UndercoverAutoCallout.tsx
Normal file
9
src/components/UndercoverAutoCallout.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Stub — ant-only component, not available in decompiled build
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
export function UndercoverAutoCallout({ onDone }: { onDone: () => void }): React.ReactElement | null {
|
||||||
|
useEffect(() => {
|
||||||
|
onDone();
|
||||||
|
}, [onDone]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import { feature } from 'bun:bundle'
|
import { feature } from 'bun:bundle';
|
||||||
|
|
||||||
// Bugfix for corepack auto-pinning, which adds yarnpkg to peoples' package.jsons
|
// Bugfix for corepack auto-pinning, which adds yarnpkg to peoples' package.jsons
|
||||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
||||||
process.env.COREPACK_ENABLE_AUTO_PIN = "0";
|
process.env.COREPACK_ENABLE_AUTO_PIN = '0';
|
||||||
|
|
||||||
// Set max heap size for child processes in CCR environments (containers have 16GB)
|
// Set max heap size for child processes in CCR environments (containers have 16GB)
|
||||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level, custom-rules/safe-env-boolean-check
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level, custom-rules/safe-env-boolean-check
|
||||||
if (process.env.CLAUDE_CODE_REMOTE === "true") {
|
if (process.env.CLAUDE_CODE_REMOTE === 'true') {
|
||||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
||||||
const existing = process.env.NODE_OPTIONS || "";
|
const existing = process.env.NODE_OPTIONS || '';
|
||||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
||||||
process.env.NODE_OPTIONS = existing
|
process.env.NODE_OPTIONS = existing ? `${existing} --max-old-space-size=8192` : '--max-old-space-size=8192';
|
||||||
? `${existing} --max-old-space-size=8192`
|
|
||||||
: "--max-old-space-size=8192";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Harness-science L0 ablation baseline. Inlined here (not init.ts) because
|
// Harness-science L0 ablation baseline. Inlined here (not init.ts) because
|
||||||
@@ -21,18 +19,18 @@ if (process.env.CLAUDE_CODE_REMOTE === "true") {
|
|||||||
// module-level consts at import time — init() runs too late. feature() gate
|
// module-level consts at import time — init() runs too late. feature() gate
|
||||||
// DCEs this entire block from external builds.
|
// DCEs this entire block from external builds.
|
||||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
||||||
if (feature("ABLATION_BASELINE") && process.env.CLAUDE_CODE_ABLATION_BASELINE) {
|
if (feature('ABLATION_BASELINE') && process.env.CLAUDE_CODE_ABLATION_BASELINE) {
|
||||||
for (const k of [
|
for (const k of [
|
||||||
"CLAUDE_CODE_SIMPLE",
|
'CLAUDE_CODE_SIMPLE',
|
||||||
"CLAUDE_CODE_DISABLE_THINKING",
|
'CLAUDE_CODE_DISABLE_THINKING',
|
||||||
"DISABLE_INTERLEAVED_THINKING",
|
'DISABLE_INTERLEAVED_THINKING',
|
||||||
"DISABLE_COMPACT",
|
'DISABLE_COMPACT',
|
||||||
"DISABLE_AUTO_COMPACT",
|
'DISABLE_AUTO_COMPACT',
|
||||||
"CLAUDE_CODE_DISABLE_AUTO_MEMORY",
|
'CLAUDE_CODE_DISABLE_AUTO_MEMORY',
|
||||||
"CLAUDE_CODE_DISABLE_BACKGROUND_TASKS",
|
'CLAUDE_CODE_DISABLE_BACKGROUND_TASKS',
|
||||||
]) {
|
]) {
|
||||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
|
||||||
process.env[k] ??= "1";
|
process.env[k] ??= '1';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,10 +43,7 @@ async function main(): Promise<void> {
|
|||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
// Fast-path for --version/-v: zero module loading needed
|
// Fast-path for --version/-v: zero module loading needed
|
||||||
if (
|
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
|
||||||
args.length === 1 &&
|
|
||||||
(args[0] === "--version" || args[0] === "-v" || args[0] === "-V")
|
|
||||||
) {
|
|
||||||
// MACRO.VERSION is inlined at build time
|
// MACRO.VERSION is inlined at build time
|
||||||
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
||||||
console.log(`${MACRO.VERSION} (Claude Code)`);
|
console.log(`${MACRO.VERSION} (Claude Code)`);
|
||||||
@@ -56,45 +51,38 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For all other paths, load the startup profiler
|
// For all other paths, load the startup profiler
|
||||||
const { profileCheckpoint } = await import("../utils/startupProfiler.js");
|
const { profileCheckpoint } = await import('../utils/startupProfiler.js');
|
||||||
profileCheckpoint("cli_entry");
|
profileCheckpoint('cli_entry');
|
||||||
|
|
||||||
// Fast-path for --dump-system-prompt: output the rendered system prompt and exit.
|
// Fast-path for --dump-system-prompt: output the rendered system prompt and exit.
|
||||||
// Used by prompt sensitivity evals to extract the system prompt at a specific commit.
|
// Used by prompt sensitivity evals to extract the system prompt at a specific commit.
|
||||||
// Ant-only: eliminated from external builds via feature flag.
|
// Ant-only: eliminated from external builds via feature flag.
|
||||||
if (feature("DUMP_SYSTEM_PROMPT") && args[0] === "--dump-system-prompt") {
|
if (feature('DUMP_SYSTEM_PROMPT') && args[0] === '--dump-system-prompt') {
|
||||||
profileCheckpoint("cli_dump_system_prompt_path");
|
profileCheckpoint('cli_dump_system_prompt_path');
|
||||||
const { enableConfigs } = await import("../utils/config.js");
|
const { enableConfigs } = await import('../utils/config.js');
|
||||||
enableConfigs();
|
enableConfigs();
|
||||||
const { getMainLoopModel } = await import("../utils/model/model.js");
|
const { getMainLoopModel } = await import('../utils/model/model.js');
|
||||||
const modelIdx = args.indexOf("--model");
|
const modelIdx = args.indexOf('--model');
|
||||||
const model =
|
const model = (modelIdx !== -1 && args[modelIdx + 1]) || getMainLoopModel();
|
||||||
(modelIdx !== -1 && args[modelIdx + 1]) || getMainLoopModel();
|
const { getSystemPrompt } = await import('../constants/prompts.js');
|
||||||
const { getSystemPrompt } = await import("../constants/prompts.js");
|
|
||||||
const prompt = await getSystemPrompt([], model);
|
const prompt = await getSystemPrompt([], model);
|
||||||
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
||||||
console.log(prompt.join("\n"));
|
console.log(prompt.join('\n'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (process.argv[2] === "--claude-in-chrome-mcp") {
|
if (process.argv[2] === '--claude-in-chrome-mcp') {
|
||||||
profileCheckpoint("cli_claude_in_chrome_mcp_path");
|
profileCheckpoint('cli_claude_in_chrome_mcp_path');
|
||||||
const { runClaudeInChromeMcpServer } =
|
const { runClaudeInChromeMcpServer } = await import('../utils/claudeInChrome/mcpServer.js');
|
||||||
await import("../utils/claudeInChrome/mcpServer.js");
|
|
||||||
await runClaudeInChromeMcpServer();
|
await runClaudeInChromeMcpServer();
|
||||||
return;
|
return;
|
||||||
} else if (process.argv[2] === "--chrome-native-host") {
|
} else if (process.argv[2] === '--chrome-native-host') {
|
||||||
profileCheckpoint("cli_chrome_native_host_path");
|
profileCheckpoint('cli_chrome_native_host_path');
|
||||||
const { runChromeNativeHost } =
|
const { runChromeNativeHost } = await import('../utils/claudeInChrome/chromeNativeHost.js');
|
||||||
await import("../utils/claudeInChrome/chromeNativeHost.js");
|
|
||||||
await runChromeNativeHost();
|
await runChromeNativeHost();
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (feature('CHICAGO_MCP') && process.argv[2] === '--computer-use-mcp') {
|
||||||
feature("CHICAGO_MCP") &&
|
profileCheckpoint('cli_computer_use_mcp_path');
|
||||||
process.argv[2] === "--computer-use-mcp"
|
const { runComputerUseMcpServer } = await import('../utils/computerUse/mcpServer.js');
|
||||||
) {
|
|
||||||
profileCheckpoint("cli_computer_use_mcp_path");
|
|
||||||
const { runComputerUseMcpServer } =
|
|
||||||
await import("../utils/computerUse/mcpServer.js");
|
|
||||||
await runComputerUseMcpServer();
|
await runComputerUseMcpServer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -104,8 +92,8 @@ async function main(): Promise<void> {
|
|||||||
// perf-sensitive. No enableConfigs(), no analytics sinks at this layer —
|
// perf-sensitive. No enableConfigs(), no analytics sinks at this layer —
|
||||||
// workers are lean. If a worker kind needs configs/auth (assistant will),
|
// workers are lean. If a worker kind needs configs/auth (assistant will),
|
||||||
// it calls them inside its run() fn.
|
// it calls them inside its run() fn.
|
||||||
if (feature("DAEMON") && args[0] === "--daemon-worker") {
|
if (feature('DAEMON') && args[0] === '--daemon-worker') {
|
||||||
const { runDaemonWorker } = await import("../daemon/workerRegistry.js");
|
const { runDaemonWorker } = await import('../daemon/workerRegistry.js');
|
||||||
await runDaemonWorker(args[1]);
|
await runDaemonWorker(args[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -115,27 +103,26 @@ async function main(): Promise<void> {
|
|||||||
// feature() must stay inline for build-time dead code elimination;
|
// feature() must stay inline for build-time dead code elimination;
|
||||||
// isBridgeEnabled() checks the runtime GrowthBook gate.
|
// isBridgeEnabled() checks the runtime GrowthBook gate.
|
||||||
if (
|
if (
|
||||||
feature("BRIDGE_MODE") &&
|
feature('BRIDGE_MODE') &&
|
||||||
(args[0] === "remote-control" ||
|
(args[0] === 'remote-control' ||
|
||||||
args[0] === "rc" ||
|
args[0] === 'rc' ||
|
||||||
args[0] === "remote" ||
|
args[0] === 'remote' ||
|
||||||
args[0] === "sync" ||
|
args[0] === 'sync' ||
|
||||||
args[0] === "bridge")
|
args[0] === 'bridge')
|
||||||
) {
|
) {
|
||||||
profileCheckpoint("cli_bridge_path");
|
profileCheckpoint('cli_bridge_path');
|
||||||
const { enableConfigs } = await import("../utils/config.js");
|
const { enableConfigs } = await import('../utils/config.js');
|
||||||
enableConfigs();
|
enableConfigs();
|
||||||
const { getBridgeDisabledReason, checkBridgeMinVersion } =
|
const { getBridgeDisabledReason, checkBridgeMinVersion } = await import('../bridge/bridgeEnabled.js');
|
||||||
await import("../bridge/bridgeEnabled.js");
|
const { BRIDGE_LOGIN_ERROR } = await import('../bridge/types.js');
|
||||||
const { BRIDGE_LOGIN_ERROR } = await import("../bridge/types.js");
|
const { bridgeMain } = await import('../bridge/bridgeMain.js');
|
||||||
const { bridgeMain } = await import("../bridge/bridgeMain.js");
|
const { exitWithError } = await import('../utils/process.js');
|
||||||
const { exitWithError } = await import("../utils/process.js");
|
|
||||||
|
|
||||||
// Auth check must come before the GrowthBook gate check — without auth,
|
// Auth check must come before the GrowthBook gate check — without auth,
|
||||||
// GrowthBook has no user context and would return a stale/default false.
|
// GrowthBook has no user context and would return a stale/default false.
|
||||||
// getBridgeDisabledReason awaits GB init, so the returned value is fresh
|
// getBridgeDisabledReason awaits GB init, so the returned value is fresh
|
||||||
// (not the stale disk cache), but init still needs auth headers to work.
|
// (not the stale disk cache), but init still needs auth headers to work.
|
||||||
const { getClaudeAIOAuthTokens } = await import("../utils/auth.js");
|
const { getClaudeAIOAuthTokens } = await import('../utils/auth.js');
|
||||||
if (!getClaudeAIOAuthTokens()?.accessToken) {
|
if (!getClaudeAIOAuthTokens()?.accessToken) {
|
||||||
exitWithError(BRIDGE_LOGIN_ERROR);
|
exitWithError(BRIDGE_LOGIN_ERROR);
|
||||||
}
|
}
|
||||||
@@ -149,26 +136,23 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bridge is a remote control feature - check policy limits
|
// Bridge is a remote control feature - check policy limits
|
||||||
const { waitForPolicyLimitsToLoad, isPolicyAllowed } =
|
const { waitForPolicyLimitsToLoad, isPolicyAllowed } = await import('../services/policyLimits/index.js');
|
||||||
await import("../services/policyLimits/index.js");
|
|
||||||
await waitForPolicyLimitsToLoad();
|
await waitForPolicyLimitsToLoad();
|
||||||
if (!isPolicyAllowed("allow_remote_control")) {
|
if (!isPolicyAllowed('allow_remote_control')) {
|
||||||
exitWithError(
|
exitWithError("Error: Remote Control is disabled by your organization's policy.");
|
||||||
"Error: Remote Control is disabled by your organization's policy.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
await bridgeMain(args.slice(1));
|
await bridgeMain(args.slice(1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast-path for `claude daemon [subcommand]`: long-running supervisor.
|
// Fast-path for `claude daemon [subcommand]`: long-running supervisor.
|
||||||
if (feature("DAEMON") && args[0] === "daemon") {
|
if (feature('DAEMON') && args[0] === 'daemon') {
|
||||||
profileCheckpoint("cli_daemon_path");
|
profileCheckpoint('cli_daemon_path');
|
||||||
const { enableConfigs } = await import("../utils/config.js");
|
const { enableConfigs } = await import('../utils/config.js');
|
||||||
enableConfigs();
|
enableConfigs();
|
||||||
const { initSinks } = await import("../utils/sinks.js");
|
const { initSinks } = await import('../utils/sinks.js');
|
||||||
initSinks();
|
initSinks();
|
||||||
const { daemonMain } = await import("../daemon/main.js");
|
const { daemonMain } = await import('../daemon/main.js');
|
||||||
await daemonMain(args.slice(1));
|
await daemonMain(args.slice(1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -177,29 +161,29 @@ async function main(): Promise<void> {
|
|||||||
// Session management against the ~/.claude/sessions/ registry. Flag
|
// Session management against the ~/.claude/sessions/ registry. Flag
|
||||||
// literals are inlined so bg.js only loads when actually dispatching.
|
// literals are inlined so bg.js only loads when actually dispatching.
|
||||||
if (
|
if (
|
||||||
feature("BG_SESSIONS") &&
|
feature('BG_SESSIONS') &&
|
||||||
(args[0] === "ps" ||
|
(args[0] === 'ps' ||
|
||||||
args[0] === "logs" ||
|
args[0] === 'logs' ||
|
||||||
args[0] === "attach" ||
|
args[0] === 'attach' ||
|
||||||
args[0] === "kill" ||
|
args[0] === 'kill' ||
|
||||||
args.includes("--bg") ||
|
args.includes('--bg') ||
|
||||||
args.includes("--background"))
|
args.includes('--background'))
|
||||||
) {
|
) {
|
||||||
profileCheckpoint("cli_bg_path");
|
profileCheckpoint('cli_bg_path');
|
||||||
const { enableConfigs } = await import("../utils/config.js");
|
const { enableConfigs } = await import('../utils/config.js');
|
||||||
enableConfigs();
|
enableConfigs();
|
||||||
const bg = await import("../cli/bg.js");
|
const bg = await import('../cli/bg.js');
|
||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case "ps":
|
case 'ps':
|
||||||
await bg.psHandler(args.slice(1));
|
await bg.psHandler(args.slice(1));
|
||||||
break;
|
break;
|
||||||
case "logs":
|
case 'logs':
|
||||||
await bg.logsHandler(args[1]);
|
await bg.logsHandler(args[1]);
|
||||||
break;
|
break;
|
||||||
case "attach":
|
case 'attach':
|
||||||
await bg.attachHandler(args[1]);
|
await bg.attachHandler(args[1]);
|
||||||
break;
|
break;
|
||||||
case "kill":
|
case 'kill':
|
||||||
await bg.killHandler(args[1]);
|
await bg.killHandler(args[1]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -209,13 +193,9 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fast-path for template job commands.
|
// Fast-path for template job commands.
|
||||||
if (
|
if (feature('TEMPLATES') && (args[0] === 'new' || args[0] === 'list' || args[0] === 'reply')) {
|
||||||
feature("TEMPLATES") &&
|
profileCheckpoint('cli_templates_path');
|
||||||
(args[0] === "new" || args[0] === "list" || args[0] === "reply")
|
const { templatesMain } = await import('../cli/handlers/templateJobs.js');
|
||||||
) {
|
|
||||||
profileCheckpoint("cli_templates_path");
|
|
||||||
const { templatesMain } =
|
|
||||||
await import("../cli/handlers/templateJobs.js");
|
|
||||||
await templatesMain(args);
|
await templatesMain(args);
|
||||||
// process.exit (not return) — mountFleetView's Ink TUI can leave event
|
// process.exit (not return) — mountFleetView's Ink TUI can leave event
|
||||||
// loop handles that prevent natural exit.
|
// loop handles that prevent natural exit.
|
||||||
@@ -225,13 +205,9 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
// Fast-path for `claude environment-runner`: headless BYOC runner.
|
// Fast-path for `claude environment-runner`: headless BYOC runner.
|
||||||
// feature() must stay inline for build-time dead code elimination.
|
// feature() must stay inline for build-time dead code elimination.
|
||||||
if (
|
if (feature('BYOC_ENVIRONMENT_RUNNER') && args[0] === 'environment-runner') {
|
||||||
feature("BYOC_ENVIRONMENT_RUNNER") &&
|
profileCheckpoint('cli_environment_runner_path');
|
||||||
args[0] === "environment-runner"
|
const { environmentRunnerMain } = await import('../environment-runner/main.js');
|
||||||
) {
|
|
||||||
profileCheckpoint("cli_environment_runner_path");
|
|
||||||
const { environmentRunnerMain } =
|
|
||||||
await import("../environment-runner/main.js");
|
|
||||||
await environmentRunnerMain(args.slice(1));
|
await environmentRunnerMain(args.slice(1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -239,65 +215,56 @@ async function main(): Promise<void> {
|
|||||||
// Fast-path for `claude self-hosted-runner`: headless self-hosted-runner
|
// Fast-path for `claude self-hosted-runner`: headless self-hosted-runner
|
||||||
// targeting the SelfHostedRunnerWorkerService API (register + poll; poll IS
|
// targeting the SelfHostedRunnerWorkerService API (register + poll; poll IS
|
||||||
// heartbeat). feature() must stay inline for build-time dead code elimination.
|
// heartbeat). feature() must stay inline for build-time dead code elimination.
|
||||||
if (feature("SELF_HOSTED_RUNNER") && args[0] === "self-hosted-runner") {
|
if (feature('SELF_HOSTED_RUNNER') && args[0] === 'self-hosted-runner') {
|
||||||
profileCheckpoint("cli_self_hosted_runner_path");
|
profileCheckpoint('cli_self_hosted_runner_path');
|
||||||
const { selfHostedRunnerMain } =
|
const { selfHostedRunnerMain } = await import('../self-hosted-runner/main.js');
|
||||||
await import("../self-hosted-runner/main.js");
|
|
||||||
await selfHostedRunnerMain(args.slice(1));
|
await selfHostedRunnerMain(args.slice(1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast-path for --worktree --tmux: exec into tmux before loading full CLI
|
// Fast-path for --worktree --tmux: exec into tmux before loading full CLI
|
||||||
const hasTmuxFlag =
|
const hasTmuxFlag = args.includes('--tmux') || args.includes('--tmux=classic');
|
||||||
args.includes("--tmux") || args.includes("--tmux=classic");
|
|
||||||
if (
|
if (
|
||||||
hasTmuxFlag &&
|
hasTmuxFlag &&
|
||||||
(args.includes("-w") ||
|
(args.includes('-w') || args.includes('--worktree') || args.some(a => a.startsWith('--worktree=')))
|
||||||
args.includes("--worktree") ||
|
|
||||||
args.some((a) => a.startsWith("--worktree=")))
|
|
||||||
) {
|
) {
|
||||||
profileCheckpoint("cli_tmux_worktree_fast_path");
|
profileCheckpoint('cli_tmux_worktree_fast_path');
|
||||||
const { enableConfigs } = await import("../utils/config.js");
|
const { enableConfigs } = await import('../utils/config.js');
|
||||||
enableConfigs();
|
enableConfigs();
|
||||||
const { isWorktreeModeEnabled } =
|
const { isWorktreeModeEnabled } = await import('../utils/worktreeModeEnabled.js');
|
||||||
await import("../utils/worktreeModeEnabled.js");
|
|
||||||
if (isWorktreeModeEnabled()) {
|
if (isWorktreeModeEnabled()) {
|
||||||
const { execIntoTmuxWorktree } =
|
const { execIntoTmuxWorktree } = await import('../utils/worktree.js');
|
||||||
await import("../utils/worktree.js");
|
|
||||||
const result = await execIntoTmuxWorktree(args);
|
const result = await execIntoTmuxWorktree(args);
|
||||||
if (result.handled) {
|
if (result.handled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If not handled (e.g., error), fall through to normal CLI
|
// If not handled (e.g., error), fall through to normal CLI
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
const { exitWithError } = await import("../utils/process.js");
|
const { exitWithError } = await import('../utils/process.js');
|
||||||
exitWithError(result.error);
|
exitWithError(result.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect common update flag mistakes to the update subcommand
|
// Redirect common update flag mistakes to the update subcommand
|
||||||
if (
|
if (args.length === 1 && (args[0] === '--update' || args[0] === '--upgrade')) {
|
||||||
args.length === 1 &&
|
process.argv = [process.argv[0]!, process.argv[1]!, 'update'];
|
||||||
(args[0] === "--update" || args[0] === "--upgrade")
|
|
||||||
) {
|
|
||||||
process.argv = [process.argv[0]!, process.argv[1]!, "update"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --bare: set SIMPLE early so gates fire during module eval / commander
|
// --bare: set SIMPLE early so gates fire during module eval / commander
|
||||||
// option building (not just inside the action handler).
|
// option building (not just inside the action handler).
|
||||||
if (args.includes("--bare")) {
|
if (args.includes('--bare')) {
|
||||||
process.env.CLAUDE_CODE_SIMPLE = "1";
|
process.env.CLAUDE_CODE_SIMPLE = '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
// No special flags detected, load and run the full CLI
|
// No special flags detected, load and run the full CLI
|
||||||
const { startCapturingEarlyInput } = await import("../utils/earlyInput.js");
|
const { startCapturingEarlyInput } = await import('../utils/earlyInput.js');
|
||||||
startCapturingEarlyInput();
|
startCapturingEarlyInput();
|
||||||
profileCheckpoint("cli_before_main_import");
|
profileCheckpoint('cli_before_main_import');
|
||||||
const { main: cliMain } = await import("../main.jsx");
|
const { main: cliMain } = await import('../main.jsx');
|
||||||
profileCheckpoint("cli_after_main_import");
|
profileCheckpoint('cli_after_main_import');
|
||||||
await cliMain();
|
await cliMain();
|
||||||
profileCheckpoint("cli_after_main_complete");
|
profileCheckpoint('cli_after_main_complete');
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { CONTEXT_1M_BETA_HEADER } from '../constants/betas.js'
|
|||||||
import { getGlobalConfig } from './config.js'
|
import { getGlobalConfig } from './config.js'
|
||||||
import { isEnvTruthy } from './envUtils.js'
|
import { isEnvTruthy } from './envUtils.js'
|
||||||
import { getCanonicalName } from './model/model.js'
|
import { getCanonicalName } from './model/model.js'
|
||||||
|
import { resolveAntModel } from './model/antModels.js'
|
||||||
import { getModelCapability } from './model/modelCapabilities.js'
|
import { getModelCapability } from './model/modelCapabilities.js'
|
||||||
|
|
||||||
// Model context window size (200k tokens for all models right now)
|
// Model context window size (200k tokens for all models right now)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { getAPIProvider } from './model/providers.js'
|
|||||||
import { get3PModelCapabilityOverride } from './model/modelSupportOverrides.js'
|
import { get3PModelCapabilityOverride } from './model/modelSupportOverrides.js'
|
||||||
import { isEnvTruthy } from './envUtils.js'
|
import { isEnvTruthy } from './envUtils.js'
|
||||||
import type { EffortLevel } from 'src/entrypoints/sdk/runtimeTypes.js'
|
import type { EffortLevel } from 'src/entrypoints/sdk/runtimeTypes.js'
|
||||||
|
import { resolveAntModel } from './model/antModels.js'
|
||||||
|
import { getAntModelOverrideConfig } from './model/antModels.js'
|
||||||
|
|
||||||
export type { EffortLevel }
|
export type { EffortLevel }
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
* during dead code elimination
|
* during dead code elimination
|
||||||
*/
|
*/
|
||||||
import { getMainLoopModelOverride } from '../../bootstrap/state.js'
|
import { getMainLoopModelOverride } from '../../bootstrap/state.js'
|
||||||
|
import { resolveAntModel, getAntModelOverrideConfig } from './antModels.js'
|
||||||
import {
|
import {
|
||||||
getSubscriptionType,
|
getSubscriptionType,
|
||||||
isClaudeAISubscriber,
|
isClaudeAISubscriber,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { getCanonicalName } from './model/model.js'
|
|||||||
import { get3PModelCapabilityOverride } from './model/modelSupportOverrides.js'
|
import { get3PModelCapabilityOverride } from './model/modelSupportOverrides.js'
|
||||||
import { getAPIProvider } from './model/providers.js'
|
import { getAPIProvider } from './model/providers.js'
|
||||||
import { getSettingsWithErrors } from './settings/settings.js'
|
import { getSettingsWithErrors } from './settings/settings.js'
|
||||||
|
import { resolveAntModel } from './model/antModels.js'
|
||||||
|
|
||||||
export type ThinkingConfig =
|
export type ThinkingConfig =
|
||||||
| { type: 'adaptive' }
|
| { type: 'adaptive' }
|
||||||
|
|||||||
Reference in New Issue
Block a user