()
| 944 | return prompt; |
| 945 | } |
| 946 | async function run(): Promise<CommanderCommand> { |
| 947 | profileCheckpoint('run_function_start'); |
| 948 | |
| 949 | // Create help config that sorts options by long option name. |
| 950 | // Commander supports compareOptions at runtime but @commander-js/extra-typings |
| 951 | // doesn't include it in the type definitions, so we use Object.assign to add it. |
| 952 | function createSortedHelpConfig(): { |
| 953 | sortSubcommands: true; |
| 954 | sortOptions: true; |
| 955 | } { |
| 956 | const getOptionSortKey = (opt: Option): string => opt.long?.replace(/^--/, '') ?? opt.short?.replace(/^-/, '') ?? ''; |
| 957 | return Object.assign({ |
| 958 | sortSubcommands: true, |
| 959 | sortOptions: true |
| 960 | } as const, { |
| 961 | compareOptions: (a: Option, b: Option) => getOptionSortKey(a).localeCompare(getOptionSortKey(b)) |
| 962 | }); |
| 963 | } |
| 964 | const program = new CommanderCommand().configureHelp(createSortedHelpConfig()).enablePositionalOptions(); |
| 965 | profileCheckpoint('run_commander_initialized'); |
| 966 | |
| 967 | // Use preAction hook to run initialization only when executing a command, |
| 968 | // not when displaying help. This avoids the need for env variable signaling. |
| 969 | program.hook('preAction', async thisCommand => { |
| 970 | profileCheckpoint('preAction_start'); |
| 971 | // Await async subprocess loads started at module evaluation (lines 12-20). |
| 972 | // Nearly free — subprocesses complete during the ~135ms of imports above. |
| 973 | // Must resolve before init() which triggers the first settings read |
| 974 | // (applySafeConfigEnvironmentVariables → getSettingsForSource('policySettings') |
| 975 | // → isRemoteManagedSettingsEligible → sync keychain reads otherwise ~65ms). |
| 976 | await Promise.all([ensureMdmSettingsLoaded(), ensureKeychainPrefetchCompleted()]); |
| 977 | profileCheckpoint('preAction_after_mdm'); |
| 978 | await init(); |
| 979 | profileCheckpoint('preAction_after_init'); |
| 980 | |
| 981 | // process.title on Windows sets the console title directly; on POSIX, |
| 982 | // terminal shell integration may mirror the process name to the tab. |
| 983 | // After init() so settings.json env can also gate this (gh-4765). |
| 984 | if (!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE)) { |
| 985 | process.title = 'claude'; |
| 986 | } |
| 987 | |
| 988 | // Attach logging sinks so subcommand handlers can use logEvent/logError. |
| 989 | // Before PR #11106 logEvent dispatched directly; after, events queue until |
| 990 | // a sink attaches. setup() attaches sinks for the default command, but |
| 991 | // subcommands (doctor, mcp, plugin, auth) never call setup() and would |
| 992 | // silently drop events on process.exit(). Both inits are idempotent. |
| 993 | const { |
| 994 | initSinks |
| 995 | } = await import('./utils/sinks.js'); |
| 996 | initSinks(); |
| 997 | profileCheckpoint('preAction_after_sinks'); |
| 998 | |
| 999 | // gh-33508: --plugin-dir is a top-level program option. The default |
| 1000 | // action reads it from its own options destructure, but subcommands |
| 1001 | // (plugin list, plugin install, mcp *) have their own actions and |
| 1002 | // never see it. Wire it up here so getInlinePlugins() works everywhere. |
| 1003 | // thisCommand.opts() is typed {} here because this hook is attached |
no test coverage detected