()
| 882 | return prompt; |
| 883 | } |
| 884 | async function run(): Promise<CommanderCommand> { |
| 885 | profileCheckpoint('run_function_start'); |
| 886 | |
| 887 | // Create help config that sorts options by long option name. |
| 888 | // Commander supports compareOptions at runtime but @commander-js/extra-typings |
| 889 | // doesn't include it in the type definitions, so we use Object.assign to add it. |
| 890 | function createSortedHelpConfig(): { |
| 891 | sortSubcommands: true; |
| 892 | sortOptions: true; |
| 893 | } { |
| 894 | const getOptionSortKey = (opt: Option): string => opt.long?.replace(/^--/, '') ?? opt.short?.replace(/^-/, '') ?? ''; |
| 895 | return Object.assign({ |
| 896 | sortSubcommands: true, |
| 897 | sortOptions: true |
| 898 | } as const, { |
| 899 | compareOptions: (a: Option, b: Option) => getOptionSortKey(a).localeCompare(getOptionSortKey(b)) |
| 900 | }); |
| 901 | } |
| 902 | const program = new CommanderCommand().configureHelp(createSortedHelpConfig()).enablePositionalOptions(); |
| 903 | profileCheckpoint('run_commander_initialized'); |
| 904 | |
| 905 | // Use preAction hook to run initialization only when executing a command, |
| 906 | // not when displaying help. This avoids the need for env variable signaling. |
| 907 | program.hook('preAction', async thisCommand => { |
| 908 | profileCheckpoint('preAction_start'); |
| 909 | // Await async subprocess loads started at module evaluation (lines 12-20). |
| 910 | // Nearly free — subprocesses complete during the ~135ms of imports above. |
| 911 | // Must resolve before init() which triggers the first settings read |
| 912 | // (applySafeConfigEnvironmentVariables → getSettingsForSource('policySettings') |
| 913 | // → isRemoteManagedSettingsEligible → sync keychain reads otherwise ~65ms). |
| 914 | await Promise.all([ensureMdmSettingsLoaded(), ensureKeychainPrefetchCompleted()]); |
| 915 | profileCheckpoint('preAction_after_mdm'); |
| 916 | await init(); |
| 917 | profileCheckpoint('preAction_after_init'); |
| 918 | |
| 919 | // process.title on Windows sets the console title directly; on POSIX, |
| 920 | // terminal shell integration may mirror the process name to the tab. |
| 921 | // After init() so settings.json env can also gate this (gh-4765). |
| 922 | if (!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE)) { |
| 923 | process.title = 'claude'; |
| 924 | } |
| 925 | |
| 926 | // Attach logging sinks so subcommand handlers can use logEvent/logError. |
| 927 | // Before PR #11106 logEvent dispatched directly; after, events queue until |
| 928 | // a sink attaches. setup() attaches sinks for the default command, but |
| 929 | // subcommands (doctor, mcp, plugin, auth) never call setup() and would |
| 930 | // silently drop events on process.exit(). Both inits are idempotent. |
| 931 | const { |
| 932 | initSinks |
| 933 | } = await import('./utils/sinks.js'); |
| 934 | initSinks(); |
| 935 | profileCheckpoint('preAction_after_sinks'); |
| 936 | |
| 937 | // gh-33508: --plugin-dir is a top-level program option. The default |
| 938 | // action reads it from its own options destructure, but subcommands |
| 939 | // (plugin list, plugin install, mcp *) have their own actions and |
| 940 | // never see it. Wire it up here so getInlinePlugins() works everywhere. |
| 941 | // thisCommand.opts() is typed {} here because this hook is attached |
no test coverage detected