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
8 changes: 7 additions & 1 deletion build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ const result = await Bun.build({
outdir,
target: 'bun',
splitting: true,
define: getMacroDefines(),
define: {
...getMacroDefines(),
// React production mode — eliminates _debugStack Error objects
// (6,889 objects × ~1.7KB = 12MB in development builds) and removes
// prop-type / key warnings not useful in a production CLI tool.
'process.env.NODE_ENV': JSON.stringify('production'),
},
features,
})

Expand Down
7 changes: 6 additions & 1 deletion scripts/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ const __dirname = dirname(__filename)
const projectRoot = join(__dirname, '..')
const cliPath = join(projectRoot, 'src/entrypoints/cli.tsx')

const defines = getMacroDefines()
const defines = {
...getMacroDefines(),
// React production mode — prevents 6,889+ _debugStack Error objects
// (12MB) from accumulating during long-running sessions.
'process.env.NODE_ENV': JSON.stringify('production'),
}
Comment on lines +17 to +22
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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how many runtime branches depend on NODE_ENV development.
rg -n --type=ts --type=tsx -C2 "process\.env\.NODE_ENV.*development|NODE_ENV as string.*development"

# Confirm the dev launcher currently forces production.
sed -n '14,24p' scripts/dev.ts

Repository: claude-code-best/claude-code

Length of output: 519


🏁 Script executed:

# Search for NODE_ENV development checks (fixing rg file type filter)
rg -n "NODE_ENV.*development|development.*NODE_ENV" --type ts -A2 -B2

# Check the specific files mentioned in the review
echo "=== desktopDeepLink.ts ===" 
rg -n "NODE_ENV" src/utils/desktopDeepLink.ts -C3

echo "=== doctorDiagnostic.ts ===" 
rg -n "NODE_ENV" src/utils/doctorDiagnostic.ts -C3

Repository: claude-code-best/claude-code

Length of output: 6645


🏁 Script executed:

# Check build/dev script context
echo "=== scripts directory ===" 
ls -la scripts/

echo "=== Check build script NODE_ENV handling ===" 
rg -n "NODE_ENV" scripts/ --type ts

echo "=== Check if there's a separate build launcher ===" 
rg -n "NODE_ENV.*production" scripts/ --type ts -C3

echo "=== Check React-related config for production mode comment context ===" 
rg -n "debugStack|6,889" . --type ts --type tsx

Repository: claude-code-best/claude-code

Length of output: 1791


🏁 Script executed:

# Read dev.ts in full to understand the production mode context
cat -n scripts/dev.ts

echo "=== dev-debug.ts ===" 
cat -n scripts/dev-debug.ts

echo "=== Check package.json for script invocations ===" 
rg -n "dev\.ts|dev-debug" . -t json --type toml -C2

Repository: claude-code-best/claude-code

Length of output: 2872


Hardcoded production NODE_ENV in dev launcher breaks dev-only diagnostics and workflows.

Lines 17–22 force all process.env.NODE_ENV checks to 'production' at compile time. This disables 15+ dev-only branches across the codebase (doctorDiagnostic.ts, desktopDeepLink.ts, DevBar.tsx, warningHandler.ts, AutoUpdater components, and others), breaking developer diagnostic features and install-type detection. While the React memory optimization comment is valid, it should not disable the entire dev experience.

Make the production mode opt-in via environment variable while preserving the React optimization for developers who need it.

🔧 Suggested fix
+const forcedNodeEnv = process.env.CLAUDE_CODE_FORCE_NODE_ENV ?? process.env.NODE_ENV ?? 'development';
 const defines = {
   ...getMacroDefines(),
-  // React production mode — prevents 6,889+ _debugStack Error objects
-  // (12MB) from accumulating during long-running sessions.
-  'process.env.NODE_ENV': JSON.stringify('production'),
+  'process.env.NODE_ENV': JSON.stringify(forcedNodeEnv),
 }

Developers who need the React optimization can opt in: CLAUDE_CODE_FORCE_NODE_ENV=production bun run dev

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const defines = {
...getMacroDefines(),
// React production mode — prevents 6,889+ _debugStack Error objects
// (12MB) from accumulating during long-running sessions.
'process.env.NODE_ENV': JSON.stringify('production'),
}
const forcedNodeEnv = process.env.CLAUDE_CODE_FORCE_NODE_ENV ?? process.env.NODE_ENV ?? 'development';
const defines = {
...getMacroDefines(),
'process.env.NODE_ENV': JSON.stringify(forcedNodeEnv),
}
🤖 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 `@scripts/dev.ts` around lines 17 - 22, The current defines object hardcodes
'process.env.NODE_ENV' to 'production' which disables dev-only branches; change
this so the production value is applied only when a new opt-in env var (e.g.
CLAUDE_CODE_FORCE_NODE_ENV) is set: keep the defines and getMacroDefines() usage
but set 'process.env.NODE_ENV' to
JSON.stringify(process.env.CLAUDE_CODE_FORCE_NODE_ENV || process.env.NODE_ENV ||
'development') (or equivalent) so developers default to dev mode while allowing
opt-in production mode for the React memory optimization when
CLAUDE_CODE_FORCE_NODE_ENV=production is provided; update any related comments
to reflect the opt-in behavior and reference the defines symbol and
getMacroDefines().


const defineArgs = Object.entries(defines).flatMap(([k, v]) => [
'-d',
Expand Down
6 changes: 1 addition & 5 deletions src/QueryEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ import { type Tools, type ToolUseContext, toolMatchesName } from './Tool.js'
import type { AgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'
import { SYNTHETIC_OUTPUT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/SyntheticOutputTool/SyntheticOutputTool.js'
import type { APIError } from '@anthropic-ai/sdk'
import type {
CompactMetadata,
Message,
SystemCompactBoundaryMessage,
} from './types/message.js'
import type { Message, SystemCompactBoundaryMessage } from './types/message.js'
import type { OrphanedPermission } from './types/textInputTypes.js'
import { createAbortController } from './utils/abortController.js'
import type { AttributionState } from './utils/commitAttribution.js'
Expand Down
1 change: 0 additions & 1 deletion src/assistant/gate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { feature } from 'bun:bundle'
import { getKairosActive } from '../bootstrap/state.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'

/**
Expand Down
1 change: 0 additions & 1 deletion src/bridge/bridgeMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
logEvent,
logEventAsync,
} from '../services/analytics/index.js'
import { isInBundledMode } from '../utils/bundledMode.js'
import { getBootstrapArgs, getScriptPath } from '../utils/cliLaunch.js'
import { logForDebugging } from '../utils/debug.js'
import { rcLog } from './rcDebugLog.js'
Expand Down
1 change: 0 additions & 1 deletion src/bridge/remoteBridgeCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ import type {
SDKControlResponse,
} from '../entrypoints/sdk/controlTypes.js'
import type { StdoutMessage } from '../entrypoints/sdk/controlTypes.js'
import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js'
import type { PermissionMode } from '../utils/permissions/PermissionMode.js'
import { setSessionMetadataChangedListener } from '../utils/sessionState.js'

Expand Down
1 change: 0 additions & 1 deletion src/bridge/replBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import type {
SDKControlResponse,
} from '../entrypoints/sdk/controlTypes.js'
import type { StdoutMessage } from '../entrypoints/sdk/controlTypes.js'
import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js'

/**
* StdoutMessage with optional session_id. The transport layer accepts
Expand Down
1 change: 0 additions & 1 deletion src/buddy/CompanionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Companion display card — shown by /buddy (no args).
* Mirrors official vc8 component: bordered box with sprite, stats, last reaction.
*/
import React from 'react';
import { Box, Text } from '@anthropic/ink';
import { useInput } from '@anthropic/ink';
import { renderSprite } from './sprites.js';
Expand Down
1 change: 0 additions & 1 deletion src/cli/handlers/mcp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import { stat } from 'fs/promises';
import pMap from 'p-map';
import { cwd } from 'process';
import React from 'react';
import { MCPServerDesktopImportDialog } from '../../components/MCPServerDesktopImportDialog.js';
import { wrappedRender as render } from '@anthropic/ink';
import { KeybindingSetup } from '../../keybindings/KeybindingProviderSetup.js';
Expand Down
3 changes: 1 addition & 2 deletions src/cli/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ import type {
ModelInfo,
SDKMessage,
SDKUserMessage,
SDKUserMessageReplay,
PermissionResult,
McpServerConfigForProcessTransport,
McpServerStatus,
Expand Down Expand Up @@ -5477,7 +5476,7 @@ function getStructuredIO(
*/
export async function handleOrphanedPermissionResponse({
message,
setAppState,
setAppState: _setAppState,
onEnqueued,
handledToolUseIds,
}: {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/assistant/assistant.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { resolve } from 'path';
import { Box, Text } from '@anthropic/ink';
import { Dialog } from '../../components/design-system/Dialog.js';
Expand Down
1 change: 0 additions & 1 deletion src/commands/config/config.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import { Settings } from '../../components/Settings/Settings.js';
import type { LocalJSXCommandCall } from '../../types/command.js';

Expand Down
1 change: 0 additions & 1 deletion src/commands/diff/diff.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import type { LocalJSXCommandCall } from '../../types/command.js';

export const call: LocalJSXCommandCall = async (onDone, context) => {
Expand Down
1 change: 0 additions & 1 deletion src/commands/doctor/doctor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Doctor } from '../../screens/Doctor.js';
import type { LocalJSXCommandCall } from '../../types/command.js';

Expand Down
1 change: 0 additions & 1 deletion src/commands/help/help.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import { HelpV2 } from '../../components/HelpV2/HelpV2.js';
import type { LocalJSXCommandCall } from '../../types/command.js';

Expand Down
1 change: 0 additions & 1 deletion src/commands/hooks/hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import { HooksConfigMenu } from '../../components/hooks/HooksConfigMenu.js';
import { logEvent } from '../../services/analytics/index.js';
import { getTools } from '../../tools.js';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/install-github-app/ApiKeyStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
import TextInput from '../../components/TextInput.js';
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
import { Box, color, Text, useTheme } from '@anthropic/ink';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
import TextInput from '../../components/TextInput.js';
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
import { Box, color, Text, useTheme } from '@anthropic/ink';
Expand Down
1 change: 0 additions & 1 deletion src/commands/install-github-app/CheckGitHubStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Text } from '@anthropic/ink';

export function CheckGitHubStep() {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/install-github-app/ChooseRepoStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
import TextInput from '../../components/TextInput.js';
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
import { Box, Text } from '@anthropic/ink';
Expand Down
1 change: 0 additions & 1 deletion src/commands/install-github-app/CreatingStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Box, Text } from '@anthropic/ink';
import type { Workflow } from './types.js';

Expand Down
1 change: 0 additions & 1 deletion src/commands/install-github-app/ErrorStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js';
import { Box, Text } from '@anthropic/ink';

Expand Down
1 change: 0 additions & 1 deletion src/commands/install-github-app/ExistingWorkflowStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Select } from 'src/components/CustomSelect/index.js';
import { Box, Text } from '@anthropic/ink';

Expand Down
1 change: 0 additions & 1 deletion src/commands/install-github-app/InstallAppStep.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import figures from 'figures';
import React from 'react';
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js';
import { Box, Text } from '@anthropic/ink';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
Expand Down
1 change: 0 additions & 1 deletion src/commands/install-github-app/WarningsStep.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import figures from 'figures';
import React from 'react';
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js';
import { Box, Text } from '@anthropic/ink';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
Expand Down
1 change: 0 additions & 1 deletion src/commands/login/login.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { feature } from 'bun:bundle';
import * as React from 'react';
import { resetCostState } from '../../bootstrap/state.js';
import { clearTrustedDeviceToken, enrollTrustedDevice } from '../../bridge/trustedDevice.js';
Expand Down
1 change: 0 additions & 1 deletion src/commands/permissions/permissions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import { PermissionRuleList } from '../../components/permissions/rules/PermissionRuleList.js';
import type { LocalJSXCommandCall } from '../../types/command.js';
import { createPermissionRetryMessage } from '../../utils/messages.js';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function getMergedEnv(): Record<string, string> {
return merged
}

const call: LocalCommandCall = async (args, context) => {
const call: LocalCommandCall = async (args, _context) => {
const arg = args.trim().toLowerCase()

// No argument: show current provider
Expand Down
2 changes: 1 addition & 1 deletion src/commands/remoteControlServer/remoteControlServer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type ChildProcess } from 'child_process';
import { resolve } from 'path';
import * as React from 'react';
import { useEffect, useState } from 'react';
import { getBridgeDisabledReason, isBridgeEnabled } from '../../bridge/bridgeEnabled.js';
import { getBridgeDisabledReason } from '../../bridge/bridgeEnabled.js';
import { getBridgeAccessToken } from '../../bridge/bridgeConfig.js';
import { BRIDGE_LOGIN_INSTRUCTION } from '../../bridge/types.js';
import { Dialog } from '../../components/design-system/Dialog.js';
Expand Down
1 change: 0 additions & 1 deletion src/commands/review/ultrareviewCommand.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.js';
import React from 'react';
import type { LocalJSXCommandCall, LocalJSXCommandOnDone } from '../../types/command.js';
import { checkOverageGate, confirmOverage, launchRemoteReview } from './reviewRemote.js';
import { UltrareviewOverageDialog } from './UltrareviewOverageDialog.js';
Expand Down
1 change: 0 additions & 1 deletion src/commands/skill-learning/skill-learning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
exportInstincts,
findPromotionCandidates,
generateSkillCandidates,
importInstincts,
ingestTranscript,
listKnownProjects,
loadInstincts,
Expand Down
1 change: 0 additions & 1 deletion src/commands/stats/stats.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import { Stats } from '../../components/Stats.js';
import type { LocalJSXCommandCall } from '../../types/command.js';

Expand Down
31 changes: 0 additions & 31 deletions src/commands/ultraplan.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { readFileSync } from 'fs';
import { REMOTE_CONTROL_DISCONNECTED_MSG } from '../bridge/types.js';
import type { Command } from '../commands.js';
import { DIAMOND_OPEN } from '../constants/figures.js';
Expand All @@ -21,7 +20,6 @@ import { logForDebugging } from '../utils/debug.js';
import { errorMessage } from '../utils/errors.js';
import { logError } from '../utils/log.js';
import { enqueuePendingNotification } from '../utils/messageQueueManager.js';
import { ALL_MODEL_CONFIGS } from '../utils/model/configs.js';
import { updateTaskState } from '../utils/task/framework.js';
import { archiveRemoteSession, teleportToRemote } from '../utils/teleport.js';
import { pollForApprovedExitPlanMode, UltraplanPollError } from '../utils/ultraplan/ccrSession.js';
Expand All @@ -36,13 +34,6 @@ import { registerCleanup } from '../utils/cleanupRegistry.js';
// TODO(prod-hardening): OAuth token may go stale over the 30min poll;
// consider refresh.

/**
* Multi-agent exploration is slow; 30min timeout.
*
* @deprecated use getUltraplanTimeoutMs()
*/
const ULTRAPLAN_TIMEOUT_MS = 30 * 60 * 1000;

export const CCR_TERMS_URL = 'https://code.claude.com/docs/en/claude-code-on-the-web';

export function getUltraplanTimeoutMs(): number {
Expand All @@ -61,15 +52,6 @@ export function isUltraplanEnabled(): boolean {
);
}

// CCR runs against the first-party API — use the canonical ID, not the
// provider-specific string getModelStrings() would return (which may be a
// Bedrock ARN or Vertex ID on the local CLI). Read at call time, not module
// load: the GrowthBook cache is empty at import and `/config` Gates can flip
// it between invocations.
function getUltraplanModel(): string {
return getFeatureValue_CACHED_MAY_BE_STALE('tengu_ultraplan_model', ALL_MODEL_CONFIGS.opus47.firstParty);
}

// prompt.txt is wrapped in <system-reminder> so the CCR browser hides
// scaffolding (CLI_BLOCK_TAGS dropped by stripSystemNotifications)
// while the model still sees full text.
Expand All @@ -84,19 +66,6 @@ const _rawPrompt = require('../utils/ultraplan/prompt.txt');
/* eslint-enable @typescript-eslint/no-require-imports */
const DEFAULT_INSTRUCTIONS: string = (typeof _rawPrompt === 'string' ? _rawPrompt : _rawPrompt.default).trimEnd();

// Dev-only prompt override resolved eagerly at module load.
// Gated to ant builds (USER_TYPE is a build-time define,
// so the override path is DCE'd from external builds).
// Shell-set env only, so top-level process.env read is fine
// — settings.env never injects this.
// @deprecated use buildUltraplanPrompt()
/* eslint-disable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs -- ant-only dev override; eager top-level read is the point (crash at startup, not silently inside the slash-command try/catch) */
const ULTRAPLAN_INSTRUCTIONS: string =
process.env.USER_TYPE === 'ant' && process.env.ULTRAPLAN_PROMPT_FILE
? readFileSync(process.env.ULTRAPLAN_PROMPT_FILE, 'utf8').trimEnd()
: DEFAULT_INSTRUCTIONS;
/* eslint-enable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs */

/**
* Assemble the initial CCR user message. seedPlan and blurb stay outside the
* system-reminder so the browser renders them; scaffolding is hidden.
Expand Down
1 change: 0 additions & 1 deletion src/commands/usage/usage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import { Settings } from '../../components/Settings/Settings.js';
import type { LocalJSXCommandCall } from '../../types/command.js';

Expand Down
Loading
Loading