// These side-effects must run before all other imports:
// 1. profileCheckpoint marks entry before heavy module evaluation begins
// 2. startMdmRawRead fires MDM subprocesses (plutil/reg query) so they run in
//    parallel with the remaining ~135ms of imports below
// 3. startKeychainPrefetch fires both macOS keychain reads (OAuth + legacy API
//    key) in parallel — isRemoteManagedSettingsEligible() otherwise reads them
//    sequentially via sync spawn inside applySafeConfigEnvironmentVariables()
//    (~65ms on every macOS startup)
import { profileCheckpoint, profileReport } from "./utils/startupProfiler.js";

// eslint-disable-next-line custom-rules/no-top-level-side-effects
profileCheckpoint("main_tsx_entry");

import { startMdmRawRead } from "./utils/settings/mdm/rawRead.js";

// eslint-disable-next-line custom-rules/no-top-level-side-effects
startMdmRawRead();

import {
	ensureKeychainPrefetchCompleted,
	startKeychainPrefetch,
} from "./utils/secureStorage/keychainPrefetch.js";

// eslint-disable-next-line custom-rules/no-top-level-side-effects
startKeychainPrefetch();

import { feature } from "bun:bundle";
import {
  Command as CommanderCommand,
  InvalidArgumentError,
  Option,
} from '@commander-js/extra-typings'
import chalk from 'chalk'
import { readFileSync } from 'fs'
import mapValues from 'lodash-es/mapValues.js'
import pickBy from 'lodash-es/pickBy.js'
import uniqBy from 'lodash-es/uniqBy.js'
import React from 'react'
import { getOauthConfig } from './constants/oauth.js'
import { getRemoteSessionUrl } from './constants/product.js'
import { getSystemContext, getUserContext } from './context.js'
import { init, initializeTelemetryAfterTrust } from './entrypoints/init.js'
import { addToHistory } from './history.js'
import type { Root } from '@anthropic/ink'
import { launchRepl } from './replLauncher.js'
import {
	hasGrowthBookEnvOverride,
	initializeGrowthBook,
	refreshGrowthBookAfterAuthChange,
} from "./services/analytics/growthbook.js";
import { fetchBootstrapData } from "./services/api/bootstrap.js";
import {
	type DownloadResult,
	downloadSessionFiles,
	type FilesApiConfig,
	parseFileSpecs,
} from "./services/api/filesApi.js";
import { prefetchPassesEligibility } from "./services/api/referral.js";
import type {
	McpSdkServerConfig,
	McpServerConfig,
	ScopedMcpServerConfig,
} from "./services/mcp/types.js";
import {
	isPolicyAllowed,
	loadPolicyLimits,
	refreshPolicyLimits,
	waitForPolicyLimitsToLoad,
} from "./services/policyLimits/index.js";
import {
	loadRemoteManagedSettings,
	refreshRemoteManagedSettings,
} from "./services/remoteManagedSettings/index.js";
import type { ToolInputJSONSchema } from "./Tool.js";
import {
	createSyntheticOutputTool,
	isSyntheticOutputToolEnabled,
} from "@claude-code-best/builtin-tools/tools/SyntheticOutputTool/SyntheticOutputTool.js";
import { getTools } from "./tools.js";
import {
	canUserConfigureAdvisor,
	getInitialAdvisorSetting,
	isAdvisorEnabled,
	isValidAdvisorModel,
	modelSupportsAdvisor,
} from "./utils/advisor.js";
import { isAgentSwarmsEnabled } from "./utils/agentSwarmsEnabled.js";
import { count, uniq } from "./utils/array.js";
import { installAsciicastRecorder } from "./utils/asciicast.js";
import {
	getSubscriptionType,
	isClaudeAISubscriber,
	prefetchAwsCredentialsAndBedRockInfoIfSafe,
	prefetchGcpCredentialsIfSafe,
	validateForceLoginOrg,
} from "./utils/auth.js";
import {
	checkHasTrustDialogAccepted,
	getGlobalConfig,
	getRemoteControlAtStartup,
	isAutoUpdaterDisabled,
	saveGlobalConfig,
} from "./utils/config.js";
import { seedEarlyInput, stopCapturingEarlyInput } from "./utils/earlyInput.js";
import { getInitialEffortSetting, parseEffortValue } from "./utils/effort.js";
import {
	getInitialFastModeSetting,
	isFastModeEnabled,
	prefetchFastModeStatus,
	resolveFastModeStatusFromCache,
} from "./utils/fastMode.js";
import { applyConfigEnvironmentVariables } from "./utils/managedEnv.js";
import { createSystemMessage, createUserMessage } from "./utils/messages.js";
import { getPlatform } from "./utils/platform.js";
import { getBaseRenderOptions } from "./utils/renderOptions.js";
import { getSessionIngressAuthToken } from "./utils/sessionIngressAuth.js";
import { settingsChangeDetector } from "./utils/settings/changeDetector.js";
import { skillChangeDetector } from "./utils/skills/skillChangeDetector.js";
import { jsonParse, writeFileSync_DEPRECATED } from "./utils/slowOperations.js";
import { computeInitialTeamContext } from "./utils/swarm/reconnection.js";
import { initializeWarningHandler } from "./utils/warningHandler.js";
import { isWorktreeModeEnabled } from "./utils/worktreeModeEnabled.js";

// Lazy require to avoid circular dependency: teammate.ts -> AppState.tsx -> ... -> main.tsx
/* eslint-disable @typescript-eslint/no-require-imports */
const getTeammateUtils = () =>
	require("./utils/teammate.js") as typeof import("./utils/teammate.js");
const getTeammatePromptAddendum = () =>
	require("./utils/swarm/teammatePromptAddendum.js") as typeof import("./utils/swarm/teammatePromptAddendum.js");
const getTeammateModeSnapshot = () =>
	require("./utils/swarm/backends/teammateModeSnapshot.js") as typeof import("./utils/swarm/backends/teammateModeSnapshot.js");
/* eslint-enable @typescript-eslint/no-require-imports */
// Dead code elimination: conditional import for COORDINATOR_MODE
/* eslint-disable @typescript-eslint/no-require-imports */
const coordinatorModeModule = feature("COORDINATOR_MODE")
	? (require("./coordinator/coordinatorMode.js") as typeof import("./coordinator/coordinatorMode.js"))
	: null;
/* eslint-enable @typescript-eslint/no-require-imports */
// Dead code elimination: conditional import for KAIROS (assistant mode)
/* eslint-disable @typescript-eslint/no-require-imports */
const assistantModule = feature("KAIROS")
	? (require("./assistant/index.js") as typeof import("./assistant/index.js"))
	: null;
const kairosGate = feature("KAIROS")
	? (require("./assistant/gate.js") as typeof import("./assistant/gate.js"))
	: null;

import { relative, resolve } from "path";
import { isAnalyticsDisabled } from "src/services/analytics/config.js";
import { getFeatureValue_CACHED_MAY_BE_STALE } from "src/services/analytics/growthbook.js";
import {
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
  logEvent,
} from 'src/services/analytics/index.js'
import { initializeAnalyticsGates } from 'src/services/analytics/sink.js'
import {
	getOriginalCwd,
	setAdditionalDirectoriesForClaudeMd,
	setIsRemoteMode,
	setMainLoopModelOverride,
	setMainThreadAgentType,
	setTeleportedSessionInfo,
} from "./bootstrap/state.js";
import { filterCommandsForRemoteMode, getCommands } from "./commands.js";
import type { StatsStore } from "./context/stats.js";
import {
  launchAssistantInstallWizard,
  launchAssistantSessionChooser,
  launchInvalidSettingsDialog,
  launchResumeChooser,
  launchSnapshotUpdateDialog,
  launchTeleportRepoMismatchDialog,
  launchTeleportResumeWrapper,
} from './dialogLaunchers.js'
import { SHOW_CURSOR } from '@anthropic/ink'
import {
	exitWithError,
	exitWithMessage,
	getRenderContext,
	renderAndRun,
	showSetupScreens,
} from "./interactiveHelpers.js";
import { initBuiltinPlugins } from "./plugins/bundled/index.js";
/* eslint-enable @typescript-eslint/no-require-imports */
import { checkQuotaStatus } from "./services/claudeAiLimits.js";
import {
	getMcpToolsCommandsAndResources,
	prefetchAllMcpResources,
} from "./services/mcp/client.js";
import {
	VALID_INSTALLABLE_SCOPES,
	VALID_UPDATE_SCOPES,
} from "./services/plugins/pluginCliCommands.js";
import { initBundledSkills } from "./skills/bundled/index.js";
import type { AgentColorName } from "@claude-code-best/builtin-tools/tools/AgentTool/agentColorManager.js";
import {
	getActiveAgentsFromList,
	getAgentDefinitionsWithOverrides,
	isBuiltInAgent,
	isCustomAgent,
	parseAgentsFromJson,
} from "@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js";
import type { LogOption } from "./types/logs.js";
import type { Message as MessageType } from "./types/message.js";
import {
	CLAUDE_IN_CHROME_SKILL_HINT,
	CLAUDE_IN_CHROME_SKILL_HINT_WITH_WEBBROWSER,
} from "./utils/claudeInChrome/prompt.js";
import {
	setupClaudeInChrome,
	shouldAutoEnableClaudeInChrome,
	shouldEnableClaudeInChrome,
} from "./utils/claudeInChrome/setup.js";
import { getContextWindowForModel } from "./utils/context.js";
import { loadConversationForResume } from "./utils/conversationRecovery.js";
import { buildDeepLinkBanner } from "./utils/deepLink/banner.js";
import {
	hasNodeOption,
	isBareMode,
	isEnvTruthy,
	isInProtectedNamespace,
} from "./utils/envUtils.js";
import { refreshExampleCommands } from "./utils/exampleCommands.js";
import type { FpsMetrics } from "./utils/fpsTracker.js";
import { getWorktreePaths } from "./utils/getWorktreePaths.js";
import {
	findGitRoot,
	getBranch,
	getIsGit,
	getWorktreeCount,
} from "./utils/git.js";
import { getGhAuthStatus } from "./utils/github/ghAuthStatus.js";
import { safeParseJSON } from "./utils/json.js";
import { logError } from "./utils/log.js";
import { getModelDeprecationWarning } from "./utils/model/deprecation.js";
import {
	getDefaultMainLoopModel,
	getUserSpecifiedModelSetting,
	normalizeModelStringForAPI,
	parseUserSpecifiedModel,
} from "./utils/model/model.js";
import { ensureModelStringsInitialized } from "./utils/model/modelStrings.js";
import { PERMISSION_MODES } from "./utils/permissions/PermissionMode.js";
import {
	getAutoModeEnabledStateIfCached,
	initializeToolPermissionContext,
	initialPermissionModeFromCLI,
	isDefaultPermissionModeAuto,
	parseToolListFromCLI,
	removeDangerousPermissions,
	stripDangerousPermissionsForAutoMode,
	verifyAutoModeGateAccess,
} from "./utils/permissions/permissionSetup.js";
import { cleanupOrphanedPluginVersionsInBackground } from "./utils/plugins/cacheUtils.js";
import { initializeVersionedPlugins } from "./utils/plugins/installedPluginsManager.js";
import { getManagedPluginNames } from "./utils/plugins/managedPlugins.js";
import { getGlobExclusionsForPluginCache } from "./utils/plugins/orphanedPluginFilter.js";
import { getPluginSeedDirs } from "./utils/plugins/pluginDirectories.js";
import { countFilesRoundedRg } from "./utils/ripgrep.js";
import {
	processSessionStartHooks,
	processSetupHooks,
} from "./utils/sessionStart.js";
import {
	cacheSessionTitle,
	getSessionIdFromLog,
	loadTranscriptFromFile,
	saveAgentSetting,
	saveMode,
	searchSessionsByCustomTitle,
	sessionIdExists,
} from "./utils/sessionStorage.js";
import { ensureMdmSettingsLoaded } from "./utils/settings/mdm/settings.js";
import {
	getInitialSettings,
	getManagedSettingsKeysForLogging,
	getSettingsForSource,
	getSettingsWithErrors,
} from "./utils/settings/settings.js";
import { resetSettingsCache } from "./utils/settings/settingsCache.js";
import type { ValidationError } from "./utils/settings/validation.js";
import {
	DEFAULT_TASKS_MODE_TASK_LIST_ID,
	TASK_STATUSES,
} from "./utils/tasks.js";
import {
	logPluginLoadErrors,
	logPluginsEnabledForSession,
} from "./utils/telemetry/pluginTelemetry.js";
import { logSkillsLoaded } from "./utils/telemetry/skillLoadedEvent.js";
import { generateTempFilePath } from "./utils/tempfile.js";
import { validateUuid } from "./utils/uuid.js";
// Plugin startup checks are now handled non-blockingly in REPL.tsx

import { registerMcpAddCommand } from "src/commands/mcp/addCommand.js";
import { registerMcpXaaIdpCommand } from "src/commands/mcp/xaaIdpCommand.js";
import { logPermissionContextForAnts } from "src/services/internalLogging.js";
import { fetchClaudeAIMcpConfigsIfEligible } from "src/services/mcp/claudeai.js";
import { clearServerCache } from "src/services/mcp/client.js";
import {
	areMcpConfigsAllowedWithEnterpriseMcpConfig,
	dedupClaudeAiMcpServers,
	doesEnterpriseMcpConfigExist,
	filterMcpServersByPolicy,
	getClaudeCodeMcpConfigs,
	getMcpServerSignature,
	parseMcpConfig,
	parseMcpConfigFromFilePath,
} from "src/services/mcp/config.js";
import {
	excludeCommandsByServer,
	excludeResourcesByServer,
} from "src/services/mcp/utils.js";
import { isXaaEnabled } from "src/services/mcp/xaaIdpLogin.js";
import { getRelevantTips } from "src/services/tips/tipRegistry.js";
import { logContextMetrics } from "src/utils/api.js";
import {
	CLAUDE_IN_CHROME_MCP_SERVER_NAME,
	isClaudeInChromeMCPServer,
} from "src/utils/claudeInChrome/common.js";
import { registerCleanup } from "src/utils/cleanupRegistry.js";
import { eagerParseCliFlag } from "src/utils/cliArgs.js";
import { createEmptyAttributionState } from "src/utils/commitAttribution.js";
import {
	countConcurrentSessions,
	registerSession,
	updateSessionName,
} from "src/utils/concurrentSessions.js";
import { getCwd } from "src/utils/cwd.js";
import { logForDebugging, setHasFormattedOutput } from "src/utils/debug.js";
import {
	errorMessage,
	getErrnoCode,
	isENOENT,
	TeleportOperationError,
	toError,
} from "src/utils/errors.js";
import {
	getFsImplementation,
	safeResolvePath,
} from "src/utils/fsOperations.js";
import {
	gracefulShutdown,
	gracefulShutdownSync,
} from "src/utils/gracefulShutdown.js";
import { setAllHookEventsEnabled } from "src/utils/hooks/hookEvents.js";
import { refreshModelCapabilities } from "src/utils/model/modelCapabilities.js";
import { peekForStdinData, writeToStderr } from "src/utils/process.js";
import { setCwd } from "src/utils/Shell.js";
import {
	type ProcessedResume,
	processResumedConversation,
} from "src/utils/sessionRestore.js";
import { parseSettingSourcesFlag } from "src/utils/settings/constants.js";
import { plural } from "src/utils/stringUtils.js";
import {
	type ChannelEntry,
	getInitialMainLoopModel,
	getIsNonInteractiveSession,
	getSdkBetas,
	getSessionId,
	getUserMsgOptIn,
	setAllowedChannels,
	setAllowedSettingSources,
	setChromeFlagOverride,
	setClientType,
	setCwdState,
	setDirectConnectServerUrl,
	setFlagSettingsPath,
	setInitialMainLoopModel,
	setInlinePlugins,
	setIsInteractive,
	setKairosActive,
	setOriginalCwd,
	setQuestionPreviewFormat,
	setSdkBetas,
	setSessionBypassPermissionsMode,
	setSessionPersistenceDisabled,
	setSessionSource,
	setUserMsgOptIn,
	switchSession,
} from "./bootstrap/state.js";

/* eslint-disable @typescript-eslint/no-require-imports */
const autoModeStateModule = feature("TRANSCRIPT_CLASSIFIER")
	? (require("./utils/permissions/autoModeState.js") as typeof import("./utils/permissions/autoModeState.js"))
	: null;

// TeleportRepoMismatchDialog, TeleportResumeWrapper dynamically imported at call sites
import { migrateBypassPermissionsAcceptedToSettings } from "./migrations/migrateBypassPermissionsAcceptedToSettings.js";
import { migrateEnableAllProjectMcpServersToSettings } from "./migrations/migrateEnableAllProjectMcpServersToSettings.js";
import { migrateFennecToOpus } from "./migrations/migrateFennecToOpus.js";
import { migrateLegacyOpusToCurrent } from "./migrations/migrateLegacyOpusToCurrent.js";
import { migrateOpusToOpus1m } from "./migrations/migrateOpusToOpus1m.js";
import { migrateReplBridgeEnabledToRemoteControlAtStartup } from "./migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.js";
import { migrateSonnet1mToSonnet45 } from "./migrations/migrateSonnet1mToSonnet45.js";
import { migrateSonnet45ToSonnet46 } from "./migrations/migrateSonnet45ToSonnet46.js";
import { resetAutoModeOptInForDefaultOffer } from "./migrations/resetAutoModeOptInForDefaultOffer.js";
import { resetProToOpusDefault } from "./migrations/resetProToOpusDefault.js";
import { createRemoteSessionConfig } from "./remote/RemoteSessionManager.js";
/* eslint-enable @typescript-eslint/no-require-imports */
// teleportWithProgress dynamically imported at call site
import {
	createDirectConnectSession,
	DirectConnectError,
} from "./server/createDirectConnectSession.js";
import { initializeLspServerManager } from "./services/lsp/manager.js";
import { shouldEnablePromptSuggestion } from "./services/PromptSuggestion/promptSuggestion.js";
import {
	type AppState,
	getDefaultAppState,
	IDLE_SPECULATION_STATE,
} from "./state/AppStateStore.js";
import { onChangeAppState } from "./state/onChangeAppState.js";
import { createStore } from "./state/store.js";
import { asSessionId } from "./types/ids.js";
import { filterAllowedSdkBetas } from "./utils/betas.js";
import { isInBundledMode, isRunningWithBun } from "./utils/bundledMode.js";
import { logForDiagnosticsNoPII } from "./utils/diagLogs.js";
import {
	filterExistingPaths,
	getKnownPathsForRepo,
} from "./utils/githubRepoPathMapping.js";
import {
	clearPluginCache,
	loadAllPluginsCacheOnly,
} from "./utils/plugins/pluginLoader.js";
import { migrateChangelogFromConfig } from "./utils/releaseNotes.js";
import { SandboxManager } from "./utils/sandbox/sandbox-adapter.js";
import { fetchSession, prepareApiRequest } from "./utils/teleport/api.js";
import {
	checkOutTeleportedSessionBranch,
	processMessagesForTeleportResume,
	teleportToRemoteWithErrorHandling,
	validateGitState,
	validateSessionRepository,
} from "./utils/teleport.js";
import {
  shouldEnableThinkingByDefault,
  type ThinkingConfig,
} from './utils/thinking.js'
import { initUser, resetUserCache } from './utils/user.js'
import {
	getTmuxInstallInstructions,
	isTmuxAvailable,
	parsePRReference,
} from "./utils/worktree.js";

// eslint-disable-next-line custom-rules/no-top-level-side-effects
profileCheckpoint("main_tsx_imports_loaded");

/**
 * Log managed settings keys to Statsig for analytics.
 * This is called after init() completes to ensure settings are loaded
 * and environment variables are applied before model resolution.
 */
function logManagedSettings(): void {
	try {
		const policySettings = getSettingsForSource("policySettings");
		if (policySettings) {
			const allKeys = getManagedSettingsKeysForLogging(policySettings);
			logEvent("tengu_managed_settings_loaded", {
				keyCount: allKeys.length,
				keys: allKeys.join(
					",",
				) as unknown as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			});
		}
	} catch {
		// Silently ignore errors - this is just for analytics
	}
}

// Check if running in debug/inspection mode
function isBeingDebugged() {
	const isBun = isRunningWithBun();

	// Check for inspect flags in process arguments (including all variants)
	const hasInspectArg = process.execArgv.some((arg) => {
		if (isBun) {
			// Note: Bun has an issue with single-file executables where application arguments
			// from process.argv leak into process.execArgv (similar to https://github.com/oven-sh/bun/issues/11673)
			// This breaks use of --debug mode if we omit this branch
			// We're fine to skip that check, because Bun doesn't support Node.js legacy --debug or --debug-brk flags
			return /--inspect(-brk)?/.test(arg);
		} else {
			// In Node.js, check for both --inspect and legacy --debug flags
			return /--inspect(-brk)?|--debug(-brk)?/.test(arg);
		}
	});

	// Check if NODE_OPTIONS contains inspect flags
	const hasInspectEnv =
		process.env.NODE_OPTIONS &&
		/--inspect(-brk)?|--debug(-brk)?/.test(process.env.NODE_OPTIONS);

	// Check if inspector is available and active (indicates debugging)
	try {
		// Dynamic import would be better but is async - use global object instead
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		const inspector = (global as any).require("inspector");
		const hasInspectorUrl = !!inspector.url();
		return hasInspectorUrl || hasInspectArg || hasInspectEnv;
	} catch {
		// Ignore error and fall back to argument detection
		return hasInspectArg || hasInspectEnv;
	}
}



/**
 * Per-session skill/plugin telemetry. Called from both the interactive path
 * and the headless -p path (before runHeadless) — both go through
 * main.tsx but branch before the interactive startup path, so it needs two
 * call sites here rather than one here + one in QueryEngine.
 */
function logSessionTelemetry(): void {
	const model = parseUserSpecifiedModel(
		getInitialMainLoopModel() ?? getDefaultMainLoopModel(),
	);
	void logSkillsLoaded(
		getCwd(),
		getContextWindowForModel(model, getSdkBetas()),
	);
	void loadAllPluginsCacheOnly()
		.then(({ enabled, errors }) => {
			const managedNames = getManagedPluginNames();
			logPluginsEnabledForSession(
				enabled,
				managedNames,
				getPluginSeedDirs(),
			);
			logPluginLoadErrors(errors, managedNames);
		})
		.catch((err) => logError(err));
}

function getCertEnvVarTelemetry(): Record<string, boolean> {
	const result: Record<string, boolean> = {};
	if (process.env.NODE_EXTRA_CA_CERTS) {
		result.has_node_extra_ca_certs = true;
	}
	if (process.env.CLAUDE_CODE_CLIENT_CERT) {
		result.has_client_cert = true;
	}
	if (hasNodeOption("--use-system-ca")) {
		result.has_use_system_ca = true;
	}
	if (hasNodeOption("--use-openssl-ca")) {
		result.has_use_openssl_ca = true;
	}
	return result;
}

async function logStartupTelemetry(): Promise<void> {
	if (isAnalyticsDisabled()) return;
	const [isGit, worktreeCount, ghAuthStatus] = await Promise.all([
		getIsGit(),
		getWorktreeCount(),
		getGhAuthStatus(),
	]);

	logEvent("tengu_startup_telemetry", {
		is_git: isGit,
		worktree_count: worktreeCount,
		gh_auth_status:
			ghAuthStatus as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
		sandbox_enabled: SandboxManager.isSandboxingEnabled(),
		are_unsandboxed_commands_allowed:
			SandboxManager.areUnsandboxedCommandsAllowed(),
		is_auto_bash_allowed_if_sandbox_enabled:
			SandboxManager.isAutoAllowBashIfSandboxedEnabled(),
		auto_updater_disabled: isAutoUpdaterDisabled(),
		prefers_reduced_motion:
			getInitialSettings().prefersReducedMotion ?? false,
		...getCertEnvVarTelemetry(),
	});
}

// @[MODEL LAUNCH]: Consider any migrations you may need for model strings. See migrateSonnet1mToSonnet45.ts for an example.
// Bump this when adding a new sync migration so existing users re-run the set.
const CURRENT_MIGRATION_VERSION = 11;
function runMigrations(): void {
	if (getGlobalConfig().migrationVersion !== CURRENT_MIGRATION_VERSION) {
		migrateBypassPermissionsAcceptedToSettings();
		migrateEnableAllProjectMcpServersToSettings();
		resetProToOpusDefault();
		migrateSonnet1mToSonnet45();
		migrateLegacyOpusToCurrent();
		migrateSonnet45ToSonnet46();
		migrateOpusToOpus1m();
		migrateReplBridgeEnabledToRemoteControlAtStartup();
		if (feature("TRANSCRIPT_CLASSIFIER")) {
			resetAutoModeOptInForDefaultOffer();
		}
		if (process.env.USER_TYPE === "ant") {
			migrateFennecToOpus();
		}
		saveGlobalConfig((prev) =>
			prev.migrationVersion === CURRENT_MIGRATION_VERSION
				? prev
				: { ...prev, migrationVersion: CURRENT_MIGRATION_VERSION },
		);
	}
	// Async migration - fire and forget since it's non-blocking
	migrateChangelogFromConfig().catch(() => {
		// Silently ignore migration errors - will retry on next startup
	});
}

/**
 * Prefetch system context (including git status) only when it's safe to do so.
 * Git commands can execute arbitrary code via hooks and config (e.g., core.fsmonitor,
 * diff.external), so we must only run them after trust is established or in
 * non-interactive mode where trust is implicit.
 */
function prefetchSystemContextIfSafe(): void {
	const isNonInteractiveSession = getIsNonInteractiveSession();

	// In non-interactive mode (--print), trust dialog is skipped and
	// execution is considered trusted (as documented in help text)
	if (isNonInteractiveSession) {
		logForDiagnosticsNoPII(
			"info",
			"prefetch_system_context_non_interactive",
		);
		void getSystemContext();
		return;
	}

	// In interactive mode, only prefetch if trust has already been established
	const hasTrust = checkHasTrustDialogAccepted();
	if (hasTrust) {
		logForDiagnosticsNoPII("info", "prefetch_system_context_has_trust");
		void getSystemContext();
	} else {
		logForDiagnosticsNoPII(
			"info",
			"prefetch_system_context_skipped_no_trust",
		);
	}
	// Otherwise, don't prefetch - wait for trust to be established first
}

/**
 * Start background prefetches and housekeeping that are NOT needed before first render.
 * These are deferred from setup() to reduce event loop contention and child process
 * spawning during the critical startup path.
 * Call this after the REPL has been rendered.
 */
export function startDeferredPrefetches(): void {
	// This function runs after first render, so it doesn't block the initial paint.
	// However, the spawned processes and async work still contend for CPU and event
	// loop time, which skews startup benchmarks (CPU profiles, time-to-first-render
	// measurements). Skip all of it when we're only measuring startup performance.
	if (
		isEnvTruthy(process.env.CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER) ||
		// --bare: skip ALL prefetches. These are cache-warms for the REPL's
		// first-turn responsiveness (initUser, getUserContext, tips, countFiles,
		// modelCapabilities, change detectors). Scripted -p calls don't have a
		// "user is typing" window to hide this work in — it's pure overhead on
		// the critical path.
		isBareMode()
	) {
		return;
	}

	// Process-spawning prefetches (consumed at first API call, user is still typing)
	void initUser();
	void getUserContext();
	prefetchSystemContextIfSafe();
	void getRelevantTips();
	if (
		isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) &&
		!isEnvTruthy(process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH)
	) {
		void prefetchAwsCredentialsAndBedRockInfoIfSafe();
	}
	if (
		isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) &&
		!isEnvTruthy(process.env.CLAUDE_CODE_SKIP_VERTEX_AUTH)
	) {
		void prefetchGcpCredentialsIfSafe();
	}
	void countFilesRoundedRg(getCwd(), AbortSignal.timeout(3000), []);

	// Analytics and feature flag initialization
	void initializeAnalyticsGates();

	void refreshModelCapabilities();

	// File change detectors deferred from init() to unblock first render
	void settingsChangeDetector.initialize();
	if (!isBareMode()) {
		void skillChangeDetector.initialize();
	}

	// Event loop stall detector — logs when the main thread is blocked >500ms
	if (process.env.USER_TYPE === "ant") {
		void import("./utils/eventLoopStallDetector.js").then((m) =>
			m.startEventLoopStallDetector(),
		);
	}
}

function loadSettingsFromFlag(settingsFile: string): void {
	try {
		const trimmedSettings = settingsFile.trim();
		const looksLikeJson =
			trimmedSettings.startsWith("{") && trimmedSettings.endsWith("}");

		let settingsPath: string;

		if (looksLikeJson) {
			// It's a JSON string - validate and create temp file
			const parsedJson = safeParseJSON(trimmedSettings);
			if (!parsedJson) {
				process.stderr.write(
					chalk.red("Error: Invalid JSON provided to --settings\n"),
				);
				process.exit(1);
			}

			// Create a temporary file and write the JSON to it.
			// Use a content-hash-based path instead of random UUID to avoid
			// busting the Anthropic API prompt cache. The settings path ends up
			// in the Bash tool's sandbox denyWithinAllow list, which is part of
			// the tool description sent to the API. A random UUID per subprocess
			// changes the tool description on every query() call, invalidating
			// the cache prefix and causing a 12x input token cost penalty.
			// The content hash ensures identical settings produce the same path
			// across process boundaries (each SDK query() spawns a new process).
			settingsPath = generateTempFilePath("claude-settings", ".json", {
				contentHash: trimmedSettings,
			});
			writeFileSync_DEPRECATED(settingsPath, trimmedSettings, "utf8");
		} else {
			// It's a file path - resolve and validate by attempting to read
			const { resolvedPath: resolvedSettingsPath } = safeResolvePath(
				getFsImplementation(),
				settingsFile,
			);
			try {
				readFileSync(resolvedSettingsPath, "utf8");
			} catch (e) {
				if (isENOENT(e)) {
					process.stderr.write(
						chalk.red(
							`Error: Settings file not found: ${resolvedSettingsPath}\n`,
						),
					);
					process.exit(1);
				}
				throw e;
			}
			settingsPath = resolvedSettingsPath;
		}

		setFlagSettingsPath(settingsPath);
		resetSettingsCache();
	} catch (error) {
		if (error instanceof Error) {
			logError(error);
		}
		process.stderr.write(
			chalk.red(`Error processing settings: ${errorMessage(error)}\n`),
		);
		process.exit(1);
	}
}

function loadSettingSourcesFromFlag(settingSourcesArg: string): void {
	try {
		const sources = parseSettingSourcesFlag(settingSourcesArg);
		setAllowedSettingSources(sources);
		resetSettingsCache();
	} catch (error) {
		if (error instanceof Error) {
			logError(error);
		}
		process.stderr.write(
			chalk.red(
				`Error processing --setting-sources: ${errorMessage(error)}\n`,
			),
		);
		process.exit(1);
	}
}

/**
 * Parse and load settings flags early, before init()
 * This ensures settings are filtered from the start of initialization
 */
function eagerLoadSettings(): void {
	profileCheckpoint("eagerLoadSettings_start");
	// Parse --settings flag early to ensure settings are loaded before init()
	const settingsFile = eagerParseCliFlag("--settings");
	if (settingsFile) {
		loadSettingsFromFlag(settingsFile);
	}

	// Parse --setting-sources flag early to control which sources are loaded
	const settingSourcesArg = eagerParseCliFlag("--setting-sources");
	if (settingSourcesArg !== undefined) {
		loadSettingSourcesFromFlag(settingSourcesArg);
	}
	profileCheckpoint("eagerLoadSettings_end");
}

function initializeEntrypoint(isNonInteractive: boolean): void {
	// Skip if already set (e.g., by SDK or other entrypoints)
	if (process.env.CLAUDE_CODE_ENTRYPOINT) {
		return;
	}

	const cliArgs = process.argv.slice(2);

	// Check for MCP serve command (handle flags before mcp serve, e.g., --debug mcp serve)
	const mcpIndex = cliArgs.indexOf("mcp");
	if (mcpIndex !== -1 && cliArgs[mcpIndex + 1] === "serve") {
		process.env.CLAUDE_CODE_ENTRYPOINT = "mcp";
		return;
	}

	if (isEnvTruthy(process.env.CLAUDE_CODE_ACTION)) {
		process.env.CLAUDE_CODE_ENTRYPOINT = "claude-code-github-action";
		return;
	}

	// Note: 'local-agent' entrypoint is set by the local agent mode launcher
	// via CLAUDE_CODE_ENTRYPOINT env var (handled by early return above)

	// Set based on interactive status
	process.env.CLAUDE_CODE_ENTRYPOINT = isNonInteractive ? "sdk-cli" : "cli";
}

// Set by early argv processing when `claude open <url>` is detected (interactive mode only)
type PendingConnect = {
	url: string | undefined;
	authToken: string | undefined;
	dangerouslySkipPermissions: boolean;
};
const _pendingConnect: PendingConnect | undefined = feature("DIRECT_CONNECT")
	? {
			url: undefined,
			authToken: undefined,
			dangerouslySkipPermissions: false,
		}
	: undefined;

// Set by early argv processing when `claude assistant [sessionId]` is detected
type PendingAssistantChat = { sessionId?: string; discover: boolean };
const _pendingAssistantChat: PendingAssistantChat | undefined = feature(
	"KAIROS",
)
	? { sessionId: undefined, discover: false }
	: undefined;

// `claude ssh <host> [dir]` — parsed from argv early (same pattern as
// DIRECT_CONNECT above) so the main command path can pick it up and hand
// the REPL an SSH-backed session instead of a local one.
type PendingSSH = {
	host: string | undefined;
	cwd: string | undefined;
	permissionMode: string | undefined;
	dangerouslySkipPermissions: boolean;
	/** --local: spawn the child CLI directly, skip ssh/probe/deploy. e2e test mode. */
	local: boolean;
	/** Extra CLI args to forward to the remote CLI on initial spawn (--resume, -c). */
	extraCliArgs: string[];
	remoteBin: string | undefined;
};
const _pendingSSH: PendingSSH | undefined = feature("SSH_REMOTE")
	? {
			host: undefined,
			cwd: undefined,
			permissionMode: undefined,
			dangerouslySkipPermissions: false,
			local: false,
			extraCliArgs: [],
			remoteBin: undefined,
		}
	: undefined;

export async function main() {
	profileCheckpoint("main_function_start");

	// SECURITY: Prevent Windows from executing commands from current directory
	// This must be set before ANY command execution to prevent PATH hijacking attacks
	// See: https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-searchpathw
	process.env.NoDefaultCurrentDirectoryInExePath = "1";

	// Initialize warning handler early to catch warnings
	initializeWarningHandler();

	process.on("exit", () => {
		resetCursor();
	});
	process.on("SIGINT", () => {
		// In print mode, print.ts registers its own SIGINT handler that aborts
		// the in-flight query and calls gracefulShutdown; skip here to avoid
		// preempting it with a synchronous process.exit().
		if (process.argv.includes("-p") || process.argv.includes("--print")) {
			return;
		}
		process.exit(0);
	});
	profileCheckpoint("main_warning_handler_initialized");

	// Check for cc:// or cc+unix:// URL in argv — rewrite so the main command
	// handles it, giving the full interactive TUI instead of a stripped-down subcommand.
	// For headless (-p), we rewrite to the internal `open` subcommand.
	if (feature("DIRECT_CONNECT")) {
		const rawCliArgs = process.argv.slice(2);
		const ccIdx = rawCliArgs.findIndex(
			(a) => a.startsWith("cc://") || a.startsWith("cc+unix://"),
		);
		if (ccIdx !== -1 && _pendingConnect) {
			const ccUrl = rawCliArgs[ccIdx]!;
			const { parseConnectUrl } =
				await import("./server/parseConnectUrl.js");
			const parsed = parseConnectUrl(ccUrl);
			_pendingConnect.dangerouslySkipPermissions = rawCliArgs.includes(
				"--dangerously-skip-permissions",
			);

			if (rawCliArgs.includes("-p") || rawCliArgs.includes("--print")) {
				// Headless: rewrite to internal `open` subcommand
				const stripped = rawCliArgs.filter((_, i) => i !== ccIdx);
				const dspIdx = stripped.indexOf(
					"--dangerously-skip-permissions",
				);
				if (dspIdx !== -1) {
					stripped.splice(dspIdx, 1);
				}
				process.argv = [
					process.argv[0]!,
					process.argv[1]!,
					"open",
					ccUrl,
					...stripped,
				];
			} else {
				// Interactive: strip cc:// URL and flags, run main command
				_pendingConnect.url = parsed.serverUrl;
				_pendingConnect.authToken = parsed.authToken;
				const stripped = rawCliArgs.filter((_, i) => i !== ccIdx);
				const dspIdx = stripped.indexOf(
					"--dangerously-skip-permissions",
				);
				if (dspIdx !== -1) {
					stripped.splice(dspIdx, 1);
				}
				process.argv = [
					process.argv[0]!,
					process.argv[1]!,
					...stripped,
				];
			}
		}
	}

	// Handle deep link URIs early — this is invoked by the OS protocol handler
	// and should bail out before full init since it only needs to parse the URI
	// and open a terminal.
	if (feature("LODESTONE")) {
		const handleUriIdx = process.argv.indexOf("--handle-uri");
		if (handleUriIdx !== -1 && process.argv[handleUriIdx + 1]) {
			const { enableConfigs } = await import("./utils/config.js");
			enableConfigs();
			const uri = process.argv[handleUriIdx + 1]!;
			const { handleDeepLinkUri } =
				await import("./utils/deepLink/protocolHandler.js");
			const exitCode = await handleDeepLinkUri(uri);
			process.exit(exitCode);
		}

		// macOS URL handler: when LaunchServices launches our .app bundle, the
		// URL arrives via Apple Event (not argv). LaunchServices overwrites
		// __CFBundleIdentifier to the launching bundle's ID, which is a precise
		// positive signal — cheaper than importing and guessing with heuristics.
		if (
			process.platform === "darwin" &&
			process.env.__CFBundleIdentifier ===
				"com.anthropic.claude-code-url-handler"
		) {
			const { enableConfigs } = await import("./utils/config.js");
			enableConfigs();
			const { handleUrlSchemeLaunch } =
				await import("./utils/deepLink/protocolHandler.js");
			const urlSchemeResult = await handleUrlSchemeLaunch();
			process.exit(urlSchemeResult ?? 1);
		}
	}

	// `claude assistant [sessionId]` — stash and strip so the main
	// command handles it, giving the full interactive TUI. Position-0 only
	// (matching the ssh pattern below) — indexOf would false-positive on
	// `claude -p "explain assistant"`. Root-flag-before-subcommand
	// (e.g. `--debug assistant`) falls through to the stub, which
	// prints usage.
	if (feature("KAIROS") && _pendingAssistantChat) {
		const rawArgs = process.argv.slice(2);
		if (rawArgs[0] === "assistant") {
			const nextArg = rawArgs[1];
			if (nextArg && !nextArg.startsWith("-")) {
				_pendingAssistantChat.sessionId = nextArg;
				rawArgs.splice(0, 2); // drop 'assistant' and sessionId
				process.argv = [process.argv[0]!, process.argv[1]!, ...rawArgs];
			} else if (!nextArg) {
				_pendingAssistantChat.discover = true;
				rawArgs.splice(0, 1); // drop 'assistant'
				process.argv = [process.argv[0]!, process.argv[1]!, ...rawArgs];
			}
			// else: `claude assistant --help` → fall through to stub
		}
	}

	// `claude ssh <host> [dir]` — strip from argv so the main command handler
	// runs (full interactive TUI), stash the host/dir for the REPL branch at
	// ~line 3720 to pick up. Headless (-p) mode not supported in v1: SSH
	// sessions need the local REPL to drive them (interrupt, permissions).
	if (feature("SSH_REMOTE") && _pendingSSH) {
		const rawCliArgs = process.argv.slice(2);
		// SSH-specific flags can appear before the host positional (e.g.
		// `ssh --permission-mode auto host /tmp` — standard POSIX flags-before-
		// positionals). Pull them all out BEFORE checking whether a host was
		// given, so `claude ssh --permission-mode auto host` and `claude ssh host
		// --permission-mode auto` are equivalent. The host check below only needs
		// to guard against `-h`/`--help` (which commander should handle).
		if (rawCliArgs[0] === "ssh") {
			const localIdx = rawCliArgs.indexOf("--local");
			if (localIdx !== -1) {
				_pendingSSH.local = true;
				rawCliArgs.splice(localIdx, 1);
			}
			const dspIdx = rawCliArgs.indexOf("--dangerously-skip-permissions");
			if (dspIdx !== -1) {
				_pendingSSH.dangerouslySkipPermissions = true;
				rawCliArgs.splice(dspIdx, 1);
			}
			const pmIdx = rawCliArgs.indexOf("--permission-mode");
			if (
				pmIdx !== -1 &&
				rawCliArgs[pmIdx + 1] &&
				!rawCliArgs[pmIdx + 1]!.startsWith("-")
			) {
				_pendingSSH.permissionMode = rawCliArgs[pmIdx + 1];
				rawCliArgs.splice(pmIdx, 2);
			}
			const pmEqIdx = rawCliArgs.findIndex((a) =>
				a.startsWith("--permission-mode="),
			);
			if (pmEqIdx !== -1) {
				_pendingSSH.permissionMode = rawCliArgs[pmEqIdx]!.split("=")[1];
				rawCliArgs.splice(pmEqIdx, 1);
			}
			// Forward session-resume + model flags to the remote CLI's initial spawn.
			// --continue/-c and --resume <uuid> operate on the REMOTE session history
			// (which persists under the remote's ~/.claude/projects/<cwd>/).
			// --model controls which model the remote uses.
			const extractFlag = (
				flag: string,
				opts: { hasValue?: boolean; as?: string } = {},
			) => {
				const i = rawCliArgs.indexOf(flag);
				if (i !== -1) {
					_pendingSSH.extraCliArgs.push(opts.as ?? flag);
					const val = rawCliArgs[i + 1];
					if (opts.hasValue && val && !val.startsWith("-")) {
						_pendingSSH.extraCliArgs.push(val);
						rawCliArgs.splice(i, 2);
					} else {
						rawCliArgs.splice(i, 1);
					}
				}
				const eqI = rawCliArgs.findIndex((a) =>
					a.startsWith(`${flag}=`),
				);
				if (eqI !== -1) {
					_pendingSSH.extraCliArgs.push(
						opts.as ?? flag,
						rawCliArgs[eqI]!.slice(flag.length + 1),
					);
					rawCliArgs.splice(eqI, 1);
				}
			};
			const rbIdx = rawCliArgs.indexOf('--remote-bin');
			if (rbIdx !== -1 && rawCliArgs[rbIdx + 1] && !rawCliArgs[rbIdx + 1]!.startsWith('-')) {
				_pendingSSH.remoteBin = rawCliArgs[rbIdx + 1];
				rawCliArgs.splice(rbIdx, 2);
			}
			const rbEqIdx = rawCliArgs.findIndex(a => a.startsWith('--remote-bin='));
			if (rbEqIdx !== -1) {
				_pendingSSH.remoteBin = rawCliArgs[rbEqIdx]!.split('=').slice(1).join('=');
				rawCliArgs.splice(rbEqIdx, 1);
			}

			extractFlag("-c", { as: "--continue" });
			extractFlag("--continue");
			extractFlag("--resume", { hasValue: true });
			extractFlag("--model", { hasValue: true });
		}
		// After pre-extraction, any remaining dash-arg at [1] is either -h/--help
		// (commander handles) or an unknown-to-ssh flag (fall through to commander
		// so it surfaces a proper error). Only a non-dash arg is the host.
		if (
			rawCliArgs[0] === "ssh" &&
			rawCliArgs[1] &&
			!rawCliArgs[1].startsWith("-")
		) {
			_pendingSSH.host = rawCliArgs[1];
			// Optional positional cwd.
			let consumed = 2;
			if (rawCliArgs[2] && !rawCliArgs[2].startsWith("-")) {
				_pendingSSH.cwd = rawCliArgs[2];
				consumed = 3;
			}
			const rest = rawCliArgs.slice(consumed);

			// Headless (-p) mode is not supported with SSH in v1 — reject early
			// so the flag doesn't silently cause local execution.
			if (rest.includes("-p") || rest.includes("--print")) {
				process.stderr.write(
					"Error: headless (-p/--print) mode is not supported with claude ssh\n",
				);
				gracefulShutdownSync(1);
				return;
			}

			// Rewrite argv so the main command sees remaining flags but not `ssh`.
			process.argv = [process.argv[0]!, process.argv[1]!, ...rest];
		}
	}

		// Check for -p/--print and --init-only flags early to set isInteractiveSession before init()
		// This is needed because telemetry initialization calls auth functions that need this flag
		const cliArgs = process.argv.slice(2);
		const hasPrintFlag = cliArgs.includes("-p") || cliArgs.includes("--print");
		const hasInitOnlyFlag = cliArgs.includes("--init-only");
		const hasSdkUrl = cliArgs.some((arg) => arg.startsWith("--sdk-url"));
		const forceInteractive = isEnvTruthy(process.env.CLAUDE_CODE_FORCE_INTERACTIVE);
		const isNonInteractive =
			hasPrintFlag || hasInitOnlyFlag || hasSdkUrl || (!forceInteractive && !process.stdout.isTTY);

	// Stop capturing early input for non-interactive modes
	if (isNonInteractive) {
		stopCapturingEarlyInput();
	}

	// Set simplified tracking fields
	const isInteractive = !isNonInteractive;
	setIsInteractive(isInteractive);

	// Initialize entrypoint based on mode - needs to be set before any event is logged
	initializeEntrypoint(isNonInteractive);

	// Determine client type
	const clientType = (() => {
		if (isEnvTruthy(process.env.GITHUB_ACTIONS)) return "github-action";
		if (process.env.CLAUDE_CODE_ENTRYPOINT === "sdk-ts")
			return "sdk-typescript";
		if (process.env.CLAUDE_CODE_ENTRYPOINT === "sdk-py")
			return "sdk-python";
		if (process.env.CLAUDE_CODE_ENTRYPOINT === "sdk-cli") return "sdk-cli";
		if (process.env.CLAUDE_CODE_ENTRYPOINT === "claude-vscode")
			return "claude-vscode";
		if (process.env.CLAUDE_CODE_ENTRYPOINT === "local-agent")
			return "local-agent";
		if (process.env.CLAUDE_CODE_ENTRYPOINT === "claude-desktop")
			return "claude-desktop";

		// Check if session-ingress token is provided (indicates remote session)
		const hasSessionIngressToken =
			process.env.CLAUDE_CODE_SESSION_ACCESS_TOKEN ||
			process.env.CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR;
		if (
			process.env.CLAUDE_CODE_ENTRYPOINT === "remote" ||
			hasSessionIngressToken
		) {
			return "remote";
		}

		return "cli";
	})();
	setClientType(clientType);

	const previewFormat = process.env.CLAUDE_CODE_QUESTION_PREVIEW_FORMAT;
	if (previewFormat === "markdown" || previewFormat === "html") {
		setQuestionPreviewFormat(previewFormat);
	} else if (
		!clientType.startsWith("sdk-") &&
		// Desktop and CCR pass previewFormat via toolConfig; when the feature is
		// gated off they pass undefined — don't override that with markdown.
		clientType !== "claude-desktop" &&
		clientType !== "local-agent" &&
		clientType !== "remote"
	) {
		setQuestionPreviewFormat("markdown");
	}

	// Tag sessions created via `claude remote-control` so the backend can identify them
	if (process.env.CLAUDE_CODE_ENVIRONMENT_KIND === "bridge") {
		setSessionSource("remote-control");
	}

	profileCheckpoint("main_client_type_determined");

	// Parse and load settings flags early, before init()
	eagerLoadSettings();

	profileCheckpoint("main_before_run");

	await run();
	profileCheckpoint("main_after_run");
}

async function getInputPrompt(
	prompt: string,
	inputFormat: "text" | "stream-json",
): Promise<string | AsyncIterable<string>> {
	if (
		!process.stdin.isTTY &&
		// Input hijacking breaks MCP.
		!process.argv.includes("mcp")
	) {
		if (inputFormat === "stream-json") {
			return process.stdin;
		}
		process.stdin.setEncoding("utf8");
		let data = "";
		const onData = (chunk: string) => {
			data += chunk;
		};
		process.stdin.on("data", onData);
		// If no data arrives in 3s, stop waiting and warn. Stdin is likely an
		// inherited pipe from a parent that isn't writing (subprocess spawned
		// without explicit stdin handling). 3s covers slow producers like curl,
		// jq on large files, python with import overhead. The warning makes
		// silent data loss visible for the rare producer that's slower still.
		const timedOut = await peekForStdinData(process.stdin, 3000);
		process.stdin.off("data", onData);
		if (timedOut) {
			process.stderr.write(
				"Warning: no stdin data received in 3s, proceeding without it. " +
					"If piping from a slow command, redirect stdin explicitly: < /dev/null to skip, or wait longer.\n",
			);
		}
		return [prompt, data].filter(Boolean).join("\n");
	}
	return prompt;
}

async function run(): Promise<CommanderCommand> {
	profileCheckpoint("run_function_start");

	// Create help config that sorts options by long option name and puts
	// Commands section before Options section in the root command's help output.
	// Commander supports compareOptions/formatHelp at runtime but
	// @commander-js/extra-typings doesn't include them in the type definitions,
	// so we use Object.assign to add them.
	function createSortedHelpConfig(): {
		sortSubcommands: true;
		sortOptions: true;
	} {
		const getOptionSortKey = (opt: Option): string =>
			opt.long?.replace(/^--/, "") ?? opt.short?.replace(/^-/, "") ?? "";
		return Object.assign(
			{ sortSubcommands: true, sortOptions: true } as const,
			{
				compareOptions: (a: Option, b: Option) =>
					getOptionSortKey(a).localeCompare(getOptionSortKey(b)),
				// Reorder help sections: Usage → Description → Arguments →
				// Commands → Options (matching cs/opencode style).
				// Uses duck-typed access because @commander-js/extra-typings
				// doesn't expose the full Help class API.
				formatHelp: (cmd: CommanderCommand, helper: unknown) => {
					const h = helper as {
						padWidth: (cmd: CommanderCommand, helper: unknown) => number;
						helpWidth: number;
						commandUsage: (cmd: CommanderCommand) => string;
						commandDescription: (cmd: CommanderCommand) => string;
						visibleArguments: (cmd: CommanderCommand) => unknown[];
						visibleCommands: (cmd: CommanderCommand) => unknown[];
						visibleOptions: (cmd: CommanderCommand) => unknown[];
						argumentTerm: (arg: unknown) => string;
						argumentDescription: (arg: unknown) => string;
						subcommandTerm: (cmd: unknown) => string;
						subcommandDescription: (cmd: unknown) => string;
						optionTerm: (opt: unknown) => string;
						optionDescription: (opt: unknown) => string;
						formatItem: (term: string, termWidth: number, description: string, helper: unknown) => string;
						styleTitle: (s: string) => string;
						styleUsage: (s: string) => string;
						styleCommandDescription: (s: string) => string;
						styleArgumentTerm: (s: string) => string;
						styleArgumentDescription: (s: string) => string;
						styleSubcommandTerm: (s: string) => string;
						styleSubcommandDescription: (s: string) => string;
						styleOptionTerm: (s: string) => string;
						styleOptionDescription: (s: string) => string;
						boxWrap: (s: string, width: number) => string;
					};
					const termWidth = h.padWidth(cmd, helper);
					const helpWidth = h.helpWidth ?? 80;
					const fmt = (term: string, desc: string) =>
						h.formatItem(term, termWidth, desc, helper);

					let output = [
						`${h.styleTitle('Usage:')} ${h.styleUsage(h.commandUsage(cmd))}`,
						'',
					];

					const desc = h.commandDescription(cmd);
					if (desc.length > 0) {
						output = output.concat([h.boxWrap(h.styleCommandDescription(desc), helpWidth), '']);
					}

					// Order: Commands → Options → Arguments (matches usage line: [options] [command] [prompt])
					const cmdList = h.visibleCommands(cmd).map(c =>
						fmt(h.styleSubcommandTerm(h.subcommandTerm(c)), h.styleSubcommandDescription(h.subcommandDescription(c))));
					if (cmdList.length > 0) output = output.concat([h.styleTitle('Commands:'), ...cmdList, '']);

					const optList = h.visibleOptions(cmd).map(o =>
						fmt(h.styleOptionTerm(h.optionTerm(o)), h.styleOptionDescription(h.optionDescription(o))));
					if (optList.length > 0) output = output.concat([h.styleTitle('Options:'), ...optList, '']);

					const argList = h.visibleArguments(cmd).map(a =>
						fmt(h.styleArgumentTerm(h.argumentTerm(a)), h.styleArgumentDescription(h.argumentDescription(a))));
					if (argList.length > 0) output = output.concat([h.styleTitle('Arguments:'), ...argList, '']);

					return output.join('\n');
				},
			},
		);
	}
	const program = new CommanderCommand()
		.addHelpCommand(false)
		.configureHelp(createSortedHelpConfig())
		.enablePositionalOptions();
	profileCheckpoint("run_commander_initialized");

	// Use preAction hook to run initialization only when executing a command,
	// not when displaying help. This avoids the need for env variable signaling.
	program.hook("preAction", async (thisCommand) => {
		profileCheckpoint("preAction_start");
		// Await async subprocess loads started at module evaluation (lines 12-20).
		// Nearly free — subprocesses complete during the ~135ms of imports above.
		// Must resolve before init() which triggers the first settings read
		// (applySafeConfigEnvironmentVariables → getSettingsForSource('policySettings')
		// → isRemoteManagedSettingsEligible → sync keychain reads otherwise ~65ms).
		await Promise.all([
			ensureMdmSettingsLoaded(),
			ensureKeychainPrefetchCompleted(),
		]);
		profileCheckpoint("preAction_after_mdm");
		await init();
		profileCheckpoint("preAction_after_init");

		// process.title on Windows sets the console title directly; on POSIX,
		// terminal shell integration may mirror the process name to the tab.
		// After init() so settings.json env can also gate this (gh-4765).
		if (!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE)) {
			process.title = "csc";
		}

		// Attach logging sinks so subcommand handlers can use logEvent/logError.
		// Before PR #11106 logEvent dispatched directly; after, events queue until
		// a sink attaches. setup() attaches sinks for the default command, but
		// subcommands (doctor, mcp, plugin, auth) never call setup() and would
		// silently drop events on process.exit(). Both inits are idempotent.
		const { initSinks } = await import("./utils/sinks.js");
		initSinks();
		profileCheckpoint("preAction_after_sinks");

		// gh-33508: --plugin-dir is a top-level program option. The default
		// action reads it from its own options destructure, but subcommands
		// (plugin list, plugin install, mcp *) have their own actions and
		// never see it. Wire it up here so getInlinePlugins() works everywhere.
		// thisCommand.opts() is typed {} here because this hook is attached
		// before .option('--plugin-dir', ...) in the chain — extra-typings
		// builds the type as options are added. Narrow with a runtime guard;
		// the collect accumulator + [] default guarantee string[] in practice.
		const pluginDir = thisCommand.getOptionValue("pluginDir");
		if (
			Array.isArray(pluginDir) &&
			pluginDir.length > 0 &&
			pluginDir.every((p) => typeof p === "string")
		) {
			setInlinePlugins(pluginDir);
			clearPluginCache("preAction: --plugin-dir inline plugins");
		}

		runMigrations();
		profileCheckpoint("preAction_after_migrations");

		// Load remote managed settings for enterprise customers (non-blocking)
		// Fails open - if fetch fails, continues without remote settings
		// Settings are applied via hot-reload when they arrive
		// Must happen after init() to ensure config reading is allowed
		// void loadRemoteManagedSettings();
		void loadPolicyLimits();

		profileCheckpoint("preAction_after_remote_settings");

		// Load settings sync (non-blocking, fail-open)
		// CLI: uploads local settings to remote (CCR download is handled by print.ts)
		if (feature("UPLOAD_USER_SETTINGS")) {
			void import("./services/settingsSync/index.js").then((m) =>
				m.uploadUserSettingsInBackground(),
			);
		}

		profileCheckpoint("preAction_after_settings_sync");
	});

	program
		.name("csc")
		.description(`CoStrict AI coding assistant`)
		.usage("[command] [options] [prompt]")
		.argument("[prompt]", "Your prompt", String)
		// Subcommands inherit helpOption via commander's copyInheritedSettings —
		// setting it once here covers mcp, plugin, auth, and all other subcommands.
		.helpOption("-h, --help", "Display help for command")
		.option(
			"-d, --debug [filter]",
			'Enable debug mode with optional category filtering (e.g., "api,hooks" or "!1p,!file")',
			(_value: string | true) => {
				// If value is provided, it will be the filter string
				// If not provided but flag is present, value will be true
				// The actual filtering is handled in debug.ts by parsing process.argv
				return true;
			},
		)
		.addOption(
			new Option("--debug-to-stderr", "Enable debug mode (to stderr)")
				.argParser(Boolean)
				.hideHelp(),
		)
		.addOption(
			new Option("--debug-file <path>", "Write debug logs to a specific file path (implicitly enables debug mode)")
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option("--verbose", "Override verbose mode setting from config")
				.argParser(() => true)
				.hideHelp(),
		)
		.option(
			"-p, --print",
			"Print response and exit (useful for pipes). Note: The workspace trust dialog is skipped when CSC is run with the -p mode. Only use this flag in directories you trust.",
			() => true,
		)
		.addOption(
			new Option(
				"--bare",
				"Minimal mode: skip hooks, LSP, plugin sync, attribution, auto-memory, background prefetches, keychain reads, and CLAUDE.md auto-discovery. Sets CLAUDE_CODE_SIMPLE=1. Anthropic auth is strictly ANTHROPIC_API_KEY or apiKeyHelper via --settings (OAuth and keychain are never read). 3P providers (Bedrock/Vertex/Foundry) use their own credentials. Skills still resolve via /skill-name. Explicitly provide context via: --system-prompt[-file], --append-system-prompt[-file], --add-dir (CLAUDE.md dirs), --mcp-config, --settings, --agents, --plugin-dir.",
			)
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--init",
				"Run Setup hooks with init trigger, then continue",
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--init-only",
				"Run Setup and SessionStart:startup hooks, then exit",
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--maintenance",
				"Run Setup hooks with maintenance trigger, then continue",
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--output-format <format>",
				'Output format (only works with --print): "text" (default), "json" (single result), or "stream-json" (realtime streaming)',
			).choices(["text", "json", "stream-json"]),
		)
		.addOption(
			new Option(
				"--json-schema <schema>",
				"JSON Schema for structured output validation. " +
					'Example: {"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}',
			)
				.argParser(String)
				.hideHelp(),
		)
		.addOption(
			new Option("--include-hook-events", "Include all hook lifecycle events in the output stream (only works with --output-format=stream-json)")
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option("--include-partial-messages", "Include partial message chunks as they arrive (only works with --print and --output-format=stream-json)")
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--input-format <format>",
				'Input format (only works with --print): "text" (default), or "stream-json" (realtime streaming input)',
			)
				.choices(["text", "stream-json"])
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--mcp-debug",
				"[DEPRECATED. Use --debug instead] Enable MCP debug mode (shows MCP server errors)",
			)
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option("--dangerously-skip-permissions", "Bypass all permission checks. Recommended only for sandboxes with no internet access.")
				.argParser(() => true),
		)
	// 		.option(
	// 			"--allow-dangerously-skip-permissions",
	// 			"Enable bypassing all permission checks as an option, without it being enabled by default. Recommended only for sandboxes with no internet access.",
	// 			() => true,
	// 		)
		.addOption(
			new Option(
				"--thinking <mode>",
				"Thinking mode: enabled (equivalent to adaptive), disabled",
			)
				.choices(["enabled", "adaptive", "disabled"])
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--max-thinking-tokens <tokens>",
				"[DEPRECATED. Use --thinking instead for newer models] Maximum number of thinking tokens (only works with --print)",
			)
				.argParser(Number)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--max-turns <turns>",
				"Maximum number of agentic turns in non-interactive mode. This will early exit the conversation after the specified number of turns. (only works with --print)",
			)
				.argParser(Number)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--max-budget-usd <amount>",
				"Maximum dollar amount to spend on API calls (only works with --print)",
			)
				.argParser((value) => {
					const amount = Number(value);
					if (isNaN(amount) || amount <= 0) {
						throw new Error(
							"--max-budget-usd must be a positive number greater than 0",
						);
					}
					return amount;
				})
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--task-budget <tokens>",
				"API-side task budget in tokens (output_config.task_budget)",
			)
				.argParser((value) => {
					const tokens = Number(value);
					if (
						isNaN(tokens) ||
						tokens <= 0 ||
						!Number.isInteger(tokens)
					) {
						throw new Error(
							"--task-budget must be a positive integer",
						);
					}
					return tokens;
				})
				.hideHelp(),
		)
		.addOption(
			new Option("--replay-user-messages", "Re-emit user messages from stdin back on stdout for acknowledgment (only works with --input-format=stream-json and --output-format=stream-json)")
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--enable-auth-status",
				"Enable auth status messages in SDK mode",
			)
				.default(false)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--allowed-tools <tools...>",
				'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")',
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--allowedTools <tools...>",
				'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")',
			).hideHelp(),
		)
		.addOption(
			new Option("--tools <tools...>", 'Specify the list of available tools from the built-in set. Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read").').hideHelp(),
		)
		.addOption(
			new Option(
				"--disallowed-tools <tools...>",
				'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")',
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--disallowedTools <tools...>",
				'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")',
			).hideHelp(),
		)
		.addOption(
			new Option("--mcp-config <configs...>", "Load MCP servers from JSON files or strings (space-separated)").hideHelp(),
		)
		.addOption(
			new Option(
				"--permission-prompt-tool <tool>",
				"MCP tool to use for permission prompts (only works with --print)",
			)
				.argParser(String)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--system-prompt <prompt>",
				"System prompt to use for the session",
			).argParser(String),
		)
		.addOption(
			new Option(
				"--system-prompt-file <file>",
				"Read system prompt from a file",
			)
				.argParser(String)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--append-system-prompt <prompt>",
				"Append a system prompt to the default system prompt",
			)
				.argParser(String)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--append-system-prompt-file <file>",
				"Read system prompt from a file and append to the default system prompt",
			)
				.argParser(String)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--permission-mode <mode>",
				"Permission mode to use for the session",
			)
				.argParser(String)
				.choices(PERMISSION_MODES),
		)
		.option(
			"-c, --continue",
			"Continue the most recent conversation in the current directory",
			() => true,
		)
		.option(
			"-r, --resume [value]",
			"Resume a conversation by session ID, or open interactive picker with optional search term",
			(value) => value || true,
		)
		.addOption(
			new Option("--fork-session", "When resuming, create a new session ID instead of reusing the original (use with --resume or --continue)")
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--prefill <text>",
				"Pre-fill the prompt input with text without submitting it",
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--deep-link-origin",
				"Signal that this session was launched from a deep link",
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--deep-link-repo <slug>",
				"Repo slug the deep link ?repo= parameter resolved to the current cwd",
			).hideHelp(),
		)
		.addOption(
			new Option(
				"--deep-link-last-fetch <ms>",
				"FETCH_HEAD mtime in epoch ms, precomputed by the deep link trampoline",
			)
				.argParser((v) => {
					const n = Number(v);
					return Number.isFinite(n) ? n : undefined;
				})
				.hideHelp(),
		)
		.addOption(
			new Option("--from-pr [value]", "Resume a session linked to a PR by PR number/URL, or open interactive picker with optional search term")
				.argParser((value) => value || true)
				.hideHelp(),
		)
		.addOption(
			new Option("--no-session-persistence", "Disable session persistence - sessions will not be saved to disk and cannot be resumed (only works with --print)")
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--resume-session-at <message id>",
				"When resuming, only messages up to and including the assistant message with <message.id> (use with --resume in print mode)",
			)
				.argParser(String)
				.hideHelp(),
		)
		.addOption(
			new Option(
				"--rewind-files <user-message-id>",
				"Restore files to state at the specified user message and exit (requires --resume)",
			).hideHelp(),
		)
		// @[MODEL LAUNCH]: Update the example model ID in the --model help text.
		.option(
			"--model <model>",
			`Model for the current session. Provide a model's full name (e.g. 'GLM-5.1').`,
		)
		.addOption(
			new Option(
				"--effort <level>",
				`Effort level for the current session (low, medium, high, max)`,
			)
				.argParser((rawValue: string) => {
					const value = rawValue.toLowerCase();
					const allowed = ["low", "medium", "high", "max"];
					if (!allowed.includes(value)) {
						throw new InvalidArgumentError(
							`It must be one of: ${allowed.join(", ")}`,
						);
					}
					return value;
				})
				.hideHelp(),
		)
		.option(
			"--agent <agent>",
			`Agent for the current session. Overrides the 'agent' setting.`,
		)
		.addOption(
			new Option("--betas <betas...>", "Beta headers to include in API requests (API key users only)").hideHelp(),
		)
		.addOption(
			new Option("--fallback-model <model>", "Enable automatic fallback to specified model when default model is overloaded (only works with --print)").hideHelp(),
		)
		.addOption(
			new Option(
				"--workload <tag>",
				"Workload tag for billing-header attribution (cc_workload). Process-scoped; set by SDK daemon callers that spawn subprocesses for cron work. (only works with --print)",
			).hideHelp(),
		)
		.option(
			"--settings <file-or-json>",
			"Path to a settings JSON file or a JSON string to load additional settings from",
		)
		.option(
			"--add-dir <directories...>",
			"Additional directories to allow tool access to",
		)
		.addOption(
			new Option("--ide", "Automatically connect to IDE on startup if exactly one valid IDE is available")
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option("--strict-mcp-config", "Only use MCP servers from --mcp-config, ignoring all other MCP configurations")
				.argParser(() => true)
				.hideHelp(),
		)
		.addOption(
			new Option("--session-id <uuid>", "Use a specific session ID for the conversation (must be a valid UUID)").hideHelp(),
		)
		.addOption(
			new Option("-n, --name <name>", "Set a display name for this session (shown in /resume and terminal title)").hideHelp(),
		)
		.addOption(
			new Option("--agents <json>", 'JSON object defining custom agents (e.g. \'{"reviewer": {"description": "Reviews code", "prompt": "You are a code reviewer"}}\')').hideHelp(),
		)
		.addOption(
			new Option("--setting-sources <sources>", "Comma-separated list of setting sources to load (user, project, local).").hideHelp(),
		)
		// gh-33508: <paths...> (variadic) consumed everything until the next
		// --flag. `claude --plugin-dir /path mcp add --transport http` swallowed
		// `mcp` and `add` as paths, then choked on --transport as an unknown
		// top-level option. Single-value + collect accumulator means each
		// --plugin-dir takes exactly one arg; repeat the flag for multiple dirs.
		.addOption(
			new Option("--plugin-dir <path>", "Load plugins from a directory for this session only (repeatable: --plugin-dir A --plugin-dir B)")
				.argParser((val: string, prev: string[]) => [...prev, val])
				.default([] as string[])
				.hideHelp(),
		)
		.addOption(
			new Option("--disable-slash-commands", "Disable all skills").argParser(() => true).hideHelp(),
		)
		.addOption(new Option("--chrome", "Enable Claude in Chrome integration").hideHelp())
		.addOption(new Option("--no-chrome", "Disable Claude in Chrome integration").hideHelp())
		.addOption(
			new Option("--file <specs...>", "File resources to download at startup. Format: file_id:relative_path (e.g., --file file_abc:doc.txt file_def:img.png)").hideHelp(),
		)
		.action(async (prompt, options) => {
			profileCheckpoint("action_handler_start");

			// --bare = one-switch minimal mode. Sets SIMPLE so all the existing
			// gates fire (CLAUDE.md, skills, hooks inside executeHooks, agent
			// dir-walk). Must be set before setup() / any of the gated work runs.
			if ((options as { bare?: boolean }).bare) {
				process.env.CLAUDE_CODE_SIMPLE = "1";
			}

			// Ignore "code" as a prompt - treat it the same as no prompt
			if (prompt === "code") {
				logEvent("tengu_code_prompt_ignored", {});
				console.warn(
					chalk.yellow(
						"Tip: You can launch CoStrict with just `csc`",
					),
				);
				prompt = undefined;
			}

			// Log event for any single-word prompt
			if (
				prompt &&
				typeof prompt === "string" &&
				!/\s/.test(prompt) &&
				prompt.length > 0
			) {
				logEvent("tengu_single_word_prompt", { length: prompt.length });
			}

			// Assistant mode: when .claude/settings.json has assistant: true AND
			// the tengu_kairos GrowthBook gate is on, force brief on. Permission
			// mode is left to the user — settings defaultMode or --permission-mode
			// apply as normal. REPL-typed messages already default to 'next'
			// priority (messageQueueManager.enqueue) so they drain mid-turn between
			// tool calls. SendUserMessage (BriefTool) is enabled via the brief env
			// var. SleepTool stays disabled (its isEnabled() gates on proactive).
			// kairosEnabled is computed once here and reused at the
			// getAssistantSystemPromptAddendum() call site further down.
			//
			// Trust gate: .claude/settings.json is attacker-controllable in an
			// untrusted clone. We run ~1000 lines before showSetupScreens() shows
			// the trust dialog, and by then we've already appended
			// .claude/agents/assistant.md to the system prompt. Refuse to activate
			// until the directory has been explicitly trusted.
			let kairosEnabled = false;
			let assistantTeamContext:
				| Awaited<
						ReturnType<
							NonNullable<
								typeof assistantModule
							>["initializeAssistantTeam"]
						>
				  >
				| undefined;
			if (
				feature("KAIROS") &&
				(options as { assistant?: boolean }).assistant &&
				assistantModule
			) {
				// --assistant (Agent SDK daemon mode): force the latch before
				// isAssistantMode() runs below. The daemon has already checked
				// entitlement — don't make the child re-check tengu_kairos.
				assistantModule.markAssistantForced();
			}
			if (
				feature("KAIROS") &&
				assistantModule &&
				(assistantModule.isAssistantForced() ||
					(options as Record<string, unknown>).assistant === true) &&
				// Spawned teammates share the leader's cwd + settings.json, so
				// the flag is true for them too. --agent-id being set
				// means we ARE a spawned teammate (extractTeammateOptions runs
				// ~170 lines later so check the raw commander option) — don't
				// re-init the team or override teammateMode/proactive/brief.
				!(options as { agentId?: unknown }).agentId &&
				kairosGate
			) {
				if (!checkHasTrustDialogAccepted()) {
					console.warn(
						chalk.yellow(
							"Assistant mode disabled: directory is not trusted. Accept the trust dialog and restart.",
						),
					);
				} else {
					// Blocking gate check — returns cached `true` instantly; if disk
					// cache is false/missing, lazily inits GrowthBook and fetches fresh
					// (max ~5s). --assistant skips the gate entirely (daemon is
					// pre-entitled).
					kairosEnabled =
						assistantModule.isAssistantForced() ||
						(await kairosGate.isKairosEnabled());
					if (kairosEnabled) {
						const opts = options as { brief?: boolean };
						opts.brief = true;
						setKairosActive(true);
						// Pre-seed an in-process team so Agent(name: "foo") spawns
						// teammates without TeamCreate. Must run BEFORE setup() captures
						// the teammateMode snapshot (initializeAssistantTeam calls
						// setCliTeammateModeOverride internally).
						assistantTeamContext =
							await assistantModule.initializeAssistantTeam();
					}
				}
			}

			const {
				debug = false,
				debugToStderr = false,
				dangerouslySkipPermissions,
				allowDangerouslySkipPermissions = false,
				tools: baseTools = [],
				allowedTools = [],
				disallowedTools = [],
				mcpConfig = [],
				permissionMode: permissionModeCli,
				addDir = [],
				fallbackModel,
				betas = [],
				ide = false,
				sessionId,
				includeHookEvents,
				includePartialMessages,
			} = options;

			if (options.prefill) {
				seedEarlyInput(options.prefill);
			}

			// Promise for file downloads - started early, awaited before REPL renders
			let fileDownloadPromise: Promise<DownloadResult[]> | undefined;

			const agentsJson = options.agents;
			const agentCli = options.agent;
			if (feature("BG_SESSIONS") && agentCli) {
				process.env.CLAUDE_CODE_AGENT = agentCli;
			}

			// NOTE: LSP manager initialization is intentionally deferred until after
			// the trust dialog is accepted. This prevents plugin LSP servers from
			// executing code in untrusted directories before user consent.

			// Extract these separately so they can be modified if needed
			let outputFormat = options.outputFormat;
			let inputFormat = options.inputFormat;
			let verbose = options.verbose ?? getGlobalConfig().verbose;
			let print = options.print;
			const init = options.init ?? false;
			const initOnly = options.initOnly ?? false;
			const maintenance = options.maintenance ?? false;

			// Extract disable slash commands flag
			const disableSlashCommands = options.disableSlashCommands || false;

			// Extract tasks mode options (ant-only)
			const tasksOption =
				process.env.USER_TYPE === "ant" &&
				(options as { tasks?: boolean | string }).tasks;
			const taskListId = tasksOption
				? typeof tasksOption === "string"
					? tasksOption
					: DEFAULT_TASKS_MODE_TASK_LIST_ID
				: undefined;
			if (process.env.USER_TYPE === "ant" && taskListId) {
				process.env.CLAUDE_CODE_TASK_LIST_ID = taskListId;
			}

			// Extract worktree option
			// worktree can be true (flag without value) or a string (custom name or PR reference)
			const worktreeOption = isWorktreeModeEnabled()
				? (options as { worktree?: boolean | string }).worktree
				: undefined;
			let worktreeName =
				typeof worktreeOption === "string" ? worktreeOption : undefined;
			const worktreeEnabled = worktreeOption !== undefined;

			// Check if worktree name is a PR reference (#N or GitHub PR URL)
			let worktreePRNumber: number | undefined;
			if (worktreeName) {
				const prNum = parsePRReference(worktreeName);
				if (prNum !== null) {
					worktreePRNumber = prNum;
					worktreeName = undefined; // slug will be generated in setup()
				}
			}

			// Extract tmux option (requires --worktree)
			const tmuxEnabled =
				isWorktreeModeEnabled() &&
				(options as { tmux?: boolean }).tmux === true;

			// Validate tmux option
			if (tmuxEnabled) {
				if (!worktreeEnabled) {
					process.stderr.write(
						chalk.red("Error: --tmux requires --worktree\n"),
					);
					process.exit(1);
				}
				if (getPlatform() === "windows") {
					process.stderr.write(
						chalk.red(
							"Error: --tmux is not supported on Windows\n",
						),
					);
					process.exit(1);
				}
				if (!(await isTmuxAvailable())) {
					process.stderr.write(
						chalk.red(
							`Error: tmux is not installed.\n${getTmuxInstallInstructions()}\n`,
						),
					);
					process.exit(1);
				}
			}

			// Extract teammate options (for tmux-spawned agents)
			// Declared outside the if block so it's accessible later for system prompt addendum
			let storedTeammateOpts: TeammateOptions | undefined;
			if (isAgentSwarmsEnabled()) {
				// Extract agent identity options (for tmux-spawned agents)
				// These replace the CLAUDE_CODE_* environment variables
				const teammateOpts = extractTeammateOptions(options);
				storedTeammateOpts = teammateOpts;

				// If any teammate identity option is provided, all three required ones must be present
				const hasAnyTeammateOpt =
					teammateOpts.agentId ||
					teammateOpts.agentName ||
					teammateOpts.teamName;
				const hasAllRequiredTeammateOpts =
					teammateOpts.agentId &&
					teammateOpts.agentName &&
					teammateOpts.teamName;

				if (hasAnyTeammateOpt && !hasAllRequiredTeammateOpts) {
					process.stderr.write(
						chalk.red(
							"Error: --agent-id, --agent-name, and --team-name must all be provided together\n",
						),
					);
					process.exit(1);
				}

				// If teammate identity is provided via CLI, set up dynamicTeamContext
				if (
					teammateOpts.agentId &&
					teammateOpts.agentName &&
					teammateOpts.teamName
				) {
					getTeammateUtils().setDynamicTeamContext?.({
						agentId: teammateOpts.agentId,
						agentName: teammateOpts.agentName,
						teamName: teammateOpts.teamName,
						color: teammateOpts.agentColor,
						planModeRequired:
							teammateOpts.planModeRequired ?? false,
						parentSessionId: teammateOpts.parentSessionId,
					});
				}

				// Set teammate mode CLI override if provided
				// This must be done before setup() captures the snapshot
				if (teammateOpts.teammateMode) {
					getTeammateModeSnapshot().setCliTeammateModeOverride?.(
						teammateOpts.teammateMode,
					);
				}
			}

			// Extract remote sdk options
			const sdkUrl = (options as { sdkUrl?: string }).sdkUrl ?? undefined;

			// Allow env var to enable partial messages (used by sandbox gateway for baku)
			const effectiveIncludePartialMessages =
				includePartialMessages ||
				isEnvTruthy(process.env.CLAUDE_CODE_INCLUDE_PARTIAL_MESSAGES);

			// Enable all hook event types when explicitly requested via SDK option
			// or when running in CLAUDE_CODE_REMOTE mode (CCR needs them).
			// Without this, only SessionStart and Setup events are emitted.
			if (
				includeHookEvents ||
				isEnvTruthy(process.env.CLAUDE_CODE_REMOTE)
			) {
				setAllHookEventsEnabled(true);
			}

			// Auto-set input/output formats, verbose mode, and print mode when SDK URL is provided
			if (sdkUrl) {
				// If SDK URL is provided, automatically use stream-json formats unless explicitly set
				if (!inputFormat) {
					inputFormat = "stream-json";
				}
				if (!outputFormat) {
					outputFormat = "stream-json";
				}
				// Auto-enable verbose mode unless explicitly disabled or already set
				if (options.verbose === undefined) {
					verbose = true;
				}
				// Auto-enable print mode unless explicitly disabled
				if (!options.print) {
					print = true;
				}
			}

			// Extract teleport option
			const teleport =
				(options as { teleport?: string | true }).teleport ?? null;

			// Extract remote option (can be true if no description provided, or a string)
			const remoteOption = (options as { remote?: string | true }).remote;
			const remote = remoteOption === true ? "" : (remoteOption ?? null);

			// Extract --remote-control / --rc flag (enable bridge in interactive session)
			const remoteControlOption =
				(options as { remoteControl?: string | true }).remoteControl ??
				(options as { rc?: string | true }).rc;
			// Actual bridge check is deferred to after showSetupScreens() so that
			// trust is established and GrowthBook has auth headers.
			let remoteControl = false;
			const remoteControlName =
				typeof remoteControlOption === "string" &&
				remoteControlOption.length > 0
					? remoteControlOption
					: undefined;

			// Validate session ID if provided
			if (sessionId) {
				// Check for conflicting flags
				// --session-id can be used with --continue or --resume when --fork-session is also provided
				// (to specify a custom ID for the forked session)
				if (
					(options.continue || options.resume) &&
					!options.forkSession
				) {
					process.stderr.write(
						chalk.red(
							"Error: --session-id can only be used with --continue or --resume if --fork-session is also specified.\n",
						),
					);
					process.exit(1);
				}

				// When --sdk-url is provided (bridge/remote mode), the session ID is a
				// server-assigned tagged ID (e.g. "session_local_01...") rather than a
				// UUID. Skip UUID validation and local existence checks in that case.
				if (!sdkUrl) {
					const validatedSessionId = validateUuid(sessionId);
					if (!validatedSessionId) {
						process.stderr.write(
							chalk.red(
								"Error: Invalid session ID. Must be a valid UUID.\n",
							),
						);
						process.exit(1);
					}

					// Check if session ID already exists
					if (sessionIdExists(validatedSessionId)) {
						process.stderr.write(
							chalk.red(
								`Error: Session ID ${validatedSessionId} is already in use.\n`,
							),
						);
						process.exit(1);
					}
				}
			}

			// Download file resources if specified via --file flag
			const fileSpecs = (options as { file?: string[] }).file;
			if (fileSpecs && fileSpecs.length > 0) {
				// Get session ingress token (provided by EnvManager via CLAUDE_CODE_SESSION_ACCESS_TOKEN)
				const sessionToken = getSessionIngressAuthToken();
				if (!sessionToken) {
					process.stderr.write(
						chalk.red(
							"Error: Session token required for file downloads. CLAUDE_CODE_SESSION_ACCESS_TOKEN must be set.\n",
						),
					);
					process.exit(1);
				}

				// Resolve session ID: prefer remote session ID, fall back to internal session ID
				const fileSessionId =
					process.env.CLAUDE_CODE_REMOTE_SESSION_ID || getSessionId();

				const files = parseFileSpecs(fileSpecs);
				if (files.length > 0) {
					// Use ANTHROPIC_BASE_URL if set (by EnvManager), otherwise use OAuth config
					// This ensures consistency with session ingress API in all environments
					const config: FilesApiConfig = {
						baseUrl:
							process.env.ANTHROPIC_BASE_URL ||
							getOauthConfig().BASE_API_URL,
						oauthToken: sessionToken,
						sessionId: fileSessionId,
					};

					// Start download without blocking startup - await before REPL renders
					fileDownloadPromise = downloadSessionFiles(files, config);
				}
			}

			// Get isNonInteractiveSession from state (was set before init())
			const isNonInteractiveSession = getIsNonInteractiveSession();

			// Validate that fallback model is different from main model
			if (
				fallbackModel &&
				options.model &&
				fallbackModel === options.model
			) {
				process.stderr.write(
					chalk.red(
						"Error: Fallback model cannot be the same as the main model. Please specify a different model for --fallback-model.\n",
					),
				);
				process.exit(1);
			}

			// Handle system prompt options
			let systemPrompt = options.systemPrompt;
			if (options.systemPromptFile) {
				if (options.systemPrompt) {
					process.stderr.write(
						chalk.red(
							"Error: Cannot use both --system-prompt and --system-prompt-file. Please use only one.\n",
						),
					);
					process.exit(1);
				}

				try {
					const filePath = resolve(options.systemPromptFile);
					systemPrompt = readFileSync(filePath, "utf8");
				} catch (error) {
					const code = getErrnoCode(error);
					if (code === "ENOENT") {
						process.stderr.write(
							chalk.red(
								`Error: System prompt file not found: ${resolve(options.systemPromptFile)}\n`,
							),
						);
						process.exit(1);
					}
					process.stderr.write(
						chalk.red(
							`Error reading system prompt file: ${errorMessage(error)}\n`,
						),
					);
					process.exit(1);
				}
			}

			// Handle append system prompt options
			let appendSystemPrompt = options.appendSystemPrompt;
			if (options.appendSystemPromptFile) {
				if (options.appendSystemPrompt) {
					process.stderr.write(
						chalk.red(
							"Error: Cannot use both --append-system-prompt and --append-system-prompt-file. Please use only one.\n",
						),
					);
					process.exit(1);
				}

				try {
					const filePath = resolve(options.appendSystemPromptFile);
					appendSystemPrompt = readFileSync(filePath, "utf8");
				} catch (error) {
					const code = getErrnoCode(error);
					if (code === "ENOENT") {
						process.stderr.write(
							chalk.red(
								`Error: Append system prompt file not found: ${resolve(options.appendSystemPromptFile)}\n`,
							),
						);
						process.exit(1);
					}
					process.stderr.write(
						chalk.red(
							`Error reading append system prompt file: ${errorMessage(error)}\n`,
						),
					);
					process.exit(1);
				}
			}

			// Add teammate-specific system prompt addendum for tmux teammates
			if (
				isAgentSwarmsEnabled() &&
				storedTeammateOpts?.agentId &&
				storedTeammateOpts?.agentName &&
				storedTeammateOpts?.teamName
			) {
				const addendum =
					getTeammatePromptAddendum().TEAMMATE_SYSTEM_PROMPT_ADDENDUM;
				appendSystemPrompt = appendSystemPrompt
					? `${appendSystemPrompt}\n\n${addendum}`
					: addendum;
			}

			const {
				mode: permissionMode,
				notification: permissionModeNotification,
			} = initialPermissionModeFromCLI({
				permissionModeCli,
				dangerouslySkipPermissions,
			});

			// Store session bypass permissions mode for trust dialog check
			setSessionBypassPermissionsMode(
				permissionMode === "bypassPermissions",
			);
			if (feature("TRANSCRIPT_CLASSIFIER")) {
				// autoModeFlagCli is the "did the user intend auto this session" signal.
				// Set when: --enable-auto-mode, --permission-mode auto, resolved mode
				// is auto, OR settings defaultMode is auto but the gate denied it
				// (permissionMode resolved to default with no explicit CLI override).
				// Used by verifyAutoModeGateAccess to decide whether to notify on
				// auto-unavailable, and by tengu_auto_mode_config opt-in carousel.
				if (
					(options as { enableAutoMode?: boolean }).enableAutoMode ||
					permissionModeCli === "auto" ||
					permissionMode === "auto" ||
					(!permissionModeCli && isDefaultPermissionModeAuto())
				) {
					autoModeStateModule?.setAutoModeFlagCli(true);
				}
			}

			// Parse the MCP config files/strings if provided
			let dynamicMcpConfig: Record<string, ScopedMcpServerConfig> = {
				// Built-in MCP servers (default disabled, user enables via /mcp)
				"mcp-chrome": {
					type: "http",
					url: "http://127.0.0.1:12306/mcp",
					scope: "dynamic",
					"headers": {
						"Authorization": "Bearer my-static-token",
					}
				},
			};

			if (mcpConfig && mcpConfig.length > 0) {
				// Process mcpConfig array
				const processedConfigs = mcpConfig
					.map((config) => config.trim())
					.filter((config) => config.length > 0);

				let allConfigs: Record<string, McpServerConfig> = {};
				const allErrors: ValidationError[] = [];

				for (const configItem of processedConfigs) {
					let configs: Record<string, McpServerConfig> | null = null;
					let errors: ValidationError[] = [];

					// First try to parse as JSON string
					const parsedJson = safeParseJSON(configItem);
					if (parsedJson) {
						const result = parseMcpConfig({
							configObject: parsedJson,
							filePath: "command line",
							expandVars: true,
							scope: "dynamic",
						});
						if (result.config) {
							configs = result.config.mcpServers;
						} else {
							errors = result.errors;
						}
					} else {
						// Try as file path
						const configPath = resolve(configItem);
						const result = parseMcpConfigFromFilePath({
							filePath: configPath,
							expandVars: true,
							scope: "dynamic",
						});
						if (result.config) {
							configs = result.config.mcpServers;
						} else {
							errors = result.errors;
						}
					}

					if (errors.length > 0) {
						allErrors.push(...errors);
					} else if (configs) {
						// Merge configs, later ones override earlier ones
						allConfigs = { ...allConfigs, ...configs };
					}
				}

				if (allErrors.length > 0) {
					const formattedErrors = allErrors
						.map(
							(err) =>
								`${err.path ? err.path + ": " : ""}${err.message}`,
						)
						.join("\n");
					logForDebugging(
						`--mcp-config validation failed (${allErrors.length} errors): ${formattedErrors}`,
						{
							level: "error",
						},
					);
					process.stderr.write(
						`Error: Invalid MCP configuration:\n${formattedErrors}\n`,
					);
					process.exit(1);
				}

				if (Object.keys(allConfigs).length > 0) {
					// SDK hosts (Nest/Desktop) own their server naming and may reuse
					// built-in names — skip reserved-name checks for type:'sdk'.
					const nonSdkConfigNames = Object.entries(allConfigs)
						.filter(([, config]) => config.type !== "sdk")
						.map(([name]) => name);

					let reservedNameError: string | null = null;
					if (nonSdkConfigNames.some(isClaudeInChromeMCPServer)) {
						reservedNameError = `Invalid MCP configuration: "${CLAUDE_IN_CHROME_MCP_SERVER_NAME}" is a reserved MCP name.`;
					} else if (feature("CHICAGO_MCP")) {
						const {
							isComputerUseMCPServer,
							COMPUTER_USE_MCP_SERVER_NAME,
						} = await import("src/utils/computerUse/common.js");
						if (nonSdkConfigNames.some(isComputerUseMCPServer)) {
							reservedNameError = `Invalid MCP configuration: "${COMPUTER_USE_MCP_SERVER_NAME}" is a reserved MCP name.`;
						}
					}
					if (reservedNameError) {
						// stderr+exit(1) — a throw here becomes a silent unhandled
						// rejection in stream-json mode (void main() in cli.tsx).
						process.stderr.write(`Error: ${reservedNameError}\n`);
						process.exit(1);
					}

					// Add dynamic scope to all configs. type:'sdk' entries pass through
					// unchanged — they're extracted into sdkMcpConfigs downstream and
					// passed to print.ts. The Python SDK relies on this path (it doesn't
					// send sdkMcpServers in the initialize message). Dropping them here
					// broke Coworker (inc-5122). The policy filter below already exempts
					// type:'sdk', and the entries are inert without an SDK transport on
					// stdin, so there's no bypass risk from letting them through.
					const scopedConfigs = mapValues(allConfigs, (config) => ({
						...config,
						scope: "dynamic" as const,
					}));

					// Enforce managed policy (allowedMcpServers / deniedMcpServers) on
					// --mcp-config servers. Without this, the CLI flag bypasses the
					// enterprise allowlist that user/project/local configs go through in
					// getClaudeCodeMcpConfigs — callers spread dynamicMcpConfig back on
					// top of filtered results. Filter here at the source so all
					// downstream consumers see the policy-filtered set.
					const { allowed, blocked } =
						filterMcpServersByPolicy(scopedConfigs);
					if (blocked.length > 0) {
						process.stderr.write(
							`Warning: MCP ${plural(blocked.length, "server")} blocked by enterprise policy: ${blocked.join(", ")}\n`,
						);
					}
					dynamicMcpConfig = { ...dynamicMcpConfig, ...(allowed as Record<string, ScopedMcpServerConfig>) };
				}
			}

			// Extract Claude in Chrome option and enforce claude.ai subscriber check (unless user is ant)
			const chromeOpts = options as { chrome?: boolean };
			// Store the explicit CLI flag so teammates can inherit it
			setChromeFlagOverride(chromeOpts.chrome);
			const enableClaudeInChrome =
				shouldEnableClaudeInChrome(chromeOpts.chrome) &&
				(process.env.USER_TYPE === "ant" || isClaudeAISubscriber());
			const autoEnableClaudeInChrome =
				!enableClaudeInChrome && shouldAutoEnableClaudeInChrome();

			if (enableClaudeInChrome) {
				const platform = getPlatform();
				try {
					logEvent("tengu_claude_in_chrome_setup", {
						platform:
							platform as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					});

					const {
						mcpConfig: chromeMcpConfig,
						allowedTools: chromeMcpTools,
						systemPrompt: chromeSystemPrompt,
					} = setupClaudeInChrome();
					dynamicMcpConfig = {
						...dynamicMcpConfig,
						...chromeMcpConfig,
					};
					allowedTools.push(...chromeMcpTools);
					if (chromeSystemPrompt) {
						appendSystemPrompt = appendSystemPrompt
							? `${chromeSystemPrompt}\n\n${appendSystemPrompt}`
							: chromeSystemPrompt;
					}
				} catch (error) {
					logEvent("tengu_claude_in_chrome_setup_failed", {
						platform:
							platform as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					});
					logForDebugging(`[Claude in Chrome] Error: ${error}`);
					logError(error);
					console.error(
						`Error: Failed to run with Claude in Chrome.`,
					);
					process.exit(1);
				}
			} else if (autoEnableClaudeInChrome) {
				try {
					const { mcpConfig: chromeMcpConfig } =
						setupClaudeInChrome();
					dynamicMcpConfig = {
						...dynamicMcpConfig,
						...chromeMcpConfig,
					};

					const hint =
						feature("WEB_BROWSER_TOOL") &&
						typeof Bun !== "undefined" &&
						"WebView" in Bun
							? CLAUDE_IN_CHROME_SKILL_HINT_WITH_WEBBROWSER
							: CLAUDE_IN_CHROME_SKILL_HINT;
					appendSystemPrompt = appendSystemPrompt
						? `${appendSystemPrompt}\n\n${hint}`
						: hint;
				} catch (error) {
					// Silently skip any errors for the auto-enable
					logForDebugging(
						`[Claude in Chrome] Error (auto-enable): ${error}`,
					);
				}
			}

			// Extract strict MCP config flag
			const strictMcpConfig = options.strictMcpConfig || false;

			// Check if enterprise MCP configuration exists. When it does, only allow dynamic MCP
			// configs that contain special server types (sdk)
			if (doesEnterpriseMcpConfigExist()) {
				if (strictMcpConfig) {
					process.stderr.write(
						chalk.red(
							"You cannot use --strict-mcp-config when an enterprise MCP config is present",
						),
					);
					process.exit(1);
				}

				// For --mcp-config, allow if all servers are internal types (sdk)
				if (
					dynamicMcpConfig &&
					!areMcpConfigsAllowedWithEnterpriseMcpConfig(
						dynamicMcpConfig,
					)
				) {
					process.stderr.write(
						chalk.red(
							"You cannot dynamically configure MCP servers when an enterprise MCP config is present",
						),
					);
					process.exit(1);
				}
			}

			// chicago MCP: guarded Computer Use (app allowlist + frontmost gate +
			// SCContentFilter screenshots). Ant-only, GrowthBook-gated — failures
			// are silent (this is dogfooding). Platform + interactive checks inline
			// so non-macOS / print-mode ants skip the heavy @ant/computer-use-mcp
			// import entirely. gates.js is light (type-only package import).
			//
			// Placed AFTER the enterprise-MCP-config check: that check rejects any
			// dynamicMcpConfig entry with `type !== 'sdk'`, and our config is
			// `type: 'stdio'`. An enterprise-config ant with the GB gate on would
			// otherwise process.exit(1). Chrome has the same latent issue but has
			// shipped without incident; chicago places itself correctly.
			if (
				feature("CHICAGO_MCP") &&
				getPlatform() !== "unknown" &&
				!getIsNonInteractiveSession()
			) {
				try {
					const { getChicagoEnabled } =
						await import("src/utils/computerUse/gates.js");
					if (getChicagoEnabled()) {
						const { setupComputerUseMCP } =
							await import("src/utils/computerUse/setup.js");
						const { mcpConfig, allowedTools: cuTools } =
							setupComputerUseMCP();
						dynamicMcpConfig = {
							...dynamicMcpConfig,
							...mcpConfig,
						};
						allowedTools.push(...cuTools);
					}
				} catch (error) {
					logForDebugging(
						`[Computer Use MCP] Setup failed: ${errorMessage(error)}`,
					);
				}
			}

			// Store additional directories for CLAUDE.md loading (controlled by env var)
			setAdditionalDirectoriesForClaudeMd(addDir);

			// Channel server allowlist from --channels flag — servers whose
			// inbound push notifications should register this session. The option
			// is added inside a feature() block so TS doesn't know about it
			// on the options type — same pattern as --assistant at main.tsx:1824.
			// devChannels is deferred: showSetupScreens shows a confirmation dialog
			// and only appends to allowedChannels on accept.
			let devChannels: ChannelEntry[] | undefined;
			// Parse plugin:name@marketplace / server:Y tags into typed entries.
			// Tag decides trust model downstream: plugin-kind hits marketplace
			// verification + GrowthBook allowlist, server-kind always fails
			// allowlist (schema is plugin-only) unless dev flag is set.
			// Untagged or marketplace-less plugin entries are hard errors —
			// silently not-matching in the gate would look like channels are
			// "on" but nothing ever fires.
			const parseChannelEntries = (
				raw: string[],
				flag: string,
			): ChannelEntry[] => {
				const entries: ChannelEntry[] = [];
				const bad: string[] = [];
				for (const c of raw) {
					if (c.startsWith("plugin:")) {
						const rest = c.slice(7);
						const at = rest.indexOf("@");
						if (at <= 0 || at === rest.length - 1) {
							bad.push(c);
						} else {
							entries.push({
								kind: "plugin",
								name: rest.slice(0, at),
								marketplace: rest.slice(at + 1),
							});
						}
					} else if (c.startsWith("server:") && c.length > 7) {
						entries.push({ kind: "server", name: c.slice(7) });
					} else {
						bad.push(c);
					}
				}
				if (bad.length > 0) {
					process.stderr.write(
						chalk.red(
							`${flag} entries must be tagged: ${bad.join(", ")}\n` +
								`  plugin:<name>@<marketplace>  — plugin-provided channel (allowlist enforced)\n` +
								`  server:<name>                — manually configured MCP server\n`,
						),
					);
					process.exit(1);
				}
				return entries;
			};

			const channelOpts = options as {
				channels?: string[];
				dangerouslyLoadDevelopmentChannels?: string[];
			};
			const rawChannels = channelOpts.channels;
			const rawDev = channelOpts.dangerouslyLoadDevelopmentChannels;
			// Always parse + set. ChannelsNotice reads getAllowedChannels() and
			// renders the appropriate branch (disabled/noAuth/policyBlocked/
			// listening) in the startup screen. gateChannelServer() enforces.
			// --channels works in both interactive and print/SDK modes; dev-channels
			// stays interactive-only (requires a confirmation dialog).
			let channelEntries: ChannelEntry[] = [];
			if (rawChannels && rawChannels.length > 0) {
				channelEntries = parseChannelEntries(
					rawChannels,
					"--channels",
				);
				setAllowedChannels(channelEntries);
			}
			if (!isNonInteractiveSession) {
				if (rawDev && rawDev.length > 0) {
					devChannels = parseChannelEntries(
						rawDev,
						"--dangerously-load-development-channels",
					);
				}
			}
			// Flag-usage telemetry. Plugin identifiers are logged (same tier as
			// tengu_plugin_installed — public-registry-style names); server-kind
			// names are not (MCP-server-name tier, opt-in-only elsewhere).
			// Per-server gate outcomes land in tengu_mcp_channel_gate once
			// servers connect. Dev entries go through a confirmation dialog after
			// this — dev_plugins captures what was typed, not what was accepted.
			if (
				channelEntries.length > 0 ||
				(devChannels?.length ?? 0) > 0
			) {
				const joinPluginIds = (entries: ChannelEntry[]) => {
					const ids = entries.flatMap((e) =>
						e.kind === "plugin"
							? [`${e.name}@${e.marketplace}`]
							: [],
					);
					return ids.length > 0
						? (ids
								.sort()
								.join(
									",",
								) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS)
						: undefined;
				};
				logEvent("tengu_mcp_channel_flags", {
					channels_count: channelEntries.length,
					dev_count: devChannels?.length ?? 0,
					plugins: joinPluginIds(channelEntries),
					dev_plugins: joinPluginIds(devChannels ?? []),
				});
			}

			// SDK opt-in for SendUserMessage via --tools. All sessions require
			// explicit opt-in; listing it in --tools signals intent. Runs BEFORE
			// initializeToolPermissionContext so getToolsForDefaultPreset() sees
			// the tool as enabled when computing the base-tools disallow filter.
			// Conditional require avoids leaking the tool-name string into
			// external builds.
			if (
				(feature("KAIROS") || feature("KAIROS_BRIEF")) &&
				baseTools.length > 0
			) {
				/* eslint-disable @typescript-eslint/no-require-imports */
				const { BRIEF_TOOL_NAME, LEGACY_BRIEF_TOOL_NAME } =
					require("@claude-code-best/builtin-tools/tools/BriefTool/prompt.js") as typeof import("@claude-code-best/builtin-tools/tools/BriefTool/prompt.js");
				const { isBriefEntitled } =
					require("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js") as typeof import("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js");
				/* eslint-enable @typescript-eslint/no-require-imports */
				const parsed = parseToolListFromCLI(baseTools);
				if (
					(parsed.includes(BRIEF_TOOL_NAME) ||
						parsed.includes(LEGACY_BRIEF_TOOL_NAME)) &&
					isBriefEntitled()
				) {
					setUserMsgOptIn(true);
				}
			}

			// This await replaces blocking existsSync/statSync calls that were already in
			// the startup path. Wall-clock time is unchanged; we just yield to the event
			// loop during the fs I/O instead of blocking it. See #19661.
			const initResult = await initializeToolPermissionContext({
				allowedToolsCli: allowedTools,
				disallowedToolsCli: disallowedTools,
				baseToolsCli: baseTools,
				permissionMode,
				allowDangerouslySkipPermissions,
				addDirs: addDir,
			});
			let toolPermissionContext = initResult.toolPermissionContext;
			const {
				warnings,
				dangerousPermissions,
				overlyBroadBashPermissions,
			} = initResult;

			// Handle overly broad shell allow rules for ant users (Bash(*), PowerShell(*))
			if (
				process.env.USER_TYPE === "ant" &&
				overlyBroadBashPermissions.length > 0
			) {
				for (const permission of overlyBroadBashPermissions) {
					logForDebugging(
						`Ignoring overly broad shell permission ${permission.ruleDisplay} from ${permission.sourceDisplay}`,
					);
				}
				toolPermissionContext = removeDangerousPermissions(
					toolPermissionContext,
					overlyBroadBashPermissions,
				);
			}

			if (
				feature("TRANSCRIPT_CLASSIFIER") &&
				dangerousPermissions.length > 0
			) {
				toolPermissionContext = stripDangerousPermissionsForAutoMode(
					toolPermissionContext,
				);
			}

			// Print any warnings from initialization
			warnings.forEach((warning) => {
				console.error(warning);
			});


			// claude.ai config fetch: -p mode only (interactive uses useManageMCPConnections
			// two-phase loading). Kicked off here to overlap with setup(); awaited
			// before runHeadless so single-turn -p sees connectors. Skipped under
			// enterprise/strict MCP to preserve policy boundaries.
			const claudeaiConfigPromise: Promise<
				Record<string, ScopedMcpServerConfig>
			> =
				isNonInteractiveSession &&
				!strictMcpConfig &&
				!doesEnterpriseMcpConfigExist() &&
				// --bare / SIMPLE: skip claude.ai proxy servers (datadog, Gmail,
				// Slack, BigQuery, PubMed — 6-14s each to connect). Scripted calls
				// that need MCP pass --mcp-config explicitly.
				!isBareMode()
					? fetchClaudeAIMcpConfigsIfEligible().then((configs) => {
							const { allowed, blocked } =
								filterMcpServersByPolicy(configs);
							if (blocked.length > 0) {
								process.stderr.write(
									`Warning: costrict.ai MCP ${plural(blocked.length, "server")} blocked by enterprise policy: ${blocked.join(", ")}\n`,
								);
							}
							return allowed;
						})
					: Promise.resolve({});

			// Kick off MCP config loading early (safe - just reads files, no execution).
			// Both interactive and -p use getClaudeCodeMcpConfigs (local file reads only).
			// The local promise is awaited later (before prefetchAllMcpResources) to
			// overlap config I/O with setup(), commands loading, and trust dialog.
			logForDebugging("[STARTUP] Loading MCP configs...");
			const mcpConfigStart = Date.now();
			let mcpConfigResolvedMs: number | undefined;
			// --bare skips auto-discovered MCP (.mcp.json, user settings, plugins) —
			// only explicit --mcp-config works. dynamicMcpConfig is spread onto
			// allMcpConfigs downstream so it survives this skip.
			const mcpConfigPromise = (
				strictMcpConfig || isBareMode()
					? Promise.resolve({
							servers: {} as Record<
								string,
								ScopedMcpServerConfig
							>,
						})
					: getClaudeCodeMcpConfigs(dynamicMcpConfig)
			).then((result) => {
				mcpConfigResolvedMs = Date.now() - mcpConfigStart;
				return result;
			});

			// NOTE: We do NOT call prefetchAllMcpResources here - that's deferred until after trust dialog

			if (
				inputFormat &&
				inputFormat !== "text" &&
				inputFormat !== "stream-json"
			) {
				console.error(`Error: Invalid input format "${inputFormat}".`);
				process.exit(1);
			}
			if (
				inputFormat === "stream-json" &&
				outputFormat !== "stream-json"
			) {
				console.error(
					`Error: --input-format=stream-json requires output-format=stream-json.`,
				);
				process.exit(1);
			}

			// Validate sdkUrl is only used with appropriate formats (formats are auto-set above)
			if (sdkUrl) {
				if (
					inputFormat !== "stream-json" ||
					outputFormat !== "stream-json"
				) {
					console.error(
						`Error: --sdk-url requires both --input-format=stream-json and --output-format=stream-json.`,
					);
					process.exit(1);
				}
			}

			// Validate replayUserMessages is only used with stream-json formats
			if (options.replayUserMessages) {
				if (
					inputFormat !== "stream-json" ||
					outputFormat !== "stream-json"
				) {
					console.error(
						`Error: --replay-user-messages requires both --input-format=stream-json and --output-format=stream-json.`,
					);
					process.exit(1);
				}
			}

			// Validate includePartialMessages is only used with print mode and stream-json output
			if (effectiveIncludePartialMessages) {
				if (
					!isNonInteractiveSession ||
					outputFormat !== "stream-json"
				) {
					writeToStderr(
						`Error: --include-partial-messages requires --print and --output-format=stream-json.`,
					);
					process.exit(1);
				}
			}

			// Validate --no-session-persistence is only used with print mode
			if (
				options.sessionPersistence === false &&
				!isNonInteractiveSession
			) {
				writeToStderr(
					`Error: --no-session-persistence can only be used with --print mode.`,
				);
				process.exit(1);
			}

			const effectivePrompt = prompt || "";
			let inputPrompt = await getInputPrompt(
				effectivePrompt,
				(inputFormat ?? "text") as "text" | "stream-json",
			);
			profileCheckpoint("action_after_input_prompt");

			// Activate proactive mode BEFORE getTools() so SleepTool.isEnabled()
			// (which returns isProactiveActive()) passes and Sleep is included.
			// The later REPL-path maybeActivateProactive() calls are idempotent.
			maybeActivateProactive(options);

			let tools = getTools(toolPermissionContext);

			// Apply coordinator mode tool filtering for headless path
			// (mirrors useMergedTools.ts filtering for REPL/interactive path)
			if (
				feature("COORDINATOR_MODE") &&
				isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
			) {
				const { applyCoordinatorToolFilter } =
					await import("./utils/toolPool.js");
				tools = applyCoordinatorToolFilter(tools);
			}

			profileCheckpoint("action_tools_loaded");

			let jsonSchema: ToolInputJSONSchema | undefined;
			if (
				isSyntheticOutputToolEnabled({ isNonInteractiveSession }) &&
				options.jsonSchema
			) {
				jsonSchema = jsonParse(
					options.jsonSchema,
				) as ToolInputJSONSchema;
			}

			if (jsonSchema) {
				const syntheticOutputResult =
					createSyntheticOutputTool(jsonSchema);
				if ("tool" in syntheticOutputResult) {
					// Add SyntheticOutputTool to the tools array AFTER getTools() filtering.
					// This tool is excluded from normal filtering (see tools.ts) because it's
					// an implementation detail for structured output, not a user-controlled tool.
					tools = [...tools, syntheticOutputResult.tool];

					logEvent("tengu_structured_output_enabled", {
						schema_property_count: Object.keys(
							(jsonSchema.properties as Record<
								string,
								unknown
							>) || {},
						)
							.length as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
						has_required_fields: Boolean(
							jsonSchema.required,
						) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					});
				} else {
					logEvent("tengu_structured_output_failure", {
						error: "Invalid JSON schema" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					});
				}
			}

			// IMPORTANT: setup() must be called before any other code that depends on the cwd or worktree setup
			profileCheckpoint("action_before_setup");
			logForDebugging("[STARTUP] Running setup()...");
			const setupStart = Date.now();
			const { setup } = await import("./setup.js");
			const messagingSocketPath = feature("UDS_INBOX")
				? (options as { messagingSocketPath?: string })
						.messagingSocketPath
				: undefined;
			// Parallelize setup() with commands+agents loading. setup()'s ~28ms is
			// mostly startUdsMessaging (socket bind, ~20ms) — not disk-bound, so it
			// doesn't contend with getCommands' file reads. Gated on !worktreeEnabled
			// since --worktree makes setup() process.chdir() (setup.ts:203), and
			// commands/agents need the post-chdir cwd.
			const preSetupCwd = getCwd();
			// Register bundled skills/plugins before kicking getCommands() — they're
			// pure in-memory array pushes (<1ms, zero I/O) that getBundledSkills()
			// reads synchronously. Previously ran inside setup() after ~20ms of
			// await points, so the parallel getCommands() memoized an empty list.
			if (process.env.CLAUDE_CODE_ENTRYPOINT !== "local-agent") {
				initBuiltinPlugins();
				initBundledSkills();
			}
			const setupPromise = setup(
				preSetupCwd,
				permissionMode,
				allowDangerouslySkipPermissions,
				worktreeEnabled,
				worktreeName,
				tmuxEnabled,
				sessionId ? validateUuid(sessionId) : undefined,
				worktreePRNumber,
				messagingSocketPath,
			);
			const commandsPromise = worktreeEnabled
				? null
				: getCommands(preSetupCwd);
			const agentDefsPromise = worktreeEnabled
				? null
				: getAgentDefinitionsWithOverrides(preSetupCwd);
			// Suppress transient unhandledRejection if these reject during the
			// ~28ms setupPromise await before Promise.all joins them below.
			commandsPromise?.catch(() => {});
			agentDefsPromise?.catch(() => {});
			await setupPromise;
			logForDebugging(
				`[STARTUP] setup() completed in ${Date.now() - setupStart}ms`,
			);
			profileCheckpoint("action_after_setup");

			// Replay user messages into stream-json only when the socket was
			// explicitly requested. The auto-generated socket is passive — it
			// lets tools inject if they want to, but turning it on by default
			// shouldn't reshape stream-json for SDK consumers who never touch it.
			// Callers who inject and also want those injections visible in the
			// stream pass --messaging-socket-path explicitly (or --replay-user-messages).
			let effectiveReplayUserMessages = !!options.replayUserMessages;
			if (feature("UDS_INBOX")) {
				if (
					!effectiveReplayUserMessages &&
					outputFormat === "stream-json"
				) {
					effectiveReplayUserMessages = !!(
						options as { messagingSocketPath?: string }
					).messagingSocketPath;
				}
			}

			if (getIsNonInteractiveSession()) {
				// Apply full merged settings env now (including project-scoped
				// .claude/settings.json PATH/GIT_DIR/GIT_WORK_TREE) so gitExe() and
				// the git spawn below see it. Trust is implicit in -p mode; the
				// docstring at managedEnv.ts:96-97 says this applies "potentially
				// dangerous environment variables such as LD_PRELOAD, PATH" from all
				// sources. The later call in the isNonInteractiveSession block below
				// is idempotent (Object.assign, configureGlobalAgents ejects prior
				// interceptor) and picks up any plugin-contributed env after plugin
				// init. Project settings are already loaded here:
				// applySafeConfigEnvironmentVariables in init() called
				// getSettings_DEPRECATED at managedEnv.ts:86 which merges all enabled
				// sources including projectSettings/localSettings.
				applyConfigEnvironmentVariables();

				// Spawn git status/log/branch now so the subprocess execution overlaps
				// with the getCommands await below and startDeferredPrefetches. After
				// setup() so cwd is final (setup.ts:254 may process.chdir(worktreePath)
				// for --worktree) and after the applyConfigEnvironmentVariables above
				// so PATH/GIT_DIR/GIT_WORK_TREE from all sources (trusted + project)
				// are applied. getSystemContext is memoized; the
				// prefetchSystemContextIfSafe call in startDeferredPrefetches becomes
				// a cache hit. The microtask from await getIsGit() drains at the
				// getCommands Promise.all await below. Trust is implicit in -p mode
				// (same gate as prefetchSystemContextIfSafe).
				void getSystemContext();
				// Kick getUserContext now too — its first await (fs.readFile in
				// getMemoryFiles) yields naturally, so the CLAUDE.md directory walk
				// runs during the ~280ms overlap window before the context
				// Promise.all join in print.ts. The void getUserContext() in
				// startDeferredPrefetches becomes a memoize cache-hit.
				void getUserContext();
				// Kick ensureModelStringsInitialized now — for Bedrock this triggers
				// a 100-200ms profile fetch that was awaited serially at
				// print.ts:739. updateBedrockModelStrings is sequential()-wrapped so
				// the await joins the in-flight fetch. Non-Bedrock is a sync
				// early-return (zero-cost).
				void ensureModelStringsInitialized();
			}

			// Apply --name: cache-only so no orphan file is created before the
			// session ID is finalized by --continue/--resume. materializeSessionFile
			// persists it on the first user message; REPL's useTerminalTitle reads it
			// via getCurrentSessionTitle.
			const sessionNameArg = options.name?.trim();
			if (sessionNameArg) {
				cacheSessionTitle(sessionNameArg);
			}

			// Ant model aliases (capybara-fast etc.) resolve via the
			// tengu_ant_model_override GrowthBook flag. _CACHED_MAY_BE_STALE reads
			// disk synchronously; disk is populated by a fire-and-forget write. On a
			// cold cache, parseUserSpecifiedModel returns the unresolved alias, the
			// API 404s, and -p exits before the async write lands — crashloop on
			// fresh pods. Awaiting init here populates the in-memory payload map that
			// _CACHED_MAY_BE_STALE now checks first. Gated so the warm path stays
			// non-blocking:
			//  - explicit model via --model or ANTHROPIC_MODEL (both feed alias resolution)
			//  - no env override (which short-circuits _CACHED_MAY_BE_STALE before disk)
			//  - flag absent from disk (== null also catches pre-#22279 poisoned null)
			const explicitModel = options.model || process.env.ANTHROPIC_MODEL;
			if (
				process.env.USER_TYPE === "ant" &&
				explicitModel &&
				explicitModel !== "default" &&
				!hasGrowthBookEnvOverride("tengu_ant_model_override") &&
				getGlobalConfig().cachedGrowthBookFeatures?.[
					"tengu_ant_model_override"
				] == null
			) {
				await initializeGrowthBook();
			}

			// Special case the default model with the null keyword
			// NOTE: Model resolution happens after setup() to ensure trust is established before AWS auth
			const userSpecifiedModel =
				options.model === "default"
					? getDefaultMainLoopModel()
					: options.model;
			const userSpecifiedFallbackModel =
				fallbackModel === "default"
					? getDefaultMainLoopModel()
					: fallbackModel;

			// Reuse preSetupCwd unless setup() chdir'd (worktreeEnabled). Saves a
			// getCwd() syscall in the common path.
			const currentCwd = worktreeEnabled ? getCwd() : preSetupCwd;
			logForDebugging("[STARTUP] Loading commands and agents...");
			const commandsStart = Date.now();
			// Join the promises kicked before setup() (or start fresh if
			// worktreeEnabled gated the early kick). Both memoized by cwd.
			const [commands, agentDefinitionsResult] = await Promise.all([
				commandsPromise ?? getCommands(currentCwd),
				agentDefsPromise ??
					getAgentDefinitionsWithOverrides(currentCwd),
			]);
			logForDebugging(
				`[STARTUP] Commands and agents loaded in ${Date.now() - commandsStart}ms`,
			);
			profileCheckpoint("action_commands_loaded");

			// Parse CLI agents if provided via --agents flag
			let cliAgents: typeof agentDefinitionsResult.activeAgents = [];
			if (agentsJson) {
				try {
					const parsedAgents = safeParseJSON(agentsJson);
					if (parsedAgents) {
						cliAgents = parseAgentsFromJson(
							parsedAgents,
							"flagSettings",
						);
					}
				} catch (error) {
					logError(error);
				}
			}

			// Merge CLI agents with existing ones
			const allAgents = [
				...agentDefinitionsResult.allAgents,
				...cliAgents,
			];
			const agentDefinitions = {
				...agentDefinitionsResult,
				allAgents,
				activeAgents: getActiveAgentsFromList(allAgents),
			};

			// Look up main thread agent from CLI flag or settings
			const agentSetting = agentCli ?? getInitialSettings().agent;
			let mainThreadAgentDefinition:
				| (typeof agentDefinitions.activeAgents)[number]
				| undefined;
			if (agentSetting) {
				mainThreadAgentDefinition = agentDefinitions.activeAgents.find(
					(agent) => agent.agentType === agentSetting,
				);
				if (!mainThreadAgentDefinition) {
					logForDebugging(
						`Warning: agent "${agentSetting}" not found. ` +
							`Available agents: ${agentDefinitions.activeAgents.map((a) => a.agentType).join(", ")}. ` +
							`Using default behavior.`,
					);
				}
			}

			// Store the main thread agent type in bootstrap state so hooks can access it
			setMainThreadAgentType(mainThreadAgentDefinition?.agentType);

			// Log agent flag usage — only log agent name for built-in agents to avoid leaking custom agent names
			if (mainThreadAgentDefinition) {
				logEvent("tengu_agent_flag", {
					agentType: isBuiltInAgent(mainThreadAgentDefinition)
						? (mainThreadAgentDefinition.agentType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS)
						: ("custom" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS),
					...(agentCli && {
						source: "cli" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					}),
				});
			}

			// Persist agent setting to session transcript for resume view display and restoration
			if (mainThreadAgentDefinition?.agentType) {
				saveAgentSetting(mainThreadAgentDefinition.agentType);
			}

			// Apply the agent's system prompt for non-interactive sessions
			// (interactive mode uses buildEffectiveSystemPrompt instead)
			if (
				isNonInteractiveSession &&
				mainThreadAgentDefinition &&
				!systemPrompt &&
				!isBuiltInAgent(mainThreadAgentDefinition)
			) {
				const agentSystemPrompt =
					mainThreadAgentDefinition.getSystemPrompt();
				if (agentSystemPrompt) {
					systemPrompt = agentSystemPrompt;
				}
			}

			// initialPrompt goes first so its slash command (if any) is processed;
			// user-provided text becomes trailing context.
			// Only concatenate when inputPrompt is a string. When it's an
			// AsyncIterable (SDK stream-json mode), template interpolation would
			// call .toString() producing "[object Object]". The AsyncIterable case
			// is handled in print.ts via structuredIO.prependUserMessage().
			if (mainThreadAgentDefinition?.initialPrompt) {
				if (typeof inputPrompt === "string") {
					inputPrompt = inputPrompt
						? `${mainThreadAgentDefinition.initialPrompt}\n\n${inputPrompt}`
						: mainThreadAgentDefinition.initialPrompt;
				} else if (!inputPrompt) {
					inputPrompt = mainThreadAgentDefinition.initialPrompt;
				}
			}

			// Compute effective model early so hooks can run in parallel with MCP
			// If user didn't specify a model but agent has one, use the agent's model
			let effectiveModel = userSpecifiedModel;
			if (
				!effectiveModel &&
				mainThreadAgentDefinition?.model &&
				mainThreadAgentDefinition.model !== "inherit"
			) {
				effectiveModel = parseUserSpecifiedModel(
					mainThreadAgentDefinition.model,
				);
			}

			setMainLoopModelOverride(effectiveModel);

			// Compute resolved model for hooks (use user-specified model at launch)
			setInitialMainLoopModel(getUserSpecifiedModelSetting() || null);
			const initialMainLoopModel = getInitialMainLoopModel();
			const resolvedInitialModel = parseUserSpecifiedModel(
				initialMainLoopModel ?? getDefaultMainLoopModel(),
			);

			let advisorModel: string | undefined;
			if (isAdvisorEnabled()) {
				const advisorOption = canUserConfigureAdvisor()
					? (options as { advisor?: string }).advisor
					: undefined;
				if (advisorOption) {
					logForDebugging(`[AdvisorTool] --advisor ${advisorOption}`);
					if (!modelSupportsAdvisor(resolvedInitialModel)) {
						process.stderr.write(
							chalk.red(
								`Error: The model "${resolvedInitialModel}" does not support the advisor tool.\n`,
							),
						);
						process.exit(1);
					}
					const normalizedAdvisorModel = normalizeModelStringForAPI(
						parseUserSpecifiedModel(advisorOption),
					);
					if (!isValidAdvisorModel(normalizedAdvisorModel)) {
						process.stderr.write(
							chalk.red(
								`Error: The model "${advisorOption}" cannot be used as an advisor.\n`,
							),
						);
						process.exit(1);
					}
				}
				advisorModel = canUserConfigureAdvisor()
					? (advisorOption ?? getInitialAdvisorSetting())
					: advisorOption;
				if (advisorModel) {
					logForDebugging(
						`[AdvisorTool] Advisor model: ${advisorModel}`,
					);
				}
			}

			// For tmux teammates with --agent-type, append the custom agent's prompt
			if (
				isAgentSwarmsEnabled() &&
				storedTeammateOpts?.agentId &&
				storedTeammateOpts?.agentName &&
				storedTeammateOpts?.teamName &&
				storedTeammateOpts?.agentType
			) {
				// Look up the custom agent definition
				const customAgent = agentDefinitions.activeAgents.find(
					(a) => a.agentType === storedTeammateOpts.agentType,
				);
				if (customAgent) {
					// Get the prompt - need to handle both built-in and custom agents
					let customPrompt: string | undefined;
					if (customAgent.source === "built-in") {
						// Built-in agents have getSystemPrompt that takes toolUseContext
						// We can't access full toolUseContext here, so skip for now
						logForDebugging(
							`[teammate] Built-in agent ${storedTeammateOpts.agentType} - skipping custom prompt (not supported)`,
						);
					} else {
						// Custom agents have getSystemPrompt that takes no args
						customPrompt = customAgent.getSystemPrompt();
					}

					// Log agent memory loaded event for tmux teammates
					if (customAgent.memory) {
						logEvent("tengu_agent_memory_loaded", {
							...(process.env.USER_TYPE === "ant" && {
								agent_type:
									customAgent.agentType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
							}),
							scope: customAgent.memory as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
							source: "teammate" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
						});
					}

					if (customPrompt) {
						const customInstructions = `\n# Custom Agent Instructions\n${customPrompt}`;
						appendSystemPrompt = appendSystemPrompt
							? `${appendSystemPrompt}\n\n${customInstructions}`
							: customInstructions;
					}
				} else {
					logForDebugging(
						`[teammate] Custom agent ${storedTeammateOpts.agentType} not found in available agents`,
					);
				}
			}

			maybeActivateBrief(options);
			// defaultView: 'chat' is a persisted opt-in — check entitlement and set
			// userMsgOptIn so the tool + prompt section activate. Interactive-only:
			// defaultView is a display preference; SDK sessions have no display, and
			// the assistant installer writes defaultView:'chat' to settings.local.json
			// which would otherwise leak into --print sessions in the same directory.
			// Runs right after maybeActivateBrief() so all startup opt-in paths fire
			// BEFORE any isBriefEnabled() read below (proactive prompt's
			// briefVisibility). A persisted 'chat' after a GB kill-switch falls
			// through (entitlement fails).
			if (
				(feature("KAIROS") || feature("KAIROS_BRIEF")) &&
				!getIsNonInteractiveSession() &&
				!getUserMsgOptIn() &&
				getInitialSettings().defaultView === "chat"
			) {
				/* eslint-disable @typescript-eslint/no-require-imports */
				const { isBriefEntitled } =
					require("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js") as typeof import("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js");
				/* eslint-enable @typescript-eslint/no-require-imports */
				if (isBriefEntitled()) {
					setUserMsgOptIn(true);
				}
			}
			// Coordinator mode has its own system prompt and filters out Sleep, so
			// the generic proactive prompt would tell it to call a tool it can't
			// access and conflict with delegation instructions.
			if (
				(feature("PROACTIVE") || feature("KAIROS")) &&
				((options as { proactive?: boolean }).proactive ||
					isEnvTruthy(process.env.CLAUDE_CODE_PROACTIVE)) &&
				!coordinatorModeModule?.isCoordinatorMode()
			) {
				/* eslint-disable @typescript-eslint/no-require-imports */
				const briefVisibility =
					feature("KAIROS") || feature("KAIROS_BRIEF")
						? (
								require("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js") as typeof import("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js")
							).isBriefEnabled()
							? "Call SendUserMessage at checkpoints to mark where things stand."
							: "The user will see any text you output."
						: "The user will see any text you output.";
				/* eslint-enable @typescript-eslint/no-require-imports */
				const proactivePrompt = `\n# Proactive Mode\n\nYou are in proactive mode. Take initiative — explore, act, and make progress without waiting for instructions.\n\nStart by briefly greeting the user.\n\nYou will receive periodic <tick> prompts. These are check-ins. Do whatever seems most useful, or call Sleep if there's nothing to do. ${briefVisibility}`;
				appendSystemPrompt = appendSystemPrompt
					? `${appendSystemPrompt}\n\n${proactivePrompt}`
					: proactivePrompt;
			}

			if (feature("KAIROS") && kairosEnabled && assistantModule) {
				const assistantAddendum =
					assistantModule.getAssistantSystemPromptAddendum();
				appendSystemPrompt = appendSystemPrompt
					? `${appendSystemPrompt}\n\n${assistantAddendum}`
					: assistantAddendum;
			}

			// Ink root is only needed for interactive sessions — patchConsole in the
			// Ink constructor would swallow console output in headless mode.
			let root!: Root;
			let getFpsMetrics!: () => FpsMetrics | undefined;
			let stats!: StatsStore;

			// Show setup screens after commands are loaded
			if (!isNonInteractiveSession) {
				const ctx = getRenderContext(false);
				getFpsMetrics = ctx.getFpsMetrics;
				stats = ctx.stats;
				// Install asciicast recorder before Ink mounts (ant-only, opt-in via CLAUDE_CODE_TERMINAL_RECORDING=1)
				if (process.env.USER_TYPE === "ant") {
					installAsciicastRecorder();
				}

				const { createRoot } = await import('@anthropic/ink')
				root = await createRoot(ctx.renderOptions)

				// Log startup time now, before any blocking dialog renders. Logging
				// from REPL's first render (the old location) included however long
				// the user sat on trust/OAuth/onboarding/resume-picker — p99 was ~70s
				// dominated by dialog-wait time, not code-path startup.
				logEvent("tengu_timer", {
					event: "startup" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					durationMs: Math.round(process.uptime() * 1000),
				});

				logForDebugging("[STARTUP] Running showSetupScreens()...");
				const setupScreensStart = Date.now();
				const onboardingShown = await showSetupScreens(
					root,
					permissionMode,
					allowDangerouslySkipPermissions,
					commands,
					enableClaudeInChrome,
					devChannels,
				);
				logForDebugging(
					`[STARTUP] showSetupScreens() completed in ${Date.now() - setupScreensStart}ms`,
				);

				// Now that trust is established and GrowthBook has auth headers,
				// resolve the --remote-control / --rc entitlement gate.
				if (
					feature("BRIDGE_MODE") &&
					remoteControlOption !== undefined
				) {
					const { getBridgeDisabledReason } =
						await import("./bridge/bridgeEnabled.js");
					const disabledReason = await getBridgeDisabledReason();
					remoteControl = disabledReason === null;
					if (disabledReason) {
						process.stderr.write(
							chalk.yellow(
								`${disabledReason}\n--rc flag ignored.\n`,
							),
						);
					}
				}

				// Check for pending agent memory snapshot updates (only for --agent mode, ant-only)
				if (
					feature("AGENT_MEMORY_SNAPSHOT") &&
					mainThreadAgentDefinition &&
					isCustomAgent(mainThreadAgentDefinition) &&
					mainThreadAgentDefinition.memory &&
					mainThreadAgentDefinition.pendingSnapshotUpdate
				) {
					const agentDef = mainThreadAgentDefinition;
					const choice = await launchSnapshotUpdateDialog(root, {
						agentType: agentDef.agentType,
						scope: agentDef.memory!,
						snapshotTimestamp:
							agentDef.pendingSnapshotUpdate!.snapshotTimestamp,
					});
					if (choice === "merge") {
						const { buildMergePrompt } =
							await import("./components/agents/SnapshotUpdateDialog.js");
						const mergePrompt = buildMergePrompt(
							agentDef.agentType,
							agentDef.memory!,
						);
						inputPrompt = inputPrompt
							? `${mergePrompt}\n\n${inputPrompt}`
							: mergePrompt;
					}
					agentDef.pendingSnapshotUpdate = undefined;
				}

				// Skip executing /login if we just completed onboarding for it
				if (
					onboardingShown &&
					prompt?.trim().toLowerCase() === "/login"
				) {
					prompt = "";
				}

				if (onboardingShown) {
					// Refresh auth-dependent services now that the user has logged in during onboarding.
					// Keep in sync with the post-login logic in src/commands/login.tsx
					// void refreshRemoteManagedSettings();
					void refreshPolicyLimits();
					// Clear user data cache BEFORE GrowthBook refresh so it picks up fresh credentials
					resetUserCache();
					// Refresh GrowthBook after login to get updated feature flags (e.g., for claude.ai MCPs)
					refreshGrowthBookAfterAuthChange();
					// Clear any stale trusted device token then enroll for Remote Control.
					// Both self-gate on tengu_sessions_elevated_auth_enforcement internally
					// — enrollTrustedDevice() via checkGate_CACHED_OR_BLOCKING (awaits
					// the GrowthBook reinit above), clearTrustedDeviceToken() via the
					// sync cached check (acceptable since clear is idempotent).
					void import("./bridge/trustedDevice.js").then((m) => {
						m.clearTrustedDeviceToken();
						return m.enrollTrustedDevice();
					});
				}

				// Validate that the active token's org matches forceLoginOrgUUID (if set
				// in managed settings). Runs after onboarding so managed settings and
				// login state are fully loaded.
				const orgValidation = await validateForceLoginOrg();
				if (!orgValidation.valid) {
					await exitWithError(root, (orgValidation as { valid: false; message: string }).message);
				}
			}

			// If gracefulShutdown was initiated (e.g., user rejected trust dialog),
			// process.exitCode will be set. Skip all subsequent operations that could
			// trigger code execution before the process exits (e.g. we don't want apiKeyHelper
			// to run if trust was not established).
			if (process.exitCode !== undefined) {
				logForDebugging(
					"Graceful shutdown initiated, skipping further initialization",
				);
				return;
			}

			// Initialize LSP manager AFTER trust is established (or in non-interactive mode
			// where trust is implicit). This prevents plugin LSP servers from executing
			// code in untrusted directories before user consent.
			// Must be after inline plugins are set (if any) so --plugin-dir LSP servers are included.
			initializeLspServerManager();

			// Show settings validation errors after trust is established
			// MCP config errors don't block settings from loading, so exclude them
			if (!isNonInteractiveSession) {
				const { errors } = getSettingsWithErrors();
				const nonMcpErrors = errors.filter((e) => !e.mcpErrorMetadata);
				if (nonMcpErrors.length > 0) {
					await launchInvalidSettingsDialog(root, {
						settingsErrors: nonMcpErrors,
						onExit: () => gracefulShutdownSync(1),
					});
				}
			}

			// Check quota status, fast mode, passes eligibility, and bootstrap data
			// after trust is established. These make API calls which could trigger
			// apiKeyHelper execution.
			// --bare / SIMPLE: skip — these are cache-warms for the REPL's
			// first-turn responsiveness (quota, passes, fastMode, bootstrap data). Fast
			// mode doesn't apply to the Agent SDK anyway (see getFastModeUnavailableReason).
			const bgRefreshThrottleMs = getFeatureValue_CACHED_MAY_BE_STALE(
				"tengu_cicada_nap_ms",
				0,
			);
			const lastPrefetched = getGlobalConfig().startupPrefetchedAt ?? 0;
			const skipStartupPrefetches =
				isBareMode() ||
				(bgRefreshThrottleMs > 0 &&
					Date.now() - lastPrefetched < bgRefreshThrottleMs);

			if (!skipStartupPrefetches) {
				const lastPrefetchedInfo =
					lastPrefetched > 0
						? ` last ran ${Math.round((Date.now() - lastPrefetched) / 1000)}s ago`
						: "";
				logForDebugging(
					`Starting background startup prefetches${lastPrefetchedInfo}`,
				);

				checkQuotaStatus().catch((error) => logError(error));

				// Fetch bootstrap data from the server and update all cache values.
				void fetchBootstrapData();

				// TODO: Consolidate other prefetches into a single bootstrap request.
				void prefetchPassesEligibility();
				if (
					!getFeatureValue_CACHED_MAY_BE_STALE(
						"tengu_miraculo_the_bard",
						false,
					)
				) {
					void prefetchFastModeStatus();
				} else {
					// Kill switch skips the network call, not org-policy enforcement.
					// Resolve from cache so orgStatus doesn't stay 'pending' (which
					// getFastModeUnavailableReason treats as permissive).
					resolveFastModeStatusFromCache();
				}
				if (bgRefreshThrottleMs > 0) {
					saveGlobalConfig((current) => ({
						...current,
						startupPrefetchedAt: Date.now(),
					}));
				}
			} else {
				logForDebugging(
					`Skipping startup prefetches, last ran ${Math.round((Date.now() - lastPrefetched) / 1000)}s ago`,
				);
				// Resolve fast mode org status from cache (no network)
				resolveFastModeStatusFromCache();
			}

			if (!isNonInteractiveSession) {
				void refreshExampleCommands(); // Pre-fetch example commands (runs git log, no API call)
			}

			// Resolve MCP configs (started early, overlaps with setup/trust dialog work)
			const { servers: existingMcpConfigs } = await mcpConfigPromise;
			logForDebugging(
				`[STARTUP] MCP configs resolved in ${mcpConfigResolvedMs}ms (awaited at +${Date.now() - mcpConfigStart}ms)`,
			);
			// CLI flag (--mcp-config) should override file-based configs, matching settings precedence
			const allMcpConfigs = {
				...existingMcpConfigs,
				...dynamicMcpConfig,
			};

			// Separate SDK configs from regular MCP configs
			const sdkMcpConfigs: Record<string, McpSdkServerConfig> = {};
			const regularMcpConfigs: Record<string, ScopedMcpServerConfig> = {};

			for (const [name, config] of Object.entries(allMcpConfigs)) {
				const typedConfig = config as
					| ScopedMcpServerConfig
					| McpSdkServerConfig;
				if (typedConfig.type === "sdk") {
					sdkMcpConfigs[name] = typedConfig as McpSdkServerConfig;
				} else {
					regularMcpConfigs[name] =
						typedConfig as ScopedMcpServerConfig;
				}
			}

			profileCheckpoint("action_mcp_configs_loaded");

			// Prefetch MCP resources after trust dialog (this is where execution happens).
			// Interactive mode only: print mode defers connects until headlessStore exists
			// and pushes per-server (below), so ToolSearch's pending-client handling works
			// and one slow server doesn't block the batch.
			const localMcpPromise = isNonInteractiveSession
				? Promise.resolve({ clients: [], tools: [], commands: [] })
				: prefetchAllMcpResources(regularMcpConfigs);
			const claudeaiMcpPromise = isNonInteractiveSession
				? Promise.resolve({ clients: [], tools: [], commands: [] })
				: claudeaiConfigPromise.then((configs) =>
						Object.keys(configs).length > 0
							? prefetchAllMcpResources(configs)
							: { clients: [], tools: [], commands: [] },
					);
			// Merge with dedup by name: each prefetchAllMcpResources call independently
			// adds helper tools (ListMcpResourcesTool, ReadMcpResourceTool) via
			// local dedup flags, so merging two calls can yield duplicates. print.ts
			// already uniqBy's the final tool pool, but dedup here keeps appState clean.
			const mcpPromise = Promise.all([
				localMcpPromise,
				claudeaiMcpPromise,
			]).then(([local, claudeai]) => ({
				clients: [...local.clients, ...claudeai.clients],
				tools: uniqBy([...local.tools, ...claudeai.tools], "name"),
				commands: uniqBy(
					[...local.commands, ...claudeai.commands],
					"name",
				),
			}));

			// Start hooks early so they run in parallel with MCP connections.
			// Skip for initOnly/init/maintenance (handled separately), non-interactive
			// (handled via setupTrigger), and resume/continue (conversationRecovery.ts
			// fires 'resume' instead — without this guard, hooks fire TWICE on /resume
			// and the second systemMessage clobbers the first. gh-30825)
			const hooksPromise =
				initOnly ||
				init ||
				maintenance ||
				isNonInteractiveSession ||
				options.continue ||
				options.resume
					? null
					: processSessionStartHooks("startup", {
							agentType: mainThreadAgentDefinition?.agentType,
							model: resolvedInitialModel,
						});

			// MCP never blocks REPL render OR turn 1 TTFT. useManageMCPConnections
			// populates appState.mcp async as servers connect (connectToServer is
			// memoized — the prefetch calls above and the hook converge on the same
			// connections). getToolUseContext reads store.getState() fresh via
			// computeTools(), so turn 1 sees whatever's connected by query time.
			// Slow servers populate for turn 2+. Matches interactive-no-prompt
			// behavior. Print mode: per-server push into headlessStore (below).
			const hookMessages: Awaited<NonNullable<typeof hooksPromise>> = [];
			// Suppress transient unhandledRejection — the prefetch warms the
			// memoized connectToServer cache but nobody awaits it in interactive.
			mcpPromise.catch(() => {});

			const mcpClients: Awaited<typeof mcpPromise>["clients"] = [];
			const mcpTools: Awaited<typeof mcpPromise>["tools"] = [];
			const mcpCommands: Awaited<typeof mcpPromise>["commands"] = [];

			let thinkingEnabled = shouldEnableThinkingByDefault();
			let thinkingConfig: ThinkingConfig =
				thinkingEnabled !== false
					? { type: "adaptive" }
					: { type: "disabled" };

			if (
				options.thinking === "adaptive" ||
				options.thinking === "enabled"
			) {
				thinkingEnabled = true;
				thinkingConfig = { type: "adaptive" };
			} else if (options.thinking === "disabled") {
				thinkingEnabled = false;
				thinkingConfig = { type: "disabled" };
			} else {
				const maxThinkingTokens = process.env.MAX_THINKING_TOKENS
					? parseInt(process.env.MAX_THINKING_TOKENS, 10)
					: options.maxThinkingTokens;
				if (maxThinkingTokens !== undefined) {
					if (maxThinkingTokens > 0) {
						thinkingEnabled = true;
						thinkingConfig = {
							type: "enabled",
							budgetTokens: maxThinkingTokens,
						};
					} else if (maxThinkingTokens === 0) {
						thinkingEnabled = false;
						thinkingConfig = { type: "disabled" };
					}
				}
			}

			logForDiagnosticsNoPII("info", "started", {
				version: MACRO.VERSION,
				is_native_binary: isInBundledMode(),
			});

			registerCleanup(async () => {
				logForDiagnosticsNoPII("info", "exited");
			});

			void logTenguInit({
				hasInitialPrompt: Boolean(prompt),
				hasStdin: Boolean(inputPrompt),
				verbose,
				debug,
				debugToStderr,
				print: print ?? false,
				outputFormat: outputFormat ?? "text",
				inputFormat: inputFormat ?? "text",
				numAllowedTools: allowedTools.length,
				numDisallowedTools: disallowedTools.length,
				mcpClientCount: Object.keys(allMcpConfigs).length,
				worktreeEnabled,
				skipWebFetchPreflight:
					getInitialSettings().skipWebFetchPreflight,
				githubActionInputs: process.env.GITHUB_ACTION_INPUTS,
				dangerouslySkipPermissionsPassed:
					dangerouslySkipPermissions ?? false,
				permissionMode,
				modeIsBypass: permissionMode === "bypassPermissions",
				allowDangerouslySkipPermissionsPassed:
					allowDangerouslySkipPermissions,
				systemPromptFlag: systemPrompt
					? options.systemPromptFile
						? "file"
						: "flag"
					: undefined,
				appendSystemPromptFlag: appendSystemPrompt
					? options.appendSystemPromptFile
						? "file"
						: "flag"
					: undefined,
				thinkingConfig,
				assistantActivationPath:
					feature("KAIROS") && kairosEnabled
						? assistantModule?.getAssistantActivationPath()
						: undefined,
			});

			// Log context metrics once at initialization
			void logContextMetrics(regularMcpConfigs, toolPermissionContext);

			void logPermissionContextForAnts(null, "initialization");

			logManagedSettings();

			// Register PID file for concurrent-session detection (~/.claude/sessions/)
			// and fire multi-clauding telemetry. Lives here (not init.ts) so only the
			// REPL path registers — not subcommands like `claude doctor`. Chained:
			// count must run after register's write completes or it misses our own file.
			void registerSession().then((registered) => {
				if (!registered) return;
				if (sessionNameArg) {
					void updateSessionName(sessionNameArg);
				}
				void countConcurrentSessions().then((count) => {
					if (count >= 2) {
						logEvent("tengu_concurrent_sessions", {
							num_sessions: count,
						});
					}
				});
			});

			// Initialize versioned plugins system (triggers V1→V2 migration if
			// needed). Then run orphan GC, THEN warm the Grep/Glob exclusion cache.
			// Sequencing matters: the warmup scans disk for .orphaned_at markers,
			// so it must see the GC's Pass 1 (remove markers from reinstalled
			// versions) and Pass 2 (stamp unmarked orphans) already applied. The
			// warm also lands before autoupdate (fires on first submit in REPL)
			// can orphan this session's active version underneath us.
			// --bare / SIMPLE: skip plugin version sync + orphan cleanup. These
			// are install/upgrade bookkeeping that scripted calls don't need —
			// the next interactive session will reconcile. The await here was
			// blocking -p on a marketplace round-trip.
			if (isBareMode()) {
				// skip — no-op
			} else if (isNonInteractiveSession) {
				// In headless mode, await to ensure plugin sync completes before CLI exits
				await initializeVersionedPlugins();
				profileCheckpoint("action_after_plugins_init");
				void cleanupOrphanedPluginVersionsInBackground().then(() =>
					getGlobExclusionsForPluginCache(),
				);
			} else {
				// In interactive mode, fire-and-forget — this is purely bookkeeping
				// that doesn't affect runtime behavior of the current session
				void initializeVersionedPlugins().then(async () => {
					profileCheckpoint("action_after_plugins_init");
					await cleanupOrphanedPluginVersionsInBackground();
					void getGlobExclusionsForPluginCache();
				});
			}

			const setupTrigger =
				initOnly || init ? "init" : maintenance ? "maintenance" : null;
			if (initOnly) {
				applyConfigEnvironmentVariables();
				await processSetupHooks("init", { forceSyncExecution: true });
				await processSessionStartHooks("startup", {
					forceSyncExecution: true,
				});
				gracefulShutdownSync(0);
				return;
			}

			// --print mode
			if (isNonInteractiveSession) {
				if (outputFormat === "stream-json" || outputFormat === "json") {
					setHasFormattedOutput(true);
				}

				// Apply full environment variables in print mode since trust dialog is bypassed
				// This includes potentially dangerous environment variables from untrusted sources
				// but print mode is considered trusted (as documented in help text)
				applyConfigEnvironmentVariables();

				// Initialize telemetry after env vars are applied so OTEL endpoint env vars and
				// otelHeadersHelper (which requires trust to execute) are available.
				initializeTelemetryAfterTrust();

				// Kick SessionStart hooks now so the subprocess spawn overlaps with
				// MCP connect + plugin init + print.ts import below. loadInitialMessages
				// joins this at print.ts:4397. Guarded same as loadInitialMessages —
				// continue/resume/teleport paths don't fire startup hooks (or fire them
				// conditionally inside the resume branch, where this promise is
				// undefined and the ?? fallback runs). Also skip when setupTrigger is
				// set — those paths run setup hooks first (print.ts:544), and session
				// start hooks must wait until setup completes.
				const sessionStartHooksPromise =
					options.continue ||
					options.resume ||
					teleport ||
					setupTrigger
						? undefined
						: processSessionStartHooks("startup");
				// Suppress transient unhandledRejection if this rejects before
				// loadInitialMessages awaits it. Downstream await still observes the
				// rejection — this just prevents the spurious global handler fire.
				sessionStartHooksPromise?.catch(() => {});

				profileCheckpoint("before_validateForceLoginOrg");
				// Validate org restriction for non-interactive sessions
				const orgValidation = await validateForceLoginOrg();
				if (!orgValidation.valid) {
					process.stderr.write((orgValidation as { valid: false; message: string }).message + "\n");
					process.exit(1);
				}

				// Headless mode supports all prompt commands and some local commands
				// If disableSlashCommands is true, return empty array
				const commandsHeadless = disableSlashCommands
					? []
					: commands.filter(
							(command) =>
								(command.type === "prompt" &&
									!command.disableNonInteractive) ||
								(command.type === "local" &&
									command.supportsNonInteractive),
						);

				const defaultState = getDefaultAppState();
				const headlessInitialState: AppState = {
					...defaultState,
					mcp: {
						...defaultState.mcp,
						clients: mcpClients,
						commands: mcpCommands,
						tools: mcpTools,
					},
					toolPermissionContext,
					effortValue:
						parseEffortValue(options.effort) ??
						getInitialEffortSetting(),
					...(isFastModeEnabled() && {
						fastMode: getInitialFastModeSetting(
							effectiveModel ?? null,
						),
					}),
					...(isAdvisorEnabled() && advisorModel && { advisorModel }),
					// kairosEnabled gates the async fire-and-forget path in
					// executeForkedSlashCommand (processSlashCommand.tsx:132) and
					// AgentTool's shouldRunAsync. The REPL initialState sets this at
					// ~3459; headless was defaulting to false, so the daemon child's
					// scheduled tasks and Agent-tool calls ran synchronously — N
					// overdue cron tasks on spawn = N serial subagent turns blocking
					// user input. Computed at :1620, well before this branch.
					...(feature("KAIROS") ? { kairosEnabled } : {}),
				};

				// Init app state
				const headlessStore = createStore(
					headlessInitialState,
					onChangeAppState,
				);

				// Async check of auto mode gate — corrects state and disables auto if needed.
				if (feature("TRANSCRIPT_CLASSIFIER")) {
					void verifyAutoModeGateAccess(
						toolPermissionContext,
						headlessStore.getState().fastMode,
					).then(({ updateContext }) => {
						headlessStore.setState((prev) => {
							const nextCtx = updateContext(
								prev.toolPermissionContext,
							);
							if (nextCtx === prev.toolPermissionContext)
								return prev;
							return { ...prev, toolPermissionContext: nextCtx };
						});
					});
				}

				// Set global state for session persistence
				if (options.sessionPersistence === false) {
					setSessionPersistenceDisabled(true);
				}

				// Store SDK betas in global state for context window calculation
				// Only store allowed betas (filters by allowlist and subscriber status)
				setSdkBetas(filterAllowedSdkBetas(betas));

				// Print-mode MCP: per-server incremental push into headlessStore.
				// Mirrors useManageMCPConnections — push pending first (so ToolSearch's
				// pending-check at ToolSearchTool.ts:334 sees them), then replace with
				// connected/failed as each server settles.
				const connectMcpBatch = (
					configs: Record<string, ScopedMcpServerConfig>,
					label: string,
				): Promise<void> => {
					if (Object.keys(configs).length === 0)
						return Promise.resolve();
					headlessStore.setState((prev) => ({
						...prev,
						mcp: {
							...prev.mcp,
							clients: [
								...prev.mcp.clients,
								...Object.entries(configs).map(
									([name, config]) => ({
										name,
										type: "pending" as const,
										config,
									}),
								),
							],
						},
					}));
					return getMcpToolsCommandsAndResources(
						({ client, tools, commands }) => {
							headlessStore.setState((prev) => ({
								...prev,
								mcp: {
									...prev.mcp,
									clients: prev.mcp.clients.some(
										(c) => c.name === client.name,
									)
										? prev.mcp.clients.map((c) =>
												c.name === client.name
													? client
													: c,
											)
										: [...prev.mcp.clients, client],
									tools: uniqBy(
										[...prev.mcp.tools, ...tools],
										"name",
									),
									commands: uniqBy(
										[...prev.mcp.commands, ...commands],
										"name",
									),
								},
							}));
						},
						configs,
					).catch((err) =>
						logForDebugging(`[MCP] ${label} connect error: ${err}`),
					);
				};
				// Await all MCP configs — print mode is often single-turn, so
				// "late-connecting servers visible next turn" doesn't help. SDK init
				// message and turn-1 tool list both need configured MCP tools present.
				// Zero-server case is free via the early return in connectMcpBatch.
				// Connectors parallelize inside getMcpToolsCommandsAndResources
				// (processBatched with Promise.all). claude.ai is awaited too — its
				// fetch was kicked off early (line ~2558) so only residual time blocks
				// here. --bare skips claude.ai entirely for perf-sensitive scripts.
				profileCheckpoint("before_connectMcp");
				await connectMcpBatch(regularMcpConfigs, "regular");
				profileCheckpoint("after_connectMcp");
				// Dedup: suppress plugin MCP servers that duplicate a claude.ai
				// connector (connector wins), then connect claude.ai servers.
				// Bounded wait — #23725 made this blocking so single-turn -p sees
				// connectors, but with 40+ slow connectors tengu_startup_perf p99
				// climbed to 76s. If fetch+connect doesn't finish in time, proceed;
				// the promise keeps running and updates headlessStore in the
				// background so turn 2+ still sees connectors.
				const CLAUDE_AI_MCP_TIMEOUT_MS = 5_000;
				const claudeaiConnect = claudeaiConfigPromise.then(
					(claudeaiConfigs) => {
						if (Object.keys(claudeaiConfigs).length > 0) {
							const claudeaiSigs = new Set<string>();
							for (const config of Object.values(
								claudeaiConfigs,
							)) {
								const sig = getMcpServerSignature(config);
								if (sig) claudeaiSigs.add(sig);
							}
							const suppressed = new Set<string>();
							for (const [name, config] of Object.entries(
								regularMcpConfigs,
							)) {
								if (!name.startsWith("plugin:")) continue;
								const sig = getMcpServerSignature(config);
								if (sig && claudeaiSigs.has(sig))
									suppressed.add(name);
							}
							if (suppressed.size > 0) {
								logForDebugging(
									`[MCP] Lazy dedup: suppressing ${suppressed.size} plugin server(s) that duplicate claude.ai connectors: ${[...suppressed].join(", ")}`,
								);
								// Disconnect before filtering from state. Only connected
								// servers need cleanup — clearServerCache on a never-connected
								// server triggers a real connect just to kill it (memoize
								// cache-miss path, see useManageMCPConnections.ts:870).
								for (const c of headlessStore.getState().mcp
									.clients) {
									if (
										!suppressed.has(c.name) ||
										c.type !== "connected"
									)
										continue;
									c.client.onclose = undefined;
									void clearServerCache(
										c.name,
										c.config,
									).catch(() => {});
								}
								headlessStore.setState((prev) => {
									let {
										clients,
										tools,
										commands,
										resources,
									} = prev.mcp;
									clients = clients.filter(
										(c) => !suppressed.has(c.name),
									);
									tools = tools.filter(
										(t) =>
											!t.mcpInfo ||
											!suppressed.has(
												t.mcpInfo.serverName,
											),
									);
									for (const name of suppressed) {
										commands = excludeCommandsByServer(
											commands,
											name,
										);
										resources = excludeResourcesByServer(
											resources,
											name,
										);
									}
									return {
										...prev,
										mcp: {
											...prev.mcp,
											clients,
											tools,
											commands,
											resources,
										},
									};
								});
							}
						}
						// Suppress claude.ai connectors that duplicate an enabled
						// manual server (URL-signature match). Plugin dedup above only
						// handles `plugin:*` keys; this catches manual `.mcp.json` entries.
						// plugin:* must be excluded here — step 1 already suppressed
						// those (claude.ai wins); leaving them in suppresses the
						// connector too, and neither survives (gh-39974).
						const nonPluginConfigs = pickBy(
							regularMcpConfigs,
							(_, n) => !n.startsWith("plugin:"),
						);
						const { servers: dedupedClaudeAi } =
							dedupClaudeAiMcpServers(
								claudeaiConfigs,
								nonPluginConfigs,
							);
						return connectMcpBatch(dedupedClaudeAi, "claudeai");
					},
				);
				let claudeaiTimer: ReturnType<typeof setTimeout> | undefined;
				const claudeaiTimedOut = await Promise.race([
					claudeaiConnect.then(() => false),
					new Promise<boolean>((resolve) => {
						claudeaiTimer = setTimeout(
							(r) => r(true),
							CLAUDE_AI_MCP_TIMEOUT_MS,
							resolve,
						);
					}),
				]);
				if (claudeaiTimer) clearTimeout(claudeaiTimer);
				if (claudeaiTimedOut) {
					logForDebugging(
						`[MCP] claude.ai connectors not ready after ${CLAUDE_AI_MCP_TIMEOUT_MS}ms — proceeding; background connection continues`,
					);
				}
				profileCheckpoint("after_connectMcp_claudeai");

				// In headless mode, start deferred prefetches immediately (no user typing delay)
				// --bare / SIMPLE: startDeferredPrefetches early-returns internally.
				// backgroundHousekeeping (initExtractMemories, pruneShellSnapshots,
				// cleanupOldMessageFiles) and sdkHeapDumpMonitor are all bookkeeping
				// that scripted calls don't need — the next interactive session reconciles.
				if (!isBareMode()) {
					startDeferredPrefetches();
					void import("./utils/backgroundHousekeeping.js").then((m) =>
						m.startBackgroundHousekeeping(),
					);
					if (process.env.USER_TYPE === "ant") {
						void import("./utils/sdkHeapDumpMonitor.js").then((m) =>
							m.startSdkMemoryMonitor(),
						);
					}
				}

				logSessionTelemetry();
				profileCheckpoint("before_print_import");
				const { runHeadless } = await import("src/cli/print.js");
				profileCheckpoint("after_print_import");
				void runHeadless(
					inputPrompt,
					() => headlessStore.getState(),
					headlessStore.setState,
					commandsHeadless,
					tools,
					sdkMcpConfigs,
					agentDefinitions.activeAgents,
					{
						continue: options.continue,
						resume: options.resume,
						verbose: verbose,
						outputFormat: outputFormat,
						jsonSchema,
						permissionPromptToolName: options.permissionPromptTool,
						allowedTools,
						thinkingConfig,
						maxTurns: options.maxTurns,
						maxBudgetUsd: options.maxBudgetUsd,
						taskBudget: options.taskBudget
							? { total: options.taskBudget }
							: undefined,
						systemPrompt,
						appendSystemPrompt,
						userSpecifiedModel: effectiveModel,
						fallbackModel: userSpecifiedFallbackModel,
						teleport,
						sdkUrl,
						replayUserMessages: effectiveReplayUserMessages,
						includePartialMessages: effectiveIncludePartialMessages,
						forkSession: options.forkSession || false,
						resumeSessionAt: options.resumeSessionAt || undefined,
						rewindFiles: options.rewindFiles,
						enableAuthStatus: options.enableAuthStatus,
						agent: agentCli,
						workload: options.workload,
						setupTrigger: setupTrigger ?? undefined,
						sessionStartHooksPromise,
					},
				);
				return;
			}

			// Log model config at startup
			logEvent("tengu_startup_manual_model_config", {
				cli_flag:
					options.model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
				env_var: process.env
					.ANTHROPIC_MODEL as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
				settings_file: (getInitialSettings() || {})
					.model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
				subscriptionType:
					getSubscriptionType() as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
				agent: agentSetting as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			});

			// Get deprecation warning for the initial model (resolvedInitialModel computed earlier for hooks parallelization)
			const deprecationWarning =
				getModelDeprecationWarning(resolvedInitialModel);

			// Build initial notification queue
			const initialNotifications: Array<{
				key: string;
				text: string;
				color?: "warning";
				priority: "high";
			}> = [];
			if (permissionModeNotification) {
				initialNotifications.push({
					key: "permission-mode-notification",
					text: permissionModeNotification,
					priority: "high",
				});
			}
			if (deprecationWarning) {
				initialNotifications.push({
					key: "model-deprecation-warning",
					text: deprecationWarning,
					color: "warning",
					priority: "high",
				});
			}
			if (overlyBroadBashPermissions.length > 0) {
				const displayList = uniq(
					overlyBroadBashPermissions.map((p) => p.ruleDisplay),
				);
				const displays = displayList.join(", ");
				const sources = uniq(
					overlyBroadBashPermissions.map((p) => p.sourceDisplay),
				).join(", ");
				const n = displayList.length;
				initialNotifications.push({
					key: "overly-broad-bash-notification",
					text: `${displays} allow ${plural(n, "rule")} from ${sources} ${plural(n, "was", "were")} ignored \u2014 not available for Ants, please use auto-mode instead`,
					color: "warning",
					priority: "high",
				});
			}

			const teammateUtils = getTeammateUtils();
			const effectiveToolPermissionContext = {
				...toolPermissionContext,
				mode:
					isAgentSwarmsEnabled() &&
					teammateUtils?.isPlanModeRequired?.()
						? ("plan" as const)
						: toolPermissionContext.mode,
			};
			// All startup opt-in paths (--tools, --brief, defaultView) have fired
			// above; initialIsBriefOnly just reads the resulting state.
			const initialIsBriefOnly =
				feature("KAIROS") || feature("KAIROS_BRIEF")
					? getUserMsgOptIn()
					: false;
			const fullRemoteControl =
				remoteControl || getRemoteControlAtStartup() || kairosEnabled;
			let ccrMirrorEnabled = false;
			if (feature("CCR_MIRROR") && !fullRemoteControl) {
				/* eslint-disable @typescript-eslint/no-require-imports */
				const { isCcrMirrorEnabled } =
					require("./bridge/bridgeEnabled.js") as typeof import("./bridge/bridgeEnabled.js");
				/* eslint-enable @typescript-eslint/no-require-imports */
				ccrMirrorEnabled = isCcrMirrorEnabled();
			}

			const initialState: AppState = {
				settings: getInitialSettings(),
				tasks: {},
				agentNameRegistry: new Map(),
				verbose: verbose ?? getGlobalConfig().verbose ?? false,
				mainLoopModel: initialMainLoopModel,
				mainLoopModelForSession: null,
				isBriefOnly: initialIsBriefOnly,
				expandedView: getGlobalConfig().showSpinnerTree
					? "teammates"
					: getGlobalConfig().showExpandedTodos
						? "tasks"
						: "none",
				showTeammateMessagePreview: isAgentSwarmsEnabled()
					? false
					: undefined,
				selectedIPAgentIndex: -1,
				coordinatorTaskIndex: -1,
				viewSelectionMode: "none",
				footerSelection: null,
				toolPermissionContext: effectiveToolPermissionContext,
				agent: mainThreadAgentDefinition?.agentType,
				agentDefinitions,
				mcp: {
					clients: [],
					tools: [],
					commands: [],
					resources: {},
					pluginReconnectKey: 0,
				},
				plugins: {
					enabled: [],
					disabled: [],
					commands: [],
					errors: [],
					installationStatus: {
						marketplaces: [],
						plugins: [],
					},
					needsRefresh: false,
				},
				statusLineText: undefined,
				kairosEnabled,
				remoteSessionUrl: undefined,
				remoteConnectionStatus: "connecting",
				remoteBackgroundTaskCount: 0,
				replBridgeEnabled: fullRemoteControl || ccrMirrorEnabled,
				replBridgeExplicit: remoteControl,
				replBridgeOutboundOnly: ccrMirrorEnabled,
				replBridgeConnected: false,
				replBridgeSessionActive: false,
				replBridgeReconnecting: false,
				replBridgeConnectUrl: undefined,
				replBridgeSessionUrl: undefined,
				replBridgeEnvironmentId: undefined,
				replBridgeSessionId: undefined,
				replBridgeError: undefined,
				replBridgeInitialName: remoteControlName,
				showRemoteCallout: false,
				notifications: {
					current: null,
					queue: initialNotifications,
				},
				elicitation: {
					queue: [],
				},
				todos: {},
				remoteAgentTaskSuggestions: [],
				fileHistory: {
					snapshots: [],
					trackedFiles: new Set(),
					snapshotSequence: 0,
				},
				attribution: createEmptyAttributionState(),
				thinkingEnabled,
				promptSuggestionEnabled: shouldEnablePromptSuggestion(),
				sessionHooks: new Map(),
				inbox: {
					messages: [],
				},
				promptSuggestion: {
					text: null,
					promptId: null,
					shownAt: 0,
					acceptedAt: 0,
					generationRequestId: null,
				},
				speculation: IDLE_SPECULATION_STATE,
				speculationSessionTimeSavedMs: 0,
				skillImprovement: {
					suggestion: null,
				},
				workerSandboxPermissions: {
					queue: [],
					selectedIndex: 0,
				},
				pendingWorkerRequest: null,
				pendingSandboxRequest: null,
				authVersion: 0,
				initialMessage: inputPrompt
					? {
							message: createUserMessage({
								content: String(inputPrompt),
							}),
						}
					: null,
				effortValue:
					parseEffortValue(options.effort) ??
					getInitialEffortSetting(),
				activeOverlays: new Set<string>(),
				fastMode: getInitialFastModeSetting(resolvedInitialModel),
				...(isAdvisorEnabled() && advisorModel && { advisorModel }),
				// Compute teamContext synchronously to avoid useEffect setState during render.
				// KAIROS: assistantTeamContext takes precedence — set earlier in the
				// KAIROS block so Agent(name: "foo") can spawn in-process teammates
				// without TeamCreate. computeInitialTeamContext() is for tmux-spawned
				// teammates reading their own identity, not the assistant-mode leader.
				teamContext: (feature("KAIROS")
					? (assistantTeamContext ?? computeInitialTeamContext())
					: computeInitialTeamContext()) as AppState["teamContext"],
			};

			// Add CLI initial prompt to history
			if (inputPrompt) {
				addToHistory(String(inputPrompt));
			}

			const initialTools = mcpTools;

			// Increment numStartups synchronously — first-render readers like
			// shouldShowEffortCallout (via useState initializer) need the updated
			// value before setImmediate fires. Defer only telemetry.
			saveGlobalConfig((current) => ({
				...current,
				numStartups: (current.numStartups ?? 0) + 1,
			}));
			setImmediate(() => {
				void logStartupTelemetry();
				logSessionTelemetry();
			});

			// Set up per-turn session environment data uploader (ant-only build).
			// Default-enabled for all ant users when working in an Anthropic-owned
			// repo. Captures git/filesystem state (NOT transcripts) at each turn so
			// environments can be recreated at any user message index. Gating:
			//   - Build-time: this import is stubbed in external builds.
			//   - Runtime: uploader checks github.com/anthropics/* remote + gcloud auth.
			//   - Safety: CLAUDE_CODE_DISABLE_SESSION_DATA_UPLOAD=1 bypasses (tests set this).
			// Import is dynamic + async to avoid adding startup latency.
			const sessionUploaderPromise =
				process.env.USER_TYPE === "ant"
					? import("./utils/sessionDataUploader.js")
					: null;

			// Defer session uploader resolution to the onTurnComplete callback to avoid
			// adding a new top-level await in main.tsx (performance-critical path).
			// The per-turn auth logic in sessionDataUploader.ts handles unauthenticated
			// state gracefully (re-checks each turn, so auth recovery mid-session works).
			const uploaderReady = sessionUploaderPromise
				? sessionUploaderPromise
						.then((mod) => mod.createSessionTurnUploader())
						.catch(() => null)
				: null;

			const sessionConfig = {
				debug: debug || debugToStderr,
				commands: [...commands, ...mcpCommands],
				initialTools,
				mcpClients,
				autoConnectIdeFlag: ide,
				mainThreadAgentDefinition,
				disableSlashCommands,
				dynamicMcpConfig,
				strictMcpConfig,
				systemPrompt,
				appendSystemPrompt,
				taskListId,
				thinkingConfig,
				...(uploaderReady && {
					onTurnComplete: (messages: MessageType[]) => {
						void uploaderReady.then((uploader) =>
							(uploader as ((msgs: MessageType[]) => void) | null)?.(messages),
						);
					},
				}),
			};

			// Shared context for processResumedConversation calls
			const resumeContext = {
				modeApi: coordinatorModeModule,
				mainThreadAgentDefinition,
				agentDefinitions,
				currentCwd,
				cliAgents,
				initialState,
			};

			if (options.continue) {
				// Continue the most recent conversation directly
				let resumeSucceeded = false;
				try {
					const resumeStart = performance.now();

					// Clear stale caches before resuming to ensure fresh file/skill discovery
					const { clearSessionCaches } =
						await import("./commands/clear/caches.js");
					clearSessionCaches();

					const result = await loadConversationForResume(
						undefined /* sessionId */,
						undefined /* sourceFile */,
					);
					if (!result) {
						logEvent("tengu_continue", {
							success: false,
						});
						return await exitWithError(
							root,
							"No conversation found to continue",
						);
					}

					const loaded = await processResumedConversation(
						result,
						{
							forkSession: !!options.forkSession,
							includeAttribution: true,
							transcriptPath: result.fullPath,
						},
						resumeContext,
					);

					if (loaded.restoredAgentDef) {
						mainThreadAgentDefinition = loaded.restoredAgentDef;
					}

					maybeActivateProactive(options);
					maybeActivateBrief(options);

					logEvent("tengu_continue", {
						success: true,
						resume_duration_ms: Math.round(
							performance.now() - resumeStart,
						),
					});
					resumeSucceeded = true;

					await launchRepl(
						root,
						{
							getFpsMetrics,
							stats,
							initialState: loaded.initialState,
						},
						{
							...sessionConfig,
							mainThreadAgentDefinition:
								loaded.restoredAgentDef ??
								mainThreadAgentDefinition,
							initialMessages: loaded.messages,
							initialFileHistorySnapshots:
								loaded.fileHistorySnapshots,
							initialContentReplacements:
								loaded.contentReplacements,
							initialAgentName: loaded.agentName,
							initialAgentColor: loaded.agentColor,
						},
						renderAndRun,
					);
				} catch (error) {
					if (!resumeSucceeded) {
						logEvent("tengu_continue", {
							success: false,
						});
					}
					logError(error);
					process.exit(1);
				}
			} else if (feature("DIRECT_CONNECT") && _pendingConnect?.url) {
				// `claude connect <url>` — full interactive TUI connected to a remote server
				let directConnectConfig;
				try {
					const session = await createDirectConnectSession({
						serverUrl: _pendingConnect.url,
						authToken: _pendingConnect.authToken,
						cwd: getOriginalCwd(),
						dangerouslySkipPermissions:
							_pendingConnect.dangerouslySkipPermissions,
					});
					if (session.workDir) {
						setOriginalCwd(session.workDir);
						setCwdState(session.workDir);
					}
					setDirectConnectServerUrl(_pendingConnect.url);
					directConnectConfig = session.config;
				} catch (err) {
					return await exitWithError(
						root,
						err instanceof DirectConnectError
							? err.message
							: String(err),
						() => gracefulShutdown(1),
					);
				}

				const connectInfoMessage = createSystemMessage(
					`Connected to server at ${_pendingConnect.url}\nSession: ${directConnectConfig.sessionId}`,
					"info",
				);

				await launchRepl(
					root,
					{ getFpsMetrics, stats, initialState },
					{
						debug: debug || debugToStderr,
						commands,
						initialTools: [],
						initialMessages: [connectInfoMessage],
						mcpClients: [],
						autoConnectIdeFlag: ide,
						mainThreadAgentDefinition,
						disableSlashCommands,
						directConnectConfig,
						thinkingConfig,
					},
					renderAndRun,
				);
				return;
			} else if (feature("SSH_REMOTE") && _pendingSSH?.host) {
				// `claude ssh <host> [dir]` — probe remote, deploy binary if needed,
				// spawn ssh with unix-socket -R forward to a local auth proxy, hand
				// the REPL an SSHSession. Tools run remotely, UI renders locally.
				// `--local` skips probe/deploy/ssh and spawns the current binary
				// directly with the same env — e2e test of the proxy/auth plumbing.
				const {
					createSSHSession,
					createLocalSSHSession,
					SSHSessionError,
				} = await import("./ssh/createSSHSession.js");
				let sshSession: import('./ssh/createSSHSession.js').SSHSession | undefined;
				try {
					if (_pendingSSH.local) {
						process.stderr.write(
							"Starting local ssh-proxy test session...\n",
						);
						sshSession = await createLocalSSHSession({
							cwd: _pendingSSH.cwd,
							permissionMode: _pendingSSH.permissionMode,
							dangerouslySkipPermissions:
								_pendingSSH.dangerouslySkipPermissions,
						});
					} else {
						process.stderr.write(
							`Connecting to ${_pendingSSH.host}…\n`,
						);
						// In-place progress: \r + EL0 (erase to end of line). Final \n on
						// success so the next message lands on a fresh line. No-op when
						// stderr isn't a TTY (piped/redirected) — \r would just emit noise.
						const isTTY = process.stderr.isTTY;
						let hadProgress = false;
						sshSession = await createSSHSession(
							{
								host: _pendingSSH.host,
								cwd: _pendingSSH.cwd,
								localVersion: MACRO.VERSION,
								permissionMode: _pendingSSH.permissionMode,
								dangerouslySkipPermissions:
									_pendingSSH.dangerouslySkipPermissions,
								extraCliArgs: _pendingSSH.extraCliArgs,
								remoteBin: _pendingSSH.remoteBin,
							},
							isTTY
								? {
										onProgress: (msg: string) => {
											hadProgress = true;
											process.stderr.write(
												`\r  ${msg}\x1b[K`,
											);
										},
									}
								: {},
						);
						if (hadProgress) process.stderr.write("\n");
					}
					setOriginalCwd(sshSession.remoteCwd);
					setCwdState(sshSession.remoteCwd);
					setDirectConnectServerUrl(
						_pendingSSH.local ? "local" : _pendingSSH.host,
					);
				} catch (err) {
					return await exitWithError(
						root,
						err instanceof SSHSessionError
							? err.message
							: String(err),
						() => gracefulShutdown(1),
					);
				}

				const sshInfoMessage = createSystemMessage(
					_pendingSSH.local
						? `Local ssh-proxy test session\ncwd: ${sshSession.remoteCwd}\nAuth: unix socket → local proxy`
						: `SSH session to ${_pendingSSH.host}\nRemote cwd: ${sshSession.remoteCwd}\nAuth: unix socket -R → local proxy`,
					"info",
				);

				await launchRepl(
					root,
					{ getFpsMetrics, stats, initialState },
					{
						debug: debug || debugToStderr,
						commands,
						initialTools: [],
						initialMessages: [sshInfoMessage],
						mcpClients: [],
						autoConnectIdeFlag: ide,
						mainThreadAgentDefinition,
						disableSlashCommands,
						sshSession,
						thinkingConfig,
					},
					renderAndRun,
				);
				return;
			} else if (
				feature("KAIROS") &&
				_pendingAssistantChat &&
				(_pendingAssistantChat.sessionId ||
					_pendingAssistantChat.discover)
			) {
				// `claude assistant [sessionId]` — REPL as a pure viewer client
				// of a remote assistant session. The agentic loop runs remotely; this
				// process streams live events and POSTs messages. History is lazy-
				// loaded by useAssistantHistory on scroll-up (no blocking fetch here).
				const { discoverAssistantSessions } =
					await import("./assistant/sessionDiscovery.js");

				let targetSessionId = _pendingAssistantChat.sessionId;

				// Discovery flow — list bridge environments, filter sessions
				if (!targetSessionId) {
					let sessions;
					try {
						sessions = await discoverAssistantSessions();
					} catch (e) {
						return await exitWithError(
							root,
							`Failed to discover sessions: ${e instanceof Error ? e.message : e}`,
							() => gracefulShutdown(1),
						);
					}
					if (sessions.length === 0) {
						let installedDir: string | null;
						try {
							installedDir =
								await launchAssistantInstallWizard(root);
						} catch (e) {
							return await exitWithError(
								root,
								`Assistant installation failed: ${e instanceof Error ? e.message : e}`,
								() => gracefulShutdown(1),
							);
						}
						if (installedDir === null) {
							await gracefulShutdown(0);
							process.exit(0);
						}
						// The daemon needs a few seconds to spin up its worker and
						// establish a bridge session before discovery will find it.
						return await exitWithMessage(
							root,
							`Assistant installed in ${installedDir}. The daemon is starting up — run \`claude assistant\` again in a few seconds to connect.`,
							{
								exitCode: 0,
								beforeExit: () => gracefulShutdown(0),
							},
						);
					}
					if (sessions.length === 1) {
						targetSessionId = sessions[0]!.id;
					} else {
						const picked = await launchAssistantSessionChooser(
							root,
							{
								sessions,
							},
						);
						if (!picked) {
							await gracefulShutdown(0);
							process.exit(0);
						}
						targetSessionId = picked;
					}
				}

				// Auth — call prepareApiRequest() once for orgUUID, but use a
				// getAccessToken closure for the token so reconnects get fresh tokens.
				const {
					checkAndRefreshOAuthTokenIfNeeded,
					getClaudeAIOAuthTokens,
				} = await import("./utils/auth.js");
				await checkAndRefreshOAuthTokenIfNeeded();
				let apiCreds;
				try {
					apiCreds = await prepareApiRequest();
				} catch (e) {
					return await exitWithError(
						root,
						`Error: ${e instanceof Error ? e.message : "Failed to authenticate"}`,
						() => gracefulShutdown(1),
					);
				}
				const getAccessToken = (): string =>
					getClaudeAIOAuthTokens()?.accessToken ??
					apiCreds.accessToken;

				// Brief mode activation: setKairosActive(true) satisfies BOTH opt-in
				// and entitlement for isBriefEnabled() (BriefTool.ts:124-132).
				setKairosActive(true);
				setUserMsgOptIn(true);
				setIsRemoteMode(true);

				const remoteSessionConfig = createRemoteSessionConfig(
					targetSessionId,
					getAccessToken,
					apiCreds.orgUUID,
					/* hasInitialPrompt */ false,
					/* viewerOnly */ true,
				);

				const infoMessage = createSystemMessage(
					`Attached to assistant session ${targetSessionId.slice(0, 8)}…`,
					"info",
				);

				const assistantInitialState: AppState = {
					...initialState,
					isBriefOnly: true,
					kairosEnabled: false,
					replBridgeEnabled: false,
				};

				const remoteCommands = filterCommandsForRemoteMode(commands);
				await launchRepl(
					root,
					{
						getFpsMetrics,
						stats,
						initialState: assistantInitialState,
					},
					{
						debug: debug || debugToStderr,
						commands: remoteCommands,
						initialTools: [],
						initialMessages: [infoMessage],
						mcpClients: [],
						autoConnectIdeFlag: ide,
						mainThreadAgentDefinition,
						disableSlashCommands,
						remoteSessionConfig,
						thinkingConfig,
					},
					renderAndRun,
				);
				return;
			} else if (
				options.resume ||
				options.fromPr ||
				teleport ||
				remote !== null
			) {
				// Handle resume flow - from file (ant-only), session ID, or interactive selector

				// Clear stale caches before resuming to ensure fresh file/skill discovery
				const { clearSessionCaches } =
					await import("./commands/clear/caches.js");
				clearSessionCaches();

				let messages: MessageType[] | null = null;
				let processedResume: ProcessedResume | undefined;

				let maybeSessionId = validateUuid(options.resume);
				let searchTerm: string | undefined;
				// Store full LogOption when found by custom title (for cross-worktree resume)
				let matchedLog: LogOption | null = null;
				// PR filter for --from-pr flag
				let filterByPr: boolean | number | string | undefined;

				// Handle --from-pr flag
				if (options.fromPr) {
					if (options.fromPr === true) {
						// Show all sessions with linked PRs
						filterByPr = true;
					} else if (typeof options.fromPr === "string") {
						// Could be a PR number or URL
						filterByPr = options.fromPr;
					}
				}

				// If resume value is not a UUID, try exact match by custom title first
				if (
					options.resume &&
					typeof options.resume === "string" &&
					!maybeSessionId
				) {
					const trimmedValue = options.resume.trim();
					if (trimmedValue) {
						const matches = await searchSessionsByCustomTitle(
							trimmedValue,
							{
								exact: true,
							},
						);

						if (matches.length === 1) {
							// Exact match found - store full LogOption for cross-worktree resume
							matchedLog = matches[0]!;
							maybeSessionId =
								getSessionIdFromLog(matchedLog) ?? null;
						} else {
							// No match or multiple matches - use as search term for picker
							searchTerm = trimmedValue;
						}
					}
				}

				// --remote and --teleport both create/resume CoStrict remote sessions.
				// Remote Control (--rc) is a separate feature gated in initReplBridge.ts.
				if (remote !== null || teleport) {
					await waitForPolicyLimitsToLoad();
					if (!isPolicyAllowed("allow_remote_sessions")) {
						return await exitWithError(
							root,
							"Error: Remote sessions are disabled by your organization's policy.",
							() => gracefulShutdown(1),
						);
					}
				}

				if (remote !== null) {
					// Create remote session (optionally with initial prompt)
					const hasInitialPrompt = remote.length > 0;

					// Check if TUI mode is enabled - description is only optional in TUI mode
					const isRemoteTuiEnabled =
						getFeatureValue_CACHED_MAY_BE_STALE(
							"tengu_remote_backend",
							false,
						);
					if (!isRemoteTuiEnabled && !hasInitialPrompt) {
						return await exitWithError(
							root,
							'Error: --remote requires a description.\nUsage: csc --remote "your task description"',
							() => gracefulShutdown(1),
						);
					}

					logEvent("tengu_remote_create_session", {
						has_initial_prompt: String(
							hasInitialPrompt,
						) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					});

					// Pass current branch so CCR clones the repo at the right revision
					const currentBranch = await getBranch();
					const createdSession =
						await teleportToRemoteWithErrorHandling(
							root,
							hasInitialPrompt ? remote : null,
							new AbortController().signal,
							currentBranch || undefined,
						);
					if (!createdSession) {
						logEvent("tengu_remote_create_session_error", {
							error: "unable_to_create_session" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
						});
						return await exitWithError(
							root,
							"Error: Unable to create remote session",
							() => gracefulShutdown(1),
						);
					}
					logEvent("tengu_remote_create_session_success", {
						session_id:
							createdSession.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
					});

					// Check if new remote TUI mode is enabled via feature gate
					if (!isRemoteTuiEnabled) {
						// Original behavior: print session info and exit
						process.stdout.write(
							`Created remote session: ${createdSession.title}\n`,
						);
						process.stdout.write(
							`View: ${getRemoteSessionUrl(createdSession.id)}?m=0\n`,
						);
						process.stdout.write(
							`Resume with: claude --teleport ${createdSession.id}\n`,
						);
						await gracefulShutdown(0);
						process.exit(0);
					}

					// New behavior: start local TUI with CCR engine
					// Mark that we're in remote mode for command visibility
					setIsRemoteMode(true);
					switchSession(asSessionId(createdSession.id));

					// Get OAuth credentials for remote session
					let apiCreds: { accessToken: string; orgUUID: string };
					try {
						apiCreds = await prepareApiRequest();
					} catch (error) {
						logError(toError(error));
						return await exitWithError(
							root,
							`Error: ${errorMessage(error) || "Failed to authenticate"}`,
							() => gracefulShutdown(1),
						);
					}

					// Create remote session config for the REPL
					const { getClaudeAIOAuthTokens: getTokensForRemote } =
						await import("./utils/auth.js");
					const getAccessTokenForRemote = (): string =>
						getTokensForRemote()?.accessToken ??
						apiCreds.accessToken;
					const remoteSessionConfig = createRemoteSessionConfig(
						createdSession.id,
						getAccessTokenForRemote,
						apiCreds.orgUUID,
						hasInitialPrompt,
					);

					// Add remote session info as initial system message
					const remoteSessionUrl = `${getRemoteSessionUrl(createdSession.id)}?m=0`;
					const remoteInfoMessage = createSystemMessage(
						`/remote-control is active. Code in CLI or at ${remoteSessionUrl}`,
						"info",
					);

					// Create initial user message from the prompt if provided (CCR echoes it back but we ignore that)
					const initialUserMessage = hasInitialPrompt
						? createUserMessage({ content: remote })
						: null;

					// Set remote session URL in app state for footer indicator
					const remoteInitialState = {
						...initialState,
						remoteSessionUrl,
					};

					// Pre-filter commands to only include remote-safe ones.
					// CCR's init response may further refine the list (via handleRemoteInit in REPL).
					const remoteCommands =
						filterCommandsForRemoteMode(commands);
					await launchRepl(
						root,
						{
							getFpsMetrics,
							stats,
							initialState: remoteInitialState,
						},
						{
							debug: debug || debugToStderr,
							commands: remoteCommands,
							initialTools: [],
							initialMessages: initialUserMessage
								? [remoteInfoMessage, initialUserMessage]
								: [remoteInfoMessage],
							mcpClients: [],
							autoConnectIdeFlag: ide,
							mainThreadAgentDefinition,
							disableSlashCommands,
							remoteSessionConfig,
							thinkingConfig,
						},
						renderAndRun,
					);
					return;
				} else if (teleport) {
					if (teleport === true || teleport === "") {
						// Interactive mode: show task selector and handle resume
						logEvent("tengu_teleport_interactive_mode", {});
						logForDebugging(
							"selectAndResumeTeleportTask: Starting teleport flow...",
						);
						const teleportResult =
							await launchTeleportResumeWrapper(root);
						if (!teleportResult) {
							// User cancelled or error occurred
							await gracefulShutdown(0);
							process.exit(0);
						}
						const { branchError } =
							await checkOutTeleportedSessionBranch(
								teleportResult.branch,
							);
						messages = processMessagesForTeleportResume(
							teleportResult.log,
							branchError,
						);
					} else if (typeof teleport === "string") {
						logEvent("tengu_teleport_resume_session", {
							mode: "direct" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
						});
						try {
							// First, fetch session and validate repository before checking git state
							const sessionData = await fetchSession(teleport);
							const repoValidation =
								await validateSessionRepository(sessionData);

							// Handle repo mismatch or not in repo cases
							if (
								repoValidation.status === "mismatch" ||
								repoValidation.status === "not_in_repo"
							) {
								const sessionRepo = repoValidation.sessionRepo;
								if (sessionRepo) {
									// Check for known paths
									const knownPaths =
										getKnownPathsForRepo(sessionRepo);
									const existingPaths =
										await filterExistingPaths(knownPaths);

									if (existingPaths.length > 0) {
										// Show directory switch dialog
										const selectedPath =
											await launchTeleportRepoMismatchDialog(
												root,
												{
													targetRepo: sessionRepo,
													initialPaths: existingPaths,
												},
											);

										if (selectedPath) {
											// Change to the selected directory
											process.chdir(selectedPath);
											setCwd(selectedPath);
											setOriginalCwd(selectedPath);
										} else {
											// User cancelled
											await gracefulShutdown(0);
										}
									} else {
										// No known paths - show original error
										throw new TeleportOperationError(
											`You must run claude --teleport ${teleport} from a checkout of ${sessionRepo}.`,
											chalk.red(
												`You must run claude --teleport ${teleport} from a checkout of ${chalk.bold(sessionRepo)}.\n`,
											),
										);
									}
								}
							} else if (repoValidation.status === "error") {
								throw new TeleportOperationError(
									repoValidation.errorMessage ||
										"Failed to validate session",
									chalk.red(
										`Error: ${repoValidation.errorMessage || "Failed to validate session"}\n`,
									),
								);
							}

							await validateGitState();

							// Use progress UI for teleport
							const { teleportWithProgress } =
								await import("./components/TeleportProgress.js");
							const result = await teleportWithProgress(
								root,
								teleport,
							);
							// Track teleported session for reliability logging
							setTeleportedSessionInfo({ sessionId: teleport });
							messages = result.messages;
						} catch (error) {
							if (error instanceof TeleportOperationError) {
								process.stderr.write(
									error.formattedMessage + "\n",
								);
							} else {
								logError(error);
								process.stderr.write(
									chalk.red(
										`Error: ${errorMessage(error)}\n`,
									),
								);
							}
							await gracefulShutdown(1);
						}
					}
				}
				if (process.env.USER_TYPE === "ant") {
					if (
						options.resume &&
						typeof options.resume === "string" &&
						!maybeSessionId
					) {
						// Check for ccshare URL (e.g. https://go/ccshare/boris-20260311-211036)
						const { parseCcshareId, loadCcshare } =
							await import("./utils/ccshareResume.js");
						const ccshareId = parseCcshareId(options.resume);
						if (ccshareId) {
							try {
								const resumeStart = performance.now();
								const logOption = await loadCcshare(ccshareId);
								const result = await loadConversationForResume(
									logOption,
									undefined,
								);
								if (result) {
									processedResume =
										await processResumedConversation(
											result,
											{
												forkSession: true,
												transcriptPath: result.fullPath,
											},
											resumeContext,
										);
									if (processedResume.restoredAgentDef) {
										mainThreadAgentDefinition =
											processedResume.restoredAgentDef;
									}
									logEvent("tengu_session_resumed", {
										entrypoint:
											"ccshare" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
										success: true,
										resume_duration_ms: Math.round(
											performance.now() - resumeStart,
										),
									});
								} else {
									logEvent("tengu_session_resumed", {
										entrypoint:
											"ccshare" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
										success: false,
									});
								}
							} catch (error) {
								logEvent("tengu_session_resumed", {
									entrypoint:
										"ccshare" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
									success: false,
								});
								logError(error);
								await exitWithError(
									root,
									`Unable to resume from ccshare: ${errorMessage(error)}`,
									() => gracefulShutdown(1),
								);
							}
						} else {
							const resolvedPath = resolve(options.resume);
							try {
								const resumeStart = performance.now();
								let logOption;
								try {
									// Attempt to load as a transcript file; ENOENT falls through to session-ID handling
									logOption =
										await loadTranscriptFromFile(
											resolvedPath,
										);
								} catch (error) {
									if (!isENOENT(error)) throw error;
									// ENOENT: not a file path — fall through to session-ID handling
								}
								if (logOption) {
									const result =
										await loadConversationForResume(
											logOption,
											undefined /* sourceFile */,
										);
									if (result) {
										processedResume =
											await processResumedConversation(
												result,
												{
													forkSession:
														!!options.forkSession,
													transcriptPath:
														result.fullPath,
												},
												resumeContext,
											);
										if (processedResume.restoredAgentDef) {
											mainThreadAgentDefinition =
												processedResume.restoredAgentDef;
										}
										logEvent("tengu_session_resumed", {
											entrypoint:
												"file" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
											success: true,
											resume_duration_ms: Math.round(
												performance.now() - resumeStart,
											),
										});
									} else {
										logEvent("tengu_session_resumed", {
											entrypoint:
												"file" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
											success: false,
										});
									}
								}
							} catch (error) {
								logEvent("tengu_session_resumed", {
									entrypoint:
										"file" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
									success: false,
								});
								logError(error);
								await exitWithError(
									root,
									`Unable to load transcript from file: ${options.resume}`,
									() => gracefulShutdown(1),
								);
							}
						}
					}
				}

				// If not loaded as a file, try as session ID
				if (maybeSessionId) {
					// Resume specific session by ID
					const sessionId = maybeSessionId;
					try {
						const resumeStart = performance.now();
						// Use matchedLog if available (for cross-worktree resume by custom title)
						// Otherwise fall back to sessionId string (for direct UUID resume)
						const result = await loadConversationForResume(
							matchedLog ?? sessionId,
							undefined,
						);

						if (!result) {
							logEvent("tengu_session_resumed", {
								entrypoint:
									"cli_flag" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
								success: false,
							});
							return await exitWithError(
								root,
								`No conversation found with session ID: ${sessionId}`,
							);
						}

						const fullPath =
							matchedLog?.fullPath ?? result.fullPath;
						processedResume = await processResumedConversation(
							result,
							{
								forkSession: !!options.forkSession,
								sessionIdOverride: sessionId,
								transcriptPath: fullPath,
							},
							resumeContext,
						);

						if (processedResume.restoredAgentDef) {
							mainThreadAgentDefinition =
								processedResume.restoredAgentDef;
						}
						logEvent("tengu_session_resumed", {
							entrypoint:
								"cli_flag" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
							success: true,
							resume_duration_ms: Math.round(
								performance.now() - resumeStart,
							),
						});
					} catch (error) {
						logEvent("tengu_session_resumed", {
							entrypoint:
								"cli_flag" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
							success: false,
						});
						logError(error);
						await exitWithError(
							root,
							`Failed to resume session ${sessionId}`,
						);
					}
				}

				// Await file downloads before rendering REPL (files must be available)
				if (fileDownloadPromise) {
					try {
						const results = await fileDownloadPromise;
						const failedCount = count(results, (r) => !r.success);
						if (failedCount > 0) {
							process.stderr.write(
								chalk.yellow(
									`Warning: ${failedCount}/${results.length} file(s) failed to download.\n`,
								),
							);
						}
					} catch (error) {
						return await exitWithError(
							root,
							`Error downloading files: ${errorMessage(error)}`,
						);
					}
				}

				// If we have a processed resume or teleport messages, render the REPL
				const resumeData =
					processedResume ??
					(Array.isArray(messages)
						? {
								messages,
								fileHistorySnapshots: undefined,
								agentName: undefined,
								agentColor: undefined as
									| AgentColorName
									| undefined,
								restoredAgentDef: mainThreadAgentDefinition,
								initialState,
								contentReplacements: undefined,
							}
						: undefined);
				if (resumeData) {
					maybeActivateProactive(options);
					maybeActivateBrief(options);

					await launchRepl(
						root,
						{
							getFpsMetrics,
							stats,
							initialState: resumeData.initialState,
						},
						{
							...sessionConfig,
							mainThreadAgentDefinition:
								resumeData.restoredAgentDef ??
								mainThreadAgentDefinition,
							initialMessages: resumeData.messages,
							initialFileHistorySnapshots:
								resumeData.fileHistorySnapshots,
							initialContentReplacements:
								resumeData.contentReplacements,
							initialAgentName: resumeData.agentName,
							initialAgentColor: resumeData.agentColor,
						},
						renderAndRun,
					);
				} else {
					// Show interactive selector (includes same-repo worktrees)
					// Note: ResumeConversation loads logs internally to ensure proper GC after selection
					await launchResumeChooser(
						root,
						{ getFpsMetrics, stats, initialState },
						getWorktreePaths(getOriginalCwd()),
						{
							...sessionConfig,
							initialSearchQuery: searchTerm,
							forkSession: options.forkSession,
							filterByPr,
						},
					);
				}
			} else {
				// Pass unresolved hooks promise to REPL so it can render immediately
				// instead of blocking ~500ms waiting for SessionStart hooks to finish.
				// REPL will inject hook messages when they resolve and await them before
				// the first API call so the model always sees hook context.
				const pendingHookMessages =
					hooksPromise && hookMessages.length === 0
						? hooksPromise
						: undefined;

				profileCheckpoint("action_after_hooks");
				maybeActivateProactive(options);
				maybeActivateBrief(options);
				// Persist the current mode for fresh sessions so future resumes know what mode was used
				if (feature("COORDINATOR_MODE")) {
					saveMode(
						coordinatorModeModule?.isCoordinatorMode()
							? "coordinator"
							: "normal",
					);
				}

				// If launched via a deep link, show a provenance banner so the user
				// knows the session originated externally. Linux xdg-open and
				// browsers with "always allow" set dispatch the link with no OS-level
				// confirmation, so this is the only signal the user gets that the
				// prompt — and the working directory / CLAUDE.md it implies — came
				// from an external source rather than something they typed.
				let deepLinkBanner: ReturnType<
					typeof createSystemMessage
				> | null = null;
				if (feature("LODESTONE")) {
					if (options.deepLinkOrigin) {
						logEvent("tengu_deep_link_opened", {
							has_prefill: Boolean(options.prefill),
							has_repo: Boolean(options.deepLinkRepo),
						});
						deepLinkBanner = createSystemMessage(
							buildDeepLinkBanner({
								cwd: getCwd(),
								prefillLength: options.prefill?.length,
								repo: options.deepLinkRepo,
								lastFetch:
									options.deepLinkLastFetch !== undefined
										? new Date(options.deepLinkLastFetch)
										: undefined,
							}),
							"warning",
						);
					} else if (options.prefill) {
						deepLinkBanner = createSystemMessage(
							"Launched with a pre-filled prompt — review it before pressing Enter.",
							"warning",
						);
					}
				}
				const initialMessages = deepLinkBanner
					? [deepLinkBanner, ...hookMessages]
					: hookMessages.length > 0
						? hookMessages
						: undefined;

				await launchRepl(
					root,
					{ getFpsMetrics, stats, initialState },
					{
						...sessionConfig,
						initialMessages,
						pendingHookMessages,
					},
					renderAndRun,
				);
			}
		})
		.version(
			`${MACRO.VERSION} (CoStrict)`,
			"-v, --version",
			"Output the version number",
		);

	// Worktree flags
	program.addOption(
		new Option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)").hideHelp(),
	);
	program.addOption(
		new Option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.").hideHelp(),
	);

	if (canUserConfigureAdvisor()) {
		program.addOption(
			new Option(
				"--advisor <model>",
				"Enable the server-side advisor tool with the specified model (alias or full ID).",
			).hideHelp(),
		);
	}

	if (process.env.USER_TYPE === "ant") {
		program.addOption(
			new Option(
				"--delegate-permissions",
				"[ANT-ONLY] Alias for --permission-mode auto.",
			).implies({
				permissionMode: "auto",
			}),
		);
		program.addOption(
			new Option(
				"--dangerously-skip-permissions-with-classifiers",
				"[ANT-ONLY] Deprecated alias for --permission-mode auto.",
			)
				.hideHelp()
				.implies({ permissionMode: "auto" }),
		);
		program.addOption(
			new Option(
				"--afk",
				"[ANT-ONLY] Deprecated alias for --permission-mode auto.",
			)
				.hideHelp()
				.implies({ permissionMode: "auto" }),
		);
		program.addOption(
			new Option(
				"--tasks [id]",
				'[ANT-ONLY] Tasks mode: watch for tasks and auto-process them. Optional id is used as both the task list ID and agent ID (defaults to "tasklist").',
			)
				.argParser(String)
				.hideHelp(),
		);
		program.option(
			"--agent-teams",
			"[ANT-ONLY] Force Claude to use multi-agent mode for solving problems",
			() => true,
		);
	}

	if (feature("TRANSCRIPT_CLASSIFIER")) {
		program.addOption(
			new Option("--enable-auto-mode", "Opt in to auto mode").hideHelp(),
		);
	}

	if (feature("PROACTIVE") || feature("KAIROS")) {
		program.addOption(
			new Option("--proactive", "Start in proactive autonomous mode").hideHelp(),
		);
	}

	if (feature("UDS_INBOX")) {
		program.addOption(
			new Option(
				"--messaging-socket-path <path>",
				"Unix domain socket path for the UDS messaging server (defaults to a tmp path)",
			).hideHelp(),
		);
	}

	if (feature("KAIROS") || feature("KAIROS_BRIEF")) {
		program.addOption(
			new Option(
				"--brief",
				"Enable SendUserMessage tool for agent-to-user communication",
			).hideHelp(),
		);
	}
	if (feature("KAIROS")) {
		program.addOption(
			new Option(
				"--assistant",
				"Force assistant mode (Agent SDK daemon use)",
			).hideHelp(),
		);
	}
	program.addOption(
		new Option(
			"--channels <servers...>",
			"MCP servers whose channel notifications (inbound push) should register this session. Space-separated server names.",
		).hideHelp(),
	);
	program.addOption(
		new Option(
			"--dangerously-load-development-channels <servers...>",
			"Load channel servers not on the approved allowlist. For local channel development only. Shows a confirmation dialog at startup.",
		).hideHelp(),
	);

	// Teammate identity options (set by leader when spawning tmux teammates)
	// These replace the CLAUDE_CODE_* environment variables
	program.addOption(
		new Option("--agent-id <id>", "Teammate agent ID").hideHelp(),
	);
	program.addOption(
		new Option("--agent-name <name>", "Teammate display name").hideHelp(),
	);
	program.addOption(
		new Option(
			"--team-name <name>",
			"Team name for swarm coordination",
		).hideHelp(),
	);
	program.addOption(
		new Option("--agent-color <color>", "Teammate UI color").hideHelp(),
	);
	program.addOption(
		new Option(
			"--plan-mode-required",
			"Require plan mode before implementation",
		).hideHelp(),
	);
	program.addOption(
		new Option(
			"--parent-session-id <id>",
			"Parent session ID for analytics correlation",
		).hideHelp(),
	);
	program.addOption(
		new Option(
			"--teammate-mode <mode>",
			'How to spawn teammates: "tmux", "in-process", or "auto"',
		)
			.choices(["auto", "tmux", "in-process"])
			.hideHelp(),
	);
	program.addOption(
		new Option(
			"--agent-type <type>",
			"Custom agent type for this teammate",
		).hideHelp(),
	);

	// Enable SDK URL for all builds but hide from help
	program.addOption(
		new Option(
			"--sdk-url <url>",
			"Use remote WebSocket endpoint for SDK I/O streaming (only with -p and stream-json format)",
		).hideHelp(),
	);

	// Enable teleport/remote flags for all builds but keep them undocumented until GA
	program.addOption(
		new Option(
			"--teleport [session]",
			"Resume a teleport session, optionally specify session ID",
		).hideHelp(),
	);
	program.addOption(
		new Option(
			"--remote [description]",
			"Create a remote session with the given description",
		).hideHelp(),
	);
	if (feature("BRIDGE_MODE")) {
		program.addOption(
			new Option(
				"--remote-control [name]",
				"Start an interactive session with Remote Control enabled (optionally named)",
			)
				.argParser((value) => value || true)
				.hideHelp(),
		);
		program.addOption(
			new Option("--rc [name]", "Alias for --remote-control")
				.argParser((value) => value || true)
				.hideHelp(),
		);
	}

	if (feature("HARD_FAIL")) {
		program.addOption(
			new Option(
				"--hard-fail",
				"Crash on logError calls instead of silently logging",
			).hideHelp(),
		);
	}

	profileCheckpoint("run_main_options_built");

	// -p/--print mode: skip subcommand registration. The 52 subcommands
	// (mcp, auth, plugin, skill, task, config, doctor, update, etc.) are
	// never dispatched in print mode — commander routes the prompt to the
	// default action. The subcommand registration path was measured at ~65ms
	// on baseline — mostly the isBridgeEnabled() call (25ms settings Zod parse
	// + 40ms sync keychain subprocess), both hidden by the try/catch that
	// always returns false before enableConfigs(). cc:// URLs are rewritten to
	// `open` at main() line ~851 BEFORE this runs, so argv check is safe here.
	const isPrintMode =
		process.argv.includes("-p") || process.argv.includes("--print");
	const isCcUrl = process.argv.some(
		(a) => a.startsWith("cc://") || a.startsWith("cc+unix://"),
	);
	if (isPrintMode && !isCcUrl) {
		profileCheckpoint("run_before_parse");
		await program.parseAsync(process.argv);
		profileCheckpoint("run_after_parse");
		return program;
	}

	// claude mcp

	const mcp = program
		.command("mcp")
		.description("Configure and manage MCP servers")
		.addHelpCommand(false)
		.configureHelp(createSortedHelpConfig())
		.enablePositionalOptions();

	mcp.command("serve")
		.description(`Start the CoStrict MCP server`)
		.option("-d, --debug", "Enable debug mode", () => true)
		.option(
			"--verbose",
			"Override verbose mode setting from config",
			() => true,
		)
		.action(
			async ({
				debug,
				verbose,
			}: {
				debug?: boolean;
				verbose?: boolean;
			}) => {
				const { mcpServeHandler } =
					await import("./cli/handlers/mcp.js");
				await mcpServeHandler({ debug, verbose });
			},
		);

	// Register the mcp add subcommand (extracted for testability)
	registerMcpAddCommand(mcp);

	if (isXaaEnabled()) {
		registerMcpXaaIdpCommand(mcp);
	}

	mcp.command("remove <name>")
		.alias("rm")
		.description("Remove an MCP server")
		.option(
			"-s, --scope <scope>",
			"Configuration scope (local, user, or project) - if not specified, removes from whichever scope it exists in",
		)
		.action(async (name: string, options: { scope?: string }) => {
			const { mcpRemoveHandler } = await import("./cli/handlers/mcp.js");
			await mcpRemoveHandler(name, options);
		});

	mcp.command("list")
		.alias("ls")
		.description("List configured MCP servers")
		.action(async () => {
			const { mcpListHandler } = await import("./cli/handlers/mcp.js");
			await mcpListHandler();
		});

	mcp.command("get <name>")
		.description("Get details about an MCP server")
		.action(async (name: string) => {
			const { mcpGetHandler } = await import("./cli/handlers/mcp.js");
			await mcpGetHandler(name);
		});

	mcp.command("add-json <name> <json>")
		.description("Add an MCP server (stdio or SSE) with a JSON string")
		.option(
			"-s, --scope <scope>",
			"Configuration scope (local, user, or project)",
			"local",
		)
		.option(
			"--client-secret",
			"Prompt for OAuth client secret (or set MCP_CLIENT_SECRET env var)",
		)
		.action(
			async (
				name: string,
				json: string,
				options: { scope?: string; clientSecret?: true },
			) => {
				const { mcpAddJsonHandler } =
					await import("./cli/handlers/mcp.js");
				await mcpAddJsonHandler(name, json, options);
			},
		);

	mcp.command("add-from-claude-desktop")
		.description(
			"Import MCP servers from Claude Desktop (Mac and WSL only)",
		)
		.option(
			"-s, --scope <scope>",
			"Configuration scope (local, user, or project)",
			"local",
		)
		.action(async (options: { scope?: string }) => {
			const { mcpAddFromDesktopHandler } =
				await import("./cli/handlers/mcp.js");
			await mcpAddFromDesktopHandler(options);
		});

	mcp.command("reset-project-choices")
		.description(
			"Reset all approved and rejected project-scoped (.mcp.json) servers within this project",
		)
		.action(async () => {
			const { mcpResetChoicesHandler } =
				await import("./cli/handlers/mcp.js");
			await mcpResetChoicesHandler();
		});

	// csc serve — HTTP API server for IDE plugins and web clients.
	// Also accessible via `csc serve` alias.
	if (feature("DIRECT_CONNECT")) {
		const serveAction = async (opts: {
			port: string;
			host: string;
			authToken?: string;
			unix?: string;
			workspace?: string;
			idleTimeout: string;
			maxSessions: string;
		}) => {
			const { startServer } = await import("./server/server.js");
			const { EventBus } = await import("./server/eventBus.js");
			const { SessionManager } =
				await import("./server/sessionManager.js");
			const { printBanner } =
				await import("./server/serverBanner.js");
			const { createServerLogger } =
				await import("./server/serverLog.js");

			const eventBus = new EventBus();
			const config = {
				port: parseInt(opts.port, 10),
				host: opts.host,
				authToken: undefined,
				unix: opts.unix,
				workspace: opts.workspace,
				idleTimeoutMs: parseInt(opts.idleTimeout, 10),
				maxSessions: parseInt(opts.maxSessions, 10),
			};

			const sessionManager = new SessionManager({
				eventBus,
				idleTimeoutMs: config.idleTimeoutMs,
				maxSessions: config.maxSessions,
				workspace: config.workspace,
			});
			await sessionManager.init();
			const logger = createServerLogger();

			const server = startServer(config, sessionManager);
			const actualPort = server.port ?? config.port;
			printBanner(config, undefined, actualPort);

			let shuttingDown = false;
			const shutdown = async () => {
				if (shuttingDown) return;
				shuttingDown = true;
				server.stop(true);
				eventBus.destroy();
				await sessionManager.destroyAll();
				process.exit(0);
			};
			process.once("SIGINT", () => void shutdown());
			process.once("SIGTERM", () => void shutdown());
		};

		program
			.command("server")
			.description("Start a CoStrict session server (alias: serve)")
			.option("--port <number>", "HTTP port", "0")
			.option("--host <string>", "Bind address", "127.0.0.1")
			.option("--auth-token <token>", "Bearer token for auth (currently disabled)")
			.option("--unix <path>", "Listen on a unix domain socket")
			.option(
				"--workspace <dir>",
				"Default working directory for sessions that do not specify cwd",
			)
			.option(
				"--idle-timeout <ms>",
				"Idle timeout for detached sessions in ms (0 = never expire)",
				"1800000",
			)
			.option(
				"--max-sessions <n>",
				"Maximum concurrent sessions (0 = unlimited)",
				"32",
			)
			.action(serveAction);

		program
			.command("serve")
			.description("Start a CoStrict HTTP API server")
			.option("--port <number>", "HTTP port", "0")
			.option("--host <string>", "Bind address", "127.0.0.1")
			.option("--auth-token <token>", "Bearer token for auth (currently disabled)")
			.option("--unix <path>", "Listen on a unix domain socket")
			.option(
				"--workspace <dir>",
				"Default working directory for sessions that do not specify cwd",
			)
			.option(
				"--idle-timeout <ms>",
				"Idle timeout for detached sessions in ms (0 = never expire)",
				"1800000",
			)
			.option(
				"--max-sessions <n>",
				"Maximum concurrent sessions (0 = unlimited)",
				"32",
			)
			.action(serveAction);
	}

	// `claude ssh <host> [dir]` — registered here only so --help shows it.
	// The actual interactive flow is handled by early argv rewriting in main()
	// (parallels the DIRECT_CONNECT/cc:// pattern above). If commander reaches
	// this action it means the argv rewrite didn't fire (e.g. user ran
	// `claude ssh` with no host) — just print usage.
	if (feature("SSH_REMOTE")) {
		program
			.command("ssh <host> [dir]")
			.description(
				"Run CoStrict on a remote host over SSH. Deploys the binary and " +
					"tunnels API auth back through your local machine — no remote setup needed.",
			)
			.option(
				"--permission-mode <mode>",
				"Permission mode for the remote session",
			)
			.option(
				"--dangerously-skip-permissions",
				"Skip all permission prompts on the remote (dangerous)",
			)
			.option(
				"--remote-bin <command>",
				"Custom remote binary command (skips probe/deploy). " +
					"Example: --remote-bin 'bun /path/to/project/dist/cli.js'",
			)
			.option(
				"--local",
				"e2e test mode — spawn the child CLI locally (skip ssh/deploy). " +
					"Exercises the auth proxy and unix-socket plumbing without a remote host.",
			)
			.action(async () => {
				// Argv rewriting in main() should have consumed `ssh <host>` before
				// commander runs. Reaching here means host was missing or the
				// rewrite predicate didn't match.
				process.stderr.write(
					"Usage: csc ssh <user@host | ssh-config-alias> [dir]\n\n" +
						"Runs CoStrict on a remote Linux host. You don't need to install\n" +
						"anything on the remote or run `csc auth login` there — the binary is\n" +
						"deployed over SSH and API auth tunnels back through your local machine.\n",
				);
				process.exit(1);
			});
	}

	// claude connect — subcommand only handles -p (headless) mode.
	// Interactive mode (without -p) is handled by early argv rewriting in main()
	// which redirects to the main command with full TUI support.
	if (feature("DIRECT_CONNECT")) {
		program
			.command("open <cc-url>")
			.description(
				"Connect to a CoStrict server (internal — use cc:// URLs)",
			)
			.option("-p, --print [prompt]", "Print mode (headless)")
			.option(
				"--output-format <format>",
				"Output format: text, json, stream-json",
				"text",
			)
			.action(
				async (
					ccUrl: string,
					opts: {
						print?: string | true;
						outputFormat?: string;
					},
				) => {
					const { parseConnectUrl } =
						await import("./server/parseConnectUrl.js");
					const { serverUrl, authToken } = parseConnectUrl(ccUrl);

					let connectConfig;
					try {
						const session = await createDirectConnectSession({
							serverUrl,
							authToken,
							cwd: getOriginalCwd(),
							dangerouslySkipPermissions:
								_pendingConnect?.dangerouslySkipPermissions,
						});
						if (session.workDir) {
							setOriginalCwd(session.workDir);
							setCwdState(session.workDir);
						}
						setDirectConnectServerUrl(serverUrl);
						connectConfig = session.config;
					} catch (err) {
						console.error(
							err instanceof DirectConnectError
								? err.message
								: String(err),
						);
						process.exit(1);
					}

					const { runConnectHeadless } =
						await import("./server/connectHeadless.js");

					const prompt =
						typeof opts.print === "string" ? opts.print : "";
					const interactive = opts.print === true;
					await runConnectHeadless(
						connectConfig,
						prompt,
						opts.outputFormat,
						interactive,
					);
				},
			);
	}

	// claude auth

	const auth = program
		.command("auth")
		.description("Manage authentication")
		.addHelpCommand(false)
		.configureHelp(createSortedHelpConfig());

	auth.command("login")
		.description("Sign in to your CoStrict account")
		.option(
			"--email <email>",
			"Pre-populate email address on the login page (Anthropic OAuth only)",
		)
		.option("--sso", "Force SSO login flow (Anthropic OAuth only)")
		.option(
			"--console",
			"Use Anthropic Console (API usage billing) instead of CoStrict",
		)
		.option("--claudeai", "Use Claude subscription instead of CoStrict")
		.action(
			async ({
				email,
				sso,
				console: useConsole,
				claudeai,
			}: {
				email?: string;
				sso?: boolean;
				console?: boolean;
				claudeai?: boolean;
			}) => {
				const { authLogin } = await import("./cli/handlers/auth.js");
				await authLogin({ email, sso, console: useConsole, claudeai });
			},
		);

	auth.command("status")
		.description("Show authentication status")
		.option("--json", "Output as JSON (default)")
		.option("--text", "Output as human-readable text")
		.action(async (opts: { json?: boolean; text?: boolean }) => {
			const { authStatus } = await import("./cli/handlers/auth.js");
			await authStatus(opts);
		});

	auth.command("logout")
		.description("Log out from your CoStrict account")
		.action(async () => {
			const { authLogout } = await import("./cli/handlers/auth.js");
			await authLogout();
		});

	/**
	 * Helper function to handle marketplace command errors consistently.
	 * Logs the error and exits the process with status 1.
	 * @param error The error that occurred
	 * @param action Description of the action that failed
	 */
	// Hidden flag on all plugin/marketplace subcommands to target cowork_plugins.
	const coworkOption = () =>
		new Option("--cowork", "Use cowork_plugins directory").hideHelp();

	// Plugin validate command
	const pluginCmd = program
		.command("plugin")
		.alias("plugins")
		.alias("plug")
		.description("Manage CoStrict plugins")
		.addHelpCommand(false)
		.configureHelp(createSortedHelpConfig());

	pluginCmd
		.command("validate <path>")
		.description("Validate a plugin or marketplace manifest")
		.addOption(coworkOption())
		.action(async (manifestPath: string, options: { cowork?: boolean }) => {
			const { pluginValidateHandler } =
				await import("./cli/handlers/plugins.js");
			await pluginValidateHandler(manifestPath, options);
		});

	// Plugin list command
	pluginCmd
		.command("list")
		.alias("ls")
		.description("List installed plugins")
		.option("--json", "Output as JSON")
		.option(
			"--available",
			"Include available plugins from marketplaces (requires --json)",
		)
		.addOption(coworkOption())
		.action(
			async (options: {
				json?: boolean;
				available?: boolean;
				cowork?: boolean;
			}) => {
				const { pluginListHandler } =
					await import("./cli/handlers/plugins.js");
				await pluginListHandler(options);
			},
		);

	// Marketplace subcommands
	const marketplaceCmd = pluginCmd
		.command("marketplace")
		.description("Manage CoStrict marketplaces")
		.configureHelp(createSortedHelpConfig());

	marketplaceCmd
		.command("add <source>")
		.description("Add a marketplace from a URL, path, or GitHub repo")
		.addOption(coworkOption())
		.option(
			"--sparse <paths...>",
			"Limit checkout to specific directories via git sparse-checkout (for monorepos). Example: --sparse .claude-plugin plugins",
		)
		.option(
			"--scope <scope>",
			"Where to declare the marketplace: user (default), project, or local",
		)
		.action(
			async (
				source: string,
				options: {
					cowork?: boolean;
					sparse?: string[];
					scope?: string;
				},
			) => {
				const { marketplaceAddHandler } =
					await import("./cli/handlers/plugins.js");
				await marketplaceAddHandler(source, options);
			},
		);

	marketplaceCmd
		.command("list")
		.description("List all configured marketplaces")
		.option("--json", "Output as JSON")
		.addOption(coworkOption())
		.action(async (options: { json?: boolean; cowork?: boolean }) => {
			const { marketplaceListHandler } =
				await import("./cli/handlers/plugins.js");
			await marketplaceListHandler(options);
		});

	marketplaceCmd
		.command("remove <name>")
		.alias("rm")
		.description("Remove a configured marketplace")
		.addOption(coworkOption())
		.action(async (name: string, options: { cowork?: boolean }) => {
			const { marketplaceRemoveHandler } =
				await import("./cli/handlers/plugins.js");
			await marketplaceRemoveHandler(name, options);
		});

	marketplaceCmd
		.command("update [name]")
		.description(
			"Update marketplace(s) from their source - updates all if no name specified",
		)
		.addOption(coworkOption())
		.action(
			async (name: string | undefined, options: { cowork?: boolean }) => {
				const { marketplaceUpdateHandler } =
					await import("./cli/handlers/plugins.js");
				await marketplaceUpdateHandler(name, options);
			},
		);

	// Plugin install command
	pluginCmd
		.command("install <plugin>")
		.alias("i")
		.description(
			"Install a plugin from available marketplaces (use plugin@marketplace for specific marketplace)",
		)
		.option(
			"-s, --scope <scope>",
			"Installation scope: user, project, or local",
			"user",
		)
		.addOption(coworkOption())
		.action(
			async (
				plugin: string,
				options: { scope?: string; cowork?: boolean },
			) => {
				const { pluginInstallHandler } =
					await import("./cli/handlers/plugins.js");
				await pluginInstallHandler(plugin, options);
			},
		);

	// Plugin uninstall command
	pluginCmd
		.command("uninstall <plugin>")
		.alias("remove")
		.alias("rm")
		.description("Uninstall an installed plugin")
		.option(
			"-s, --scope <scope>",
			"Uninstall from scope: user, project, or local",
			"user",
		)
		.option(
			"--keep-data",
			"Preserve the plugin's persistent data directory (~/.claude/plugins/data/{id}/)",
		)
		.addOption(coworkOption())
		.action(
			async (
				plugin: string,
				options: {
					scope?: string;
					cowork?: boolean;
					keepData?: boolean;
				},
			) => {
				const { pluginUninstallHandler } =
					await import("./cli/handlers/plugins.js");
				await pluginUninstallHandler(plugin, options);
			},
		);

	// Plugin enable command
	pluginCmd
		.command("enable <plugin>")
		.description("Enable a disabled plugin")
		.option(
			"-s, --scope <scope>",
			`Installation scope: ${VALID_INSTALLABLE_SCOPES.join(", ")} (default: auto-detect)`,
		)
		.addOption(coworkOption())
		.action(
			async (
				plugin: string,
				options: { scope?: string; cowork?: boolean },
			) => {
				const { pluginEnableHandler } =
					await import("./cli/handlers/plugins.js");
				await pluginEnableHandler(plugin, options);
			},
		);

	// Plugin disable command
	pluginCmd
		.command("disable [plugin]")
		.description("Disable an enabled plugin")
		.option("-a, --all", "Disable all enabled plugins")
		.option(
			"-s, --scope <scope>",
			`Installation scope: ${VALID_INSTALLABLE_SCOPES.join(", ")} (default: auto-detect)`,
		)
		.addOption(coworkOption())
		.action(
			async (
				plugin: string | undefined,
				options: { scope?: string; cowork?: boolean; all?: boolean },
			) => {
				const { pluginDisableHandler } =
					await import("./cli/handlers/plugins.js");
				await pluginDisableHandler(plugin, options);
			},
		);

	// Plugin update command
	pluginCmd
		.command("update <plugin>")
		.description(
			"Update a plugin to the latest version (restart required to apply)",
		)
		.option(
			"-s, --scope <scope>",
			`Installation scope: ${VALID_UPDATE_SCOPES.join(", ")} (default: user)`,
		)
		.addOption(coworkOption())
		.action(
			async (
				plugin: string,
				options: { scope?: string; cowork?: boolean },
			) => {
				const { pluginUpdateHandler } =
					await import("./cli/handlers/plugins.js");
				await pluginUpdateHandler(plugin, options);
			},
		);
	// END ANT-ONLY

  // Setup token command
  program
    .command('setup-token')
    .description(
      'Set up a long-lived authentication token (requires Claude subscription)',
    )
    .action(async () => {
      const [{ setupTokenHandler }, { createRoot }] = await Promise.all([
        import('./cli/handlers/util.js'),
        import('@anthropic/ink'),
      ])
      const root = await createRoot(getBaseRenderOptions(false))
      await setupTokenHandler(root)
    })

	// Agents command - list configured agents
	program
		.command("agents")
		.description("List configured agents")
		.option(
			"--setting-sources <sources>",
			"Comma-separated list of setting sources to load (user, project, local).",
		)
		.action(async () => {
			const { agentsHandler } = await import("./cli/handlers/agents.js");
			await agentsHandler();
			process.exit(0);
		});

	if (feature("TRANSCRIPT_CLASSIFIER")) {
		// Skip when tengu_auto_mode_config.enabled === 'disabled' (circuit breaker).
		// Reads from disk cache — GrowthBook isn't initialized at registration time.
		if (getAutoModeEnabledStateIfCached() !== "disabled") {
			const autoModeCmd = program
				.command("auto-mode", { hidden: true })
				.description("Inspect auto mode classifier configuration");

			autoModeCmd
				.command("defaults")
				.description(
					"Print the default auto mode environment, allow, and deny rules as JSON",
				)
				.action(async () => {
					const { autoModeDefaultsHandler } =
						await import("./cli/handlers/autoMode.js");
					autoModeDefaultsHandler();
					process.exit(0);
				});

			autoModeCmd
				.command("config")
				.description(
					"Print the effective auto mode config as JSON: your settings where set, defaults otherwise",
				)
				.action(async () => {
					const { autoModeConfigHandler } =
						await import("./cli/handlers/autoMode.js");
					autoModeConfigHandler();
					process.exit(0);
				});

			autoModeCmd
				.command("critique")
				.description("Get AI feedback on your custom auto mode rules")
				.option("--model <model>", "Override which model is used")
				.action(async (options) => {
					const { autoModeCritiqueHandler } =
						await import("./cli/handlers/autoMode.js");
					await autoModeCritiqueHandler(options);
					process.exit();
				});
		}
	}

	// claude autonomy — CLI subcommands mirroring /autonomy slash command
	{
		const autonomyCmd = program
			.command("autonomy")
			.description("Inspect and manage automatic autonomy runs and flows");

		autonomyCmd
			.command("status")
			.description("Print autonomy run, flow, team, pipe, and remote-control status")
			.option("--deep", "Include teams, pipes, daemon, and remote-control sections")
			.action(async (options: { deep?: boolean }) => {
				const { autonomyStatusHandler } = await import("./cli/handlers/autonomy.js");
				await autonomyStatusHandler(options);
				process.exit(0);
			});

		autonomyCmd
			.command("runs [limit]")
			.description("List recent autonomy runs")
			.action(async (limit?: string) => {
				const { autonomyRunsHandler } = await import("./cli/handlers/autonomy.js");
				await autonomyRunsHandler(limit);
				process.exit(0);
			});

		autonomyCmd
			.command("flows [limit]")
			.description("List recent autonomy flows")
			.action(async (limit?: string) => {
				const { autonomyFlowsHandler } = await import("./cli/handlers/autonomy.js");
				await autonomyFlowsHandler(limit);
				process.exit(0);
			});

		const flowCmd = autonomyCmd
			.command("flow <flowId>")
			.description("Inspect a single autonomy flow")
			.action(async (flowId: string) => {
				const { autonomyFlowHandler } = await import("./cli/handlers/autonomy.js");
				await autonomyFlowHandler(flowId);
				process.exit(0);
			});

		flowCmd
			.command("cancel <flowId>")
			.description("Cancel a queued, waiting, or running autonomy flow")
			.action(async (flowId: string) => {
				const { autonomyFlowCancelHandler } = await import("./cli/handlers/autonomy.js");
				await autonomyFlowCancelHandler(flowId);
				process.exit(0);
			});

		flowCmd
			.command("resume <flowId>")
			.description("Resume a waiting autonomy flow")
			.action(async (flowId: string) => {
				const { autonomyFlowResumeHandler } = await import("./cli/handlers/autonomy.js");
				await autonomyFlowResumeHandler(flowId);
				process.exit(0);
			});
	}

	// Remote Control command — connect local environment to claude.ai/code.
	// The actual command is intercepted by the fast-path in cli.tsx before
	// Commander.js runs, so this registration exists only for help output.
	// Always hidden: isBridgeEnabled() at this point (before enableConfigs)
	// would throw inside isClaudeAISubscriber → getGlobalConfig and return
	// false via the try/catch — but not before paying ~65ms of side effects
	// (25ms settings Zod parse + 40ms sync `security` keychain subprocess).
	// The dynamic visibility never worked; the command was always hidden.
	if (feature("BRIDGE_MODE")) {
		program
			.command("remote-control", { hidden: true })
			.alias("rc")
			.description(
				"Connect your local environment for remote-control sessions via costrict.ai",
			)
			.action(async () => {
				// Unreachable — cli.tsx fast-path handles this command before main.tsx loads.
				// If somehow reached, delegate to bridgeMain.
				const { bridgeMain } = await import("./bridge/bridgeMain.js");
				await bridgeMain(process.argv.slice(3));
			});
	}

	if (feature("KAIROS")) {
		program
			.command("assistant [sessionId]", { hidden: true })
			.description(
				"Attach the REPL as a client to a running bridge session. Discovers sessions via API if no sessionId given.",
			)
			.action(() => {
				// Argv rewriting above should have consumed `assistant [id]`
				// before commander runs. Reaching here means a root flag came first
				// (e.g. `--debug assistant`) and the position-0 predicate
				// didn't match. Print usage like the ssh stub does.
				process.stderr.write(
					"Usage: csc assistant [sessionId]\n\n" +
						"Attach the REPL as a viewer client to a running bridge session.\n" +
						"Omit sessionId to discover and pick from available sessions.\n",
				);
				process.exit(1);
			});
	}

  // Update command - check and install updates
  program
    .command('update')
    .alias('upgrade')
    .description('Check for updates and install the latest version')
    .action(async () => {
      const { update } = await import('./cli/update.js')
      await update()
    })

  // Doctor command - check installation health
  program
    .command('doctor')
    .description(
      'Check the health of your CoStrict auto-updater',
    )
    .action(async () => {
      const [{ doctorHandler }, { createRoot }] = await Promise.all([
        import('./cli/handlers/util.js'),
        import('@anthropic/ink'),
      ])
      const root = await createRoot(getBaseRenderOptions(false))
      await doctorHandler(root)
    })


	// claude up — run the project's CLAUDE.md "# claude up" setup instructions.
	if (process.env.USER_TYPE === "ant") {
		program
			.command("up")
			.description(
				'[ANT-ONLY] Initialize or upgrade the local dev environment using the "# claude up" section of the nearest CLAUDE.md',
			)
			.action(async () => {
				const { up } = await import("src/cli/up.js");
				await up();
			});
	}

	// claude rollback (ant-only)
	// Rolls back to previous releases
	if (process.env.USER_TYPE === "ant") {
		program
			.command("rollback [target]")
			.description(
				"[ANT-ONLY] Roll back to a previous release\n\nExamples:\n  claude rollback                                    Go 1 version back from current\n  claude rollback 3                                  Go 3 versions back from current\n  claude rollback 2.0.73-dev.20251217.t190658        Roll back to a specific version",
			)
			.option("-l, --list", "List recent published versions with ages")
			.option(
				"--dry-run",
				"Show what would be installed without installing",
			)
			.option(
				"--safe",
				"Roll back to the server-pinned safe version (set by oncall during incidents)",
			)
			.action(
				async (
					target?: string,
					options?: {
						list?: boolean;
						dryRun?: boolean;
						safe?: boolean;
					},
				) => {
					const { rollback } = await import("src/cli/rollback.js");
					await rollback(target, options);
				},
			);
	}

	// claude install
	program
		.command("install [target]")
		.description(
			"Install CoStrict native build. Use [target] to specify version (stable, latest, or specific version)",
		)
		.option("--force", "Force installation even if already installed")
		.action(
			async (
				target: string | undefined,
				options: { force?: boolean },
			) => {
				const { installHandler } =
					await import("./cli/handlers/util.js");
				await installHandler(target, options);
			},
		);


	// ant-only commands
	if (process.env.USER_TYPE === "ant") {
		const validateLogId = (value: string) => {
			const maybeSessionId = validateUuid(value);
			if (maybeSessionId) return maybeSessionId;
			return Number(value);
		};
		// claude log
		program
			.command("log")
			.description("[ANT-ONLY] Manage conversation logs.")
			.argument(
				"[number|sessionId]",
				"A number (0, 1, 2, etc.) to display a specific log, or the sesssion ID (uuid) of a log",
				validateLogId,
			)
			.action(async (logId: string | number | undefined) => {
				const { logHandler } = await import("./cli/handlers/ant.js");
				await logHandler(logId);
			});

		// claude error
		program
			.command("error")
			.description(
				"[ANT-ONLY] View error logs. Optionally provide a number (0, -1, -2, etc.) to display a specific log.",
			)
			.argument(
				"[number]",
				"A number (0, 1, 2, etc.) to display a specific log",
				parseInt,
			)
			.action(async (number: number | undefined) => {
				const { errorHandler } = await import("./cli/handlers/ant.js");
				await errorHandler(number);
			});

		// claude export
		program
			.command("export")
			.description("[ANT-ONLY] Export a conversation to a text file.")
			.usage("<source> <outputFile>")
			.argument(
				"<source>",
				"Session ID, log index (0, 1, 2...), or path to a .json/.jsonl log file",
			)
			.argument("<outputFile>", "Output file path for the exported text")
			.addHelpText(
				"after",
				`
Examples:
  $ claude export 0 conversation.txt                Export conversation at log index 0
  $ claude export <uuid> conversation.txt           Export conversation by session ID
  $ claude export input.json output.txt             Render JSON log file to text
  $ claude export <uuid>.jsonl output.txt           Render JSONL session file to text`,
			)
			.action(async (source: string, outputFile: string) => {
				const { exportHandler } = await import("./cli/handlers/ant.js");
				await exportHandler(source, outputFile);
			});

		if (process.env.USER_TYPE === "ant") {
			const taskCmd = program
				.command("task")
				.description("[ANT-ONLY] Manage task list tasks");

			taskCmd
				.command("create <subject>")
				.description("Create a new task")
				.option("-d, --description <text>", "Task description")
				.option(
					"-l, --list <id>",
					'Task list ID (defaults to "tasklist")',
				)
				.action(
					async (
						subject: string,
						opts: { description?: string; list?: string },
					) => {
						const { taskCreateHandler } =
							await import("./cli/handlers/ant.js");
						await taskCreateHandler(subject, opts);
					},
				);

			taskCmd
				.command("list")
				.description("List all tasks")
				.option(
					"-l, --list <id>",
					'Task list ID (defaults to "tasklist")',
				)
				.option("--pending", "Show only pending tasks")
				.option("--json", "Output as JSON")
				.action(
					async (opts: {
						list?: string;
						pending?: boolean;
						json?: boolean;
					}) => {
						const { taskListHandler } =
							await import("./cli/handlers/ant.js");
						await taskListHandler(opts);
					},
				);

			taskCmd
				.command("get <id>")
				.description("Get details of a task")
				.option(
					"-l, --list <id>",
					'Task list ID (defaults to "tasklist")',
				)
				.action(async (id: string, opts: { list?: string }) => {
					const { taskGetHandler } =
						await import("./cli/handlers/ant.js");
					await taskGetHandler(id, opts);
				});

			taskCmd
				.command("update <id>")
				.description("Update a task")
				.option(
					"-l, --list <id>",
					'Task list ID (defaults to "tasklist")',
				)
				.option(
					"-s, --status <status>",
					`Set status (${TASK_STATUSES.join(", ")})`,
				)
				.option("--subject <text>", "Update subject")
				.option("-d, --description <text>", "Update description")
				.option("--owner <agentId>", "Set owner")
				.option("--clear-owner", "Clear owner")
				.action(
					async (
						id: string,
						opts: {
							list?: string;
							status?: string;
							subject?: string;
							description?: string;
							owner?: string;
							clearOwner?: boolean;
						},
					) => {
						const { taskUpdateHandler } =
							await import("./cli/handlers/ant.js");
						await taskUpdateHandler(id, opts);
					},
				);

			taskCmd
				.command("dir")
				.description("Show the tasks directory path")
				.option(
					"-l, --list <id>",
					'Task list ID (defaults to "tasklist")',
				)
				.action(async (opts: { list?: string }) => {
					const { taskDirHandler } =
						await import("./cli/handlers/ant.js");
					await taskDirHandler(opts);
				});
		}

		// claude completion <shell>
		program
			.command("completion <shell>", { hidden: true })
			.description(
				"Generate shell completion script (bash, zsh, or fish)",
			)
			.option(
				"--output <file>",
				"Write completion script directly to a file instead of stdout",
			)
			.action(async (shell: string, opts: { output?: string }) => {
				const { completionHandler } =
					await import("./cli/handlers/ant.js");
				await completionHandler(shell, opts, program);
			});
	}

	profileCheckpoint("run_before_parse");
	await program.parseAsync(process.argv);
	profileCheckpoint("run_after_parse");

	// Record final checkpoint for total_time calculation
	profileCheckpoint("main_after_run");

	// Log startup perf to Statsig (sampled) and output detailed report if enabled
	profileReport();

	return program;
}

async function logTenguInit({
	hasInitialPrompt,
	hasStdin,
	verbose,
	debug,
	debugToStderr,
	print,
	outputFormat,
	inputFormat,
	numAllowedTools,
	numDisallowedTools,
	mcpClientCount,
	worktreeEnabled,
	skipWebFetchPreflight,
	githubActionInputs,
	dangerouslySkipPermissionsPassed,
	permissionMode,
	modeIsBypass,
	allowDangerouslySkipPermissionsPassed,
	systemPromptFlag,
	appendSystemPromptFlag,
	thinkingConfig,
	assistantActivationPath,
}: {
	hasInitialPrompt: boolean;
	hasStdin: boolean;
	verbose: boolean;
	debug: boolean;
	debugToStderr: boolean;
	print: boolean;
	outputFormat: string;
	inputFormat: string;
	numAllowedTools: number;
	numDisallowedTools: number;
	mcpClientCount: number;
	worktreeEnabled: boolean;
	skipWebFetchPreflight: boolean | undefined;
	githubActionInputs: string | undefined;
	dangerouslySkipPermissionsPassed: boolean;
	permissionMode: string;
	modeIsBypass: boolean;
	allowDangerouslySkipPermissionsPassed: boolean;
	systemPromptFlag: "file" | "flag" | undefined;
	appendSystemPromptFlag: "file" | "flag" | undefined;
	thinkingConfig: ThinkingConfig;
	assistantActivationPath: string | undefined;
}): Promise<void> {
	try {
		logEvent("tengu_init", {
			entrypoint:
				"claude" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			hasInitialPrompt,
			hasStdin,
			verbose,
			debug,
			debugToStderr,
			print,
			outputFormat:
				outputFormat as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			inputFormat:
				inputFormat as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			numAllowedTools,
			numDisallowedTools,
			mcpClientCount,
			worktree: worktreeEnabled,
			skipWebFetchPreflight,
			...(githubActionInputs && {
				githubActionInputs:
					githubActionInputs as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			}),
			dangerouslySkipPermissionsPassed,
			permissionMode:
				permissionMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			modeIsBypass,
			inProtectedNamespace: isInProtectedNamespace(),
			allowDangerouslySkipPermissionsPassed,
			thinkingType:
				thinkingConfig.type as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			...(thinkingConfig.type === "enabled" && {
				thinkingBudgetTokens: thinkingConfig.budgetTokens,
			}),
			...(systemPromptFlag && {
				systemPromptFlag:
					systemPromptFlag as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			}),
			...(appendSystemPromptFlag && {
				appendSystemPromptFlag:
					appendSystemPromptFlag as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			}),
			is_simple: isBareMode() || undefined,
			is_coordinator:
				feature("COORDINATOR_MODE") &&
				coordinatorModeModule?.isCoordinatorMode()
					? true
					: undefined,
			...(assistantActivationPath && {
				assistantActivationPath:
					assistantActivationPath as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			}),
			autoUpdatesChannel: (getInitialSettings().autoUpdatesChannel ??
				"latest") as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
			...(process.env.USER_TYPE === "ant"
				? (() => {
						const cwd = getCwd();
						const gitRoot = findGitRoot(cwd);
						const rp = gitRoot
							? relative(gitRoot, cwd) || "."
							: undefined;
						return rp
							? {
									relativeProjectPath:
										rp as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
								}
							: {};
					})()
				: {}),
		});
	} catch (error) {
		logError(error);
	}
}

function maybeActivateProactive(options: unknown): void {
	if (
		(feature("PROACTIVE") || feature("KAIROS")) &&
		((options as { proactive?: boolean }).proactive ||
			isEnvTruthy(process.env.CLAUDE_CODE_PROACTIVE))
	) {
		// eslint-disable-next-line @typescript-eslint/no-require-imports
		const proactiveModule = require("./proactive/index.js");
		if (!proactiveModule.isProactiveActive()) {
			proactiveModule.activateProactive("command");
		}
	}
}

function maybeActivateBrief(options: unknown): void {
	if (!(feature("KAIROS") || feature("KAIROS_BRIEF"))) return;
	const briefFlag = (options as { brief?: boolean }).brief;
	const briefEnv = isEnvTruthy(process.env.CLAUDE_CODE_BRIEF);
	if (!briefFlag && !briefEnv) return;
	// --brief / CLAUDE_CODE_BRIEF are explicit opt-ins: check entitlement,
	// then set userMsgOptIn to activate the tool + prompt section. The env
	// var also grants entitlement (isBriefEntitled() reads it), so setting
	// CLAUDE_CODE_BRIEF=1 alone force-enables for dev/testing — no GB gate
	// needed. initialIsBriefOnly reads getUserMsgOptIn() directly.
	// Conditional require: static import would leak the tool name string
	// into external builds via BriefTool.ts → prompt.ts.
	/* eslint-disable @typescript-eslint/no-require-imports */
	const { isBriefEntitled } =
		require("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js") as typeof import("@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js");
	/* eslint-enable @typescript-eslint/no-require-imports */
	const entitled = isBriefEntitled();
	if (entitled) {
		setUserMsgOptIn(true);
	}
	// Fire unconditionally once intent is seen: enabled=false captures the
	// "user tried but was gated" failure mode in Datadog.
	logEvent("tengu_brief_mode_enabled", {
		enabled: entitled,
		gated: !entitled,
		source: (briefEnv
			? "env"
			: "flag") as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
	});
}

function resetCursor() {
	const terminal = process.stderr.isTTY
		? process.stderr
		: process.stdout.isTTY
			? process.stdout
			: undefined;
	terminal?.write(SHOW_CURSOR);
}

type TeammateOptions = {
	agentId?: string;
	agentName?: string;
	teamName?: string;
	agentColor?: string;
	planModeRequired?: boolean;
	parentSessionId?: string;
	teammateMode?: "auto" | "tmux" | "in-process";
	agentType?: string;
};

function extractTeammateOptions(options: unknown): TeammateOptions {
	if (typeof options !== "object" || options === null) {
		return {};
	}
	const opts = options as Record<string, unknown>;
	const teammateMode = opts.teammateMode;
	return {
		agentId: typeof opts.agentId === "string" ? opts.agentId : undefined,
		agentName:
			typeof opts.agentName === "string" ? opts.agentName : undefined,
		teamName: typeof opts.teamName === "string" ? opts.teamName : undefined,
		agentColor:
			typeof opts.agentColor === "string" ? opts.agentColor : undefined,
		planModeRequired:
			typeof opts.planModeRequired === "boolean"
				? opts.planModeRequired
				: undefined,
		parentSessionId:
			typeof opts.parentSessionId === "string"
				? opts.parentSessionId
				: undefined,
		teammateMode:
			teammateMode === "auto" ||
			teammateMode === "tmux" ||
			teammateMode === "in-process"
				? teammateMode
				: undefined,
		agentType:
			typeof opts.agentType === "string" ? opts.agentType : undefined,
	};
}
