()
| 1064 | } |
| 1065 | |
| 1066 | async function run(): Promise<CommanderCommand> { |
| 1067 | profileCheckpoint('run_function_start'); |
| 1068 | |
| 1069 | // Create help config that sorts options by long option name. |
| 1070 | // Commander supports compareOptions at runtime but @commander-js/extra-typings |
| 1071 | // doesn't include it in the type definitions, so we use Object.assign to add it. |
| 1072 | function createSortedHelpConfig(): { |
| 1073 | sortSubcommands: true; |
| 1074 | sortOptions: true; |
| 1075 | } { |
| 1076 | const getOptionSortKey = (opt: Option): string => |
| 1077 | opt.long?.replace(/^--/, '') ?? opt.short?.replace(/^-/, '') ?? ''; |
| 1078 | return Object.assign({ sortSubcommands: true, sortOptions: true } as const, { |
| 1079 | compareOptions: (a: Option, b: Option) => getOptionSortKey(a).localeCompare(getOptionSortKey(b)), |
| 1080 | }); |
| 1081 | } |
| 1082 | const program = new CommanderCommand().configureHelp(createSortedHelpConfig()).enablePositionalOptions(); |
| 1083 | profileCheckpoint('run_commander_initialized'); |
| 1084 | |
| 1085 | // Use preAction hook to run initialization only when executing a command, |
| 1086 | // not when displaying help. This avoids the need for env variable signaling. |
| 1087 | program.hook('preAction', async thisCommand => { |
| 1088 | profileCheckpoint('preAction_start'); |
| 1089 | // Await async subprocess loads started at module evaluation (lines 12-20). |
| 1090 | // Nearly free — subprocesses complete during the ~135ms of imports above. |
| 1091 | // Must resolve before init() which triggers the first settings read |
| 1092 | // (applySafeConfigEnvironmentVariables → getSettingsForSource('policySettings') |
| 1093 | // → isRemoteManagedSettingsEligible → sync keychain reads otherwise ~65ms). |
| 1094 | await Promise.all([ensureMdmSettingsLoaded(), ensureKeychainPrefetchCompleted()]); |
| 1095 | profileCheckpoint('preAction_after_mdm'); |
| 1096 | await init(); |
| 1097 | profileCheckpoint('preAction_after_init'); |
| 1098 | |
| 1099 | // process.title on Windows sets the console title directly; on POSIX, |
| 1100 | // terminal shell integration may mirror the process name to the tab. |
| 1101 | // After init() so settings.json env can also gate this (gh-4765). |
| 1102 | if (!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE)) { |
| 1103 | process.title = 'claude'; |
| 1104 | } |
| 1105 | |
| 1106 | // Attach logging sinks so subcommand handlers can use logEvent/logError. |
| 1107 | // Before PR #11106 logEvent dispatched directly; after, events queue until |
| 1108 | // a sink attaches. setup() attaches sinks for the default command, but |
| 1109 | // subcommands (doctor, mcp, plugin, auth) never call setup() and would |
| 1110 | // silently drop events on process.exit(). Both inits are idempotent. |
| 1111 | const { initSinks } = await import('./utils/sinks.js'); |
| 1112 | initSinks(); |
| 1113 | profileCheckpoint('preAction_after_sinks'); |
| 1114 | |
| 1115 | // gh-33508: --plugin-dir is a top-level program option. The default |
| 1116 | // action reads it from its own options destructure, but subcommands |
| 1117 | // (plugin list, plugin install, mcp *) have their own actions and |
| 1118 | // never see it. Wire it up here so getInlinePlugins() works everywhere. |
| 1119 | // thisCommand.opts() is typed {} here because this hook is attached |
| 1120 | // before .option('--plugin-dir', ...) in the chain — extra-typings |
| 1121 | // builds the type as options are added. Narrow with a runtime guard; |
| 1122 | // the collect accumulator + [] default guarantee string[] in practice. |
| 1123 | const pluginDir = thisCommand.getOptionValue('pluginDir'); |
no test coverage detected