@vybestack/llxprt-code 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/README.md +0 -244
  2. package/dist/package.json +15 -6
  3. package/dist/src/auth/gemini-oauth-provider.d.ts +18 -0
  4. package/dist/src/auth/gemini-oauth-provider.js +140 -78
  5. package/dist/src/auth/gemini-oauth-provider.js.map +1 -1
  6. package/dist/src/auth/local-oauth-callback.spec.js +29 -2
  7. package/dist/src/auth/local-oauth-callback.spec.js.map +1 -1
  8. package/dist/src/commands/extensions/disable.d.ts +1 -2
  9. package/dist/src/commands/extensions/disable.js +15 -3
  10. package/dist/src/commands/extensions/disable.js.map +1 -1
  11. package/dist/src/commands/extensions/enable.d.ts +1 -2
  12. package/dist/src/commands/extensions/enable.js +17 -6
  13. package/dist/src/commands/extensions/enable.js.map +1 -1
  14. package/dist/src/commands/extensions/install.d.ts +3 -1
  15. package/dist/src/commands/extensions/install.js +63 -12
  16. package/dist/src/commands/extensions/install.js.map +1 -1
  17. package/dist/src/commands/extensions/install.test.js +166 -13
  18. package/dist/src/commands/extensions/install.test.js.map +1 -1
  19. package/dist/src/commands/extensions/link.js +2 -2
  20. package/dist/src/commands/extensions/link.js.map +1 -1
  21. package/dist/src/commands/extensions/list.d.ts +1 -1
  22. package/dist/src/commands/extensions/list.js +3 -2
  23. package/dist/src/commands/extensions/list.js.map +1 -1
  24. package/dist/src/commands/extensions/new.test.js +2 -2
  25. package/dist/src/commands/extensions/new.test.js.map +1 -1
  26. package/dist/src/commands/extensions/uninstall.js +1 -1
  27. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  28. package/dist/src/commands/extensions/uninstall.test.js +4 -1
  29. package/dist/src/commands/extensions/uninstall.test.js.map +1 -1
  30. package/dist/src/commands/extensions/update.d.ts +2 -2
  31. package/dist/src/commands/extensions/update.js +68 -19
  32. package/dist/src/commands/extensions/update.js.map +1 -1
  33. package/dist/src/commands/mcp/add.js +6 -1
  34. package/dist/src/commands/mcp/add.js.map +1 -1
  35. package/dist/src/commands/mcp/list.js +3 -2
  36. package/dist/src/commands/mcp/list.js.map +1 -1
  37. package/dist/src/config/__tests__/profileBootstrap.test.js +1351 -1
  38. package/dist/src/config/__tests__/profileBootstrap.test.js.map +1 -1
  39. package/dist/src/config/config.d.ts +4 -1
  40. package/dist/src/config/config.js +172 -69
  41. package/dist/src/config/config.js.map +1 -1
  42. package/dist/src/config/config.loadMemory.test.js +6 -3
  43. package/dist/src/config/config.loadMemory.test.js.map +1 -1
  44. package/dist/src/config/extension.d.ts +48 -14
  45. package/dist/src/config/extension.js +263 -142
  46. package/dist/src/config/extension.js.map +1 -1
  47. package/dist/src/config/extensions/extensionEnablement.d.ts +47 -0
  48. package/dist/src/config/extensions/extensionEnablement.js +188 -0
  49. package/dist/src/config/extensions/extensionEnablement.js.map +1 -0
  50. package/dist/src/config/extensions/extensionEnablement.test.d.ts +6 -0
  51. package/dist/src/config/extensions/extensionEnablement.test.js +333 -0
  52. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -0
  53. package/dist/src/config/extensions/github.d.ts +37 -0
  54. package/dist/src/config/extensions/github.js +348 -0
  55. package/dist/src/config/extensions/github.js.map +1 -0
  56. package/dist/src/config/extensions/github.test.d.ts +6 -0
  57. package/dist/src/config/extensions/github.test.js +340 -0
  58. package/dist/src/config/extensions/github.test.js.map +1 -0
  59. package/dist/src/config/extensions/update.d.ts +19 -0
  60. package/dist/src/config/extensions/update.js +118 -0
  61. package/dist/src/config/extensions/update.js.map +1 -0
  62. package/dist/src/config/extensions/update.test.d.ts +6 -0
  63. package/dist/src/config/extensions/update.test.js +340 -0
  64. package/dist/src/config/extensions/update.test.js.map +1 -0
  65. package/dist/src/config/extensions/variableSchema.d.ts +8 -0
  66. package/dist/src/config/extensions/variableSchema.js +4 -0
  67. package/dist/src/config/extensions/variableSchema.js.map +1 -1
  68. package/dist/src/config/keyBindings.d.ts +1 -0
  69. package/dist/src/config/keyBindings.js +3 -1
  70. package/dist/src/config/keyBindings.js.map +1 -1
  71. package/dist/src/config/paths.d.ts +8 -0
  72. package/dist/src/config/paths.js +11 -0
  73. package/dist/src/config/paths.js.map +1 -0
  74. package/dist/src/config/profileBootstrap.d.ts +13 -0
  75. package/dist/src/config/profileBootstrap.js +369 -15
  76. package/dist/src/config/profileBootstrap.js.map +1 -1
  77. package/dist/src/config/settings.d.ts +14 -4
  78. package/dist/src/config/settings.js +161 -99
  79. package/dist/src/config/settings.js.map +1 -1
  80. package/dist/src/config/settingsSchema.d.ts +510 -216
  81. package/dist/src/config/settingsSchema.js +468 -214
  82. package/dist/src/config/settingsSchema.js.map +1 -1
  83. package/dist/src/config/settingsSchema.test.js +18 -31
  84. package/dist/src/config/settingsSchema.test.js.map +1 -1
  85. package/dist/src/config/trustedFolders.d.ts +6 -3
  86. package/dist/src/config/trustedFolders.js +35 -13
  87. package/dist/src/config/trustedFolders.js.map +1 -1
  88. package/dist/src/config/trustedFolders.test.js +83 -9
  89. package/dist/src/config/trustedFolders.test.js.map +1 -1
  90. package/dist/src/coreToolToggle.test.d.ts +6 -0
  91. package/dist/src/coreToolToggle.test.js +427 -0
  92. package/dist/src/coreToolToggle.test.js.map +1 -0
  93. package/dist/src/extensions/extensionAutoUpdater.d.ts +76 -0
  94. package/dist/src/extensions/extensionAutoUpdater.js +279 -0
  95. package/dist/src/extensions/extensionAutoUpdater.js.map +1 -0
  96. package/dist/src/extensions/extensionAutoUpdater.test.d.ts +6 -0
  97. package/dist/src/extensions/extensionAutoUpdater.test.js +146 -0
  98. package/dist/src/extensions/extensionAutoUpdater.test.js.map +1 -0
  99. package/dist/src/gemini.js +93 -63
  100. package/dist/src/gemini.js.map +1 -1
  101. package/dist/src/gemini.test.js +121 -1
  102. package/dist/src/gemini.test.js.map +1 -1
  103. package/dist/src/generated/git-commit.d.ts +1 -1
  104. package/dist/src/generated/git-commit.js +1 -1
  105. package/dist/src/generated/git-commit.js.map +1 -1
  106. package/dist/src/integration-tests/cli-args.integration.test.js +299 -0
  107. package/dist/src/integration-tests/cli-args.integration.test.js.map +1 -1
  108. package/dist/src/nonInteractiveCli.d.ts +2 -1
  109. package/dist/src/nonInteractiveCli.js +29 -16
  110. package/dist/src/nonInteractiveCli.js.map +1 -1
  111. package/dist/src/nonInteractiveCliCommands.d.ts +17 -0
  112. package/dist/src/nonInteractiveCliCommands.js +81 -0
  113. package/dist/src/nonInteractiveCliCommands.js.map +1 -0
  114. package/dist/src/runtime/runtimeSettings.test.js +2 -0
  115. package/dist/src/runtime/runtimeSettings.test.js.map +1 -1
  116. package/dist/src/services/BuiltinCommandLoader.d.ts +3 -3
  117. package/dist/src/services/BuiltinCommandLoader.js +7 -0
  118. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  119. package/dist/src/services/BuiltinCommandLoader.test.js +37 -0
  120. package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
  121. package/dist/src/services/McpPromptLoader.js +39 -34
  122. package/dist/src/services/McpPromptLoader.js.map +1 -1
  123. package/dist/src/settings/ephemeralSettings.js +56 -0
  124. package/dist/src/settings/ephemeralSettings.js.map +1 -1
  125. package/dist/src/test-utils/createExtension.d.ts +15 -0
  126. package/dist/src/test-utils/createExtension.js +24 -0
  127. package/dist/src/test-utils/createExtension.js.map +1 -0
  128. package/dist/src/test-utils/mockCommandContext.js +4 -2
  129. package/dist/src/test-utils/mockCommandContext.js.map +1 -1
  130. package/dist/src/test-utils/render.d.ts +3 -1
  131. package/dist/src/test-utils/render.js +4 -1
  132. package/dist/src/test-utils/render.js.map +1 -1
  133. package/dist/src/ui/App.d.ts +18 -3
  134. package/dist/src/ui/App.js +32 -873
  135. package/dist/src/ui/App.js.map +1 -1
  136. package/dist/src/ui/AppContainer.d.ts +19 -0
  137. package/dist/src/ui/AppContainer.js +1187 -0
  138. package/dist/src/ui/AppContainer.js.map +1 -0
  139. package/dist/src/ui/IdeIntegrationNudge.d.ts +2 -2
  140. package/dist/src/ui/IdeIntegrationNudge.js +4 -7
  141. package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
  142. package/dist/src/ui/commands/chatCommand.js +214 -58
  143. package/dist/src/ui/commands/chatCommand.js.map +1 -1
  144. package/dist/src/ui/commands/clearCommand.js +1 -2
  145. package/dist/src/ui/commands/clearCommand.js.map +1 -1
  146. package/dist/src/ui/commands/diagnosticsCommand.js +2 -2
  147. package/dist/src/ui/commands/diagnosticsCommand.js.map +1 -1
  148. package/dist/src/ui/commands/directoryCommand.js +3 -2
  149. package/dist/src/ui/commands/directoryCommand.js.map +1 -1
  150. package/dist/src/ui/commands/docsCommand.js +1 -1
  151. package/dist/src/ui/commands/docsCommand.js.map +1 -1
  152. package/dist/src/ui/commands/extensionsCommand.d.ts +1 -1
  153. package/dist/src/ui/commands/extensionsCommand.js +92 -17
  154. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  155. package/dist/src/ui/commands/helpCommand.js +6 -5
  156. package/dist/src/ui/commands/helpCommand.js.map +1 -1
  157. package/dist/src/ui/commands/ideCommand.js +7 -7
  158. package/dist/src/ui/commands/ideCommand.js.map +1 -1
  159. package/dist/src/ui/commands/loggingCommand.js +1 -1
  160. package/dist/src/ui/commands/loggingCommand.js.map +1 -1
  161. package/dist/src/ui/commands/mcpCommand.js +11 -3
  162. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  163. package/dist/src/ui/commands/memoryCommand.js +21 -3
  164. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  165. package/dist/src/ui/commands/permissionsCommand.d.ts +7 -0
  166. package/dist/src/ui/commands/permissionsCommand.js +16 -0
  167. package/dist/src/ui/commands/permissionsCommand.js.map +1 -0
  168. package/dist/src/ui/commands/permissionsCommand.test.d.ts +6 -0
  169. package/dist/src/ui/commands/permissionsCommand.test.js +55 -0
  170. package/dist/src/ui/commands/permissionsCommand.test.js.map +1 -0
  171. package/dist/src/ui/commands/policiesCommand.d.ts +10 -0
  172. package/dist/src/ui/commands/policiesCommand.js +112 -0
  173. package/dist/src/ui/commands/policiesCommand.js.map +1 -0
  174. package/dist/src/ui/commands/policiesCommand.test.d.ts +6 -0
  175. package/dist/src/ui/commands/policiesCommand.test.js +224 -0
  176. package/dist/src/ui/commands/policiesCommand.test.js.map +1 -0
  177. package/dist/src/ui/commands/setCommand.js +105 -30
  178. package/dist/src/ui/commands/setCommand.js.map +1 -1
  179. package/dist/src/ui/commands/setCommand.test.js +1 -1
  180. package/dist/src/ui/commands/setCommand.test.js.map +1 -1
  181. package/dist/src/ui/commands/statsCommand.js +11 -1
  182. package/dist/src/ui/commands/statsCommand.js.map +1 -1
  183. package/dist/src/ui/commands/test/subagentCommand.test.js +6 -1
  184. package/dist/src/ui/commands/test/subagentCommand.test.js.map +1 -1
  185. package/dist/src/ui/commands/types.d.ts +19 -23
  186. package/dist/src/ui/commands/types.js +1 -1
  187. package/dist/src/ui/commands/types.js.map +1 -1
  188. package/dist/src/ui/commands/uiprofileCommand.d.ts +7 -0
  189. package/dist/src/ui/commands/uiprofileCommand.js +23 -0
  190. package/dist/src/ui/commands/uiprofileCommand.js.map +1 -0
  191. package/dist/src/ui/components/AuthDialog.js +4 -0
  192. package/dist/src/ui/components/AuthDialog.js.map +1 -1
  193. package/dist/src/ui/components/AuthDialog.test.js +32 -32
  194. package/dist/src/ui/components/AuthDialog.test.js.map +1 -1
  195. package/dist/src/ui/components/CacheStatsDisplay.d.ts +7 -0
  196. package/dist/src/ui/components/CacheStatsDisplay.js +35 -0
  197. package/dist/src/ui/components/CacheStatsDisplay.js.map +1 -0
  198. package/dist/src/ui/components/CacheStatsDisplay.test.d.ts +6 -0
  199. package/dist/src/ui/components/CacheStatsDisplay.test.js +118 -0
  200. package/dist/src/ui/components/CacheStatsDisplay.test.js.map +1 -0
  201. package/dist/src/ui/components/CliSpinner.d.ts +10 -0
  202. package/dist/src/ui/components/CliSpinner.js +20 -0
  203. package/dist/src/ui/components/CliSpinner.js.map +1 -0
  204. package/dist/src/ui/components/Composer.d.ts +17 -0
  205. package/dist/src/ui/components/Composer.js +16 -0
  206. package/dist/src/ui/components/Composer.js.map +1 -0
  207. package/dist/src/ui/components/ConsentPrompt.d.ts +13 -0
  208. package/dist/src/ui/components/ConsentPrompt.js +18 -0
  209. package/dist/src/ui/components/ConsentPrompt.js.map +1 -0
  210. package/dist/src/ui/components/ConsentPrompt.test.d.ts +6 -0
  211. package/dist/src/ui/components/ConsentPrompt.test.js +67 -0
  212. package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -0
  213. package/dist/src/ui/components/DebugProfiler.d.ts +15 -0
  214. package/dist/src/ui/components/DebugProfiler.js +137 -13
  215. package/dist/src/ui/components/DebugProfiler.js.map +1 -1
  216. package/dist/src/ui/components/DebugProfiler.test.d.ts +6 -0
  217. package/dist/src/ui/components/DebugProfiler.test.js +131 -0
  218. package/dist/src/ui/components/DebugProfiler.test.js.map +1 -0
  219. package/dist/src/ui/components/DialogManager.d.ts +16 -0
  220. package/dist/src/ui/components/DialogManager.js +131 -0
  221. package/dist/src/ui/components/DialogManager.js.map +1 -0
  222. package/dist/src/ui/components/EditorSettingsDialog.js +18 -8
  223. package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
  224. package/dist/src/ui/components/FolderTrustDialog.js +4 -0
  225. package/dist/src/ui/components/FolderTrustDialog.js.map +1 -1
  226. package/dist/src/ui/components/FolderTrustDialog.test.js +2 -2
  227. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  228. package/dist/src/ui/components/HistoryItemDisplay.d.ts +6 -2
  229. package/dist/src/ui/components/HistoryItemDisplay.js +10 -2
  230. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  231. package/dist/src/ui/components/HistoryItemDisplay.test.js +90 -10
  232. package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
  233. package/dist/src/ui/components/InputPrompt.d.ts +6 -0
  234. package/dist/src/ui/components/InputPrompt.js +52 -18
  235. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  236. package/dist/src/ui/components/LoadProfileDialog.js +13 -9
  237. package/dist/src/ui/components/LoadProfileDialog.js.map +1 -1
  238. package/dist/src/ui/components/LoadingIndicator.js +1 -1
  239. package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
  240. package/dist/src/ui/components/LoadingIndicator.test.js +6 -0
  241. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  242. package/dist/src/ui/components/LoggingDialog.js +14 -10
  243. package/dist/src/ui/components/LoggingDialog.js.map +1 -1
  244. package/dist/src/ui/components/Notifications.d.ts +14 -0
  245. package/dist/src/ui/components/Notifications.js +34 -0
  246. package/dist/src/ui/components/Notifications.js.map +1 -0
  247. package/dist/src/ui/components/OAuthCodeDialog.js +2 -1
  248. package/dist/src/ui/components/OAuthCodeDialog.js.map +1 -1
  249. package/dist/src/ui/components/PermissionsModifyTrustDialog.d.ts +14 -0
  250. package/dist/src/ui/components/PermissionsModifyTrustDialog.js +115 -0
  251. package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -0
  252. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.d.ts +6 -0
  253. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +98 -0
  254. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -0
  255. package/dist/src/ui/components/ProviderDialog.js +23 -18
  256. package/dist/src/ui/components/ProviderDialog.js.map +1 -1
  257. package/dist/src/ui/components/ProviderModelDialog.js +24 -18
  258. package/dist/src/ui/components/ProviderModelDialog.js.map +1 -1
  259. package/dist/src/ui/components/SettingsDialog.d.ts +12 -2
  260. package/dist/src/ui/components/SettingsDialog.js +253 -23
  261. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  262. package/dist/src/ui/components/SettingsDialog.test.js +90 -8
  263. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  264. package/dist/src/ui/components/ShellConfirmationDialog.js +3 -0
  265. package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
  266. package/dist/src/ui/components/ThemeDialog.js +6 -4
  267. package/dist/src/ui/components/ThemeDialog.js.map +1 -1
  268. package/dist/src/ui/components/ToolsDialog.js +6 -4
  269. package/dist/src/ui/components/ToolsDialog.js.map +1 -1
  270. package/dist/src/ui/components/WorkspaceMigrationDialog.js +5 -3
  271. package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +1 -1
  272. package/dist/src/ui/components/messages/ToolConfirmationMessage.js +37 -6
  273. package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  274. package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +9 -0
  275. package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -1
  276. package/dist/src/ui/components/messages/ToolGroupMessage.js +7 -3
  277. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  278. package/dist/src/ui/components/messages/ToolMessage.js +7 -3
  279. package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  280. package/dist/src/ui/components/messages/WarningMessage.d.ts +11 -0
  281. package/dist/src/ui/components/messages/WarningMessage.js +10 -0
  282. package/dist/src/ui/components/messages/WarningMessage.js.map +1 -0
  283. package/dist/src/ui/components/messages/WarningMessage.test.d.ts +6 -0
  284. package/dist/src/ui/components/messages/WarningMessage.test.js +15 -0
  285. package/dist/src/ui/components/messages/WarningMessage.test.js.map +1 -0
  286. package/dist/src/ui/components/shared/BaseSelectionList.d.ts +38 -0
  287. package/dist/src/ui/components/shared/BaseSelectionList.js +76 -0
  288. package/dist/src/ui/components/shared/BaseSelectionList.js.map +1 -0
  289. package/dist/src/ui/components/shared/BaseSelectionList.test.d.ts +6 -0
  290. package/dist/src/ui/components/shared/BaseSelectionList.test.js +376 -0
  291. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -0
  292. package/dist/src/ui/components/shared/RadioButtonSelect.d.ts +4 -5
  293. package/dist/src/ui/components/shared/RadioButtonSelect.js +11 -117
  294. package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
  295. package/dist/src/ui/components/shared/RadioButtonSelect.test.d.ts +1 -1
  296. package/dist/src/ui/components/shared/RadioButtonSelect.test.js +116 -95
  297. package/dist/src/ui/components/shared/RadioButtonSelect.test.js.map +1 -1
  298. package/dist/src/ui/components/shared/text-buffer.d.ts +6 -0
  299. package/dist/src/ui/components/shared/text-buffer.js +71 -4
  300. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  301. package/dist/src/ui/components/views/ChatList.d.ts +12 -0
  302. package/dist/src/ui/components/views/ChatList.js +17 -0
  303. package/dist/src/ui/components/views/ChatList.js.map +1 -0
  304. package/dist/src/ui/components/views/ChatList.test.d.ts +6 -0
  305. package/dist/src/ui/components/views/ChatList.test.js +42 -0
  306. package/dist/src/ui/components/views/ChatList.test.js.map +1 -0
  307. package/dist/src/ui/constants.d.ts +1 -0
  308. package/dist/src/ui/constants.js +1 -0
  309. package/dist/src/ui/constants.js.map +1 -1
  310. package/dist/src/ui/contexts/FocusContext.d.ts +7 -0
  311. package/dist/src/ui/contexts/FocusContext.js +9 -0
  312. package/dist/src/ui/contexts/FocusContext.js.map +1 -0
  313. package/dist/src/ui/contexts/KeypressContext.d.ts +1 -1
  314. package/dist/src/ui/contexts/KeypressContext.js +63 -4
  315. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  316. package/dist/src/ui/contexts/KeypressContext.test.js +156 -0
  317. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  318. package/dist/src/ui/contexts/SessionContext.d.ts +6 -0
  319. package/dist/src/ui/contexts/SessionContext.js +107 -5
  320. package/dist/src/ui/contexts/SessionContext.js.map +1 -1
  321. package/dist/src/ui/contexts/UIActionsContext.d.ts +82 -0
  322. package/dist/src/ui/contexts/UIActionsContext.js +20 -0
  323. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -0
  324. package/dist/src/ui/contexts/UIStateContext.d.ts +121 -0
  325. package/dist/src/ui/contexts/UIStateContext.js +20 -0
  326. package/dist/src/ui/contexts/UIStateContext.js.map +1 -0
  327. package/dist/src/ui/contexts/VimModeContext.js +4 -4
  328. package/dist/src/ui/contexts/VimModeContext.js.map +1 -1
  329. package/dist/src/ui/hooks/slashCommandProcessor.d.ts +29 -6
  330. package/dist/src/ui/hooks/slashCommandProcessor.js +150 -155
  331. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  332. package/dist/src/ui/hooks/useBracketedPaste.js +3 -4
  333. package/dist/src/ui/hooks/useBracketedPaste.js.map +1 -1
  334. package/dist/src/ui/hooks/useEditorSettings.js +1 -1
  335. package/dist/src/ui/hooks/useEditorSettings.js.map +1 -1
  336. package/dist/src/ui/hooks/useExtensionAutoUpdate.d.ts +13 -0
  337. package/dist/src/ui/hooks/useExtensionAutoUpdate.js +40 -0
  338. package/dist/src/ui/hooks/useExtensionAutoUpdate.js.map +1 -0
  339. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +21 -0
  340. package/dist/src/ui/hooks/useExtensionUpdates.js +168 -0
  341. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -0
  342. package/dist/src/ui/hooks/useExtensionUpdates.test.d.ts +6 -0
  343. package/dist/src/ui/hooks/useExtensionUpdates.test.js +243 -0
  344. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -0
  345. package/dist/src/ui/hooks/useFlickerDetector.d.ts +22 -0
  346. package/dist/src/ui/hooks/useFlickerDetector.js +38 -0
  347. package/dist/src/ui/hooks/useFlickerDetector.js.map +1 -0
  348. package/dist/src/ui/hooks/useFlickerDetector.test.d.ts +6 -0
  349. package/dist/src/ui/hooks/useFlickerDetector.test.js +142 -0
  350. package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -0
  351. package/dist/src/ui/hooks/useGeminiStream.d.ts +1 -1
  352. package/dist/src/ui/hooks/useGeminiStream.integration.test.js +1 -1
  353. package/dist/src/ui/hooks/useGeminiStream.integration.test.js.map +1 -1
  354. package/dist/src/ui/hooks/useGeminiStream.js +41 -18
  355. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  356. package/dist/src/ui/hooks/useMemoryMonitor.d.ts +13 -0
  357. package/dist/src/ui/hooks/useMemoryMonitor.js +28 -0
  358. package/dist/src/ui/hooks/useMemoryMonitor.js.map +1 -0
  359. package/dist/src/ui/hooks/usePermissionsModifyTrust.d.ts +34 -0
  360. package/dist/src/ui/hooks/usePermissionsModifyTrust.js +90 -0
  361. package/dist/src/ui/hooks/usePermissionsModifyTrust.js.map +1 -0
  362. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.d.ts +6 -0
  363. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +60 -0
  364. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -0
  365. package/dist/src/ui/hooks/useReactToolScheduler.d.ts +1 -1
  366. package/dist/src/ui/hooks/useReactToolScheduler.js +5 -1
  367. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  368. package/dist/src/ui/hooks/useSelectionList.d.ts +34 -0
  369. package/dist/src/ui/hooks/useSelectionList.js +292 -0
  370. package/dist/src/ui/hooks/useSelectionList.js.map +1 -0
  371. package/dist/src/ui/hooks/useSelectionList.test.d.ts +6 -0
  372. package/dist/src/ui/hooks/useSelectionList.test.js +726 -0
  373. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -0
  374. package/dist/src/ui/hooks/useShowMemoryCommand.js +1 -1
  375. package/dist/src/ui/hooks/useShowMemoryCommand.js.map +1 -1
  376. package/dist/src/ui/hooks/useStateAndRef.d.ts +1 -1
  377. package/dist/src/ui/hooks/useStateAndRef.js +2 -2
  378. package/dist/src/ui/hooks/useStateAndRef.js.map +1 -1
  379. package/dist/src/ui/hooks/useStaticHistoryRefresh.d.ts +13 -0
  380. package/dist/src/ui/hooks/useStaticHistoryRefresh.js +35 -0
  381. package/dist/src/ui/hooks/useStaticHistoryRefresh.js.map +1 -0
  382. package/dist/src/ui/hooks/useStaticHistoryRefresh.test.d.ts +6 -0
  383. package/dist/src/ui/hooks/useStaticHistoryRefresh.test.js +42 -0
  384. package/dist/src/ui/hooks/useStaticHistoryRefresh.test.js.map +1 -0
  385. package/dist/src/ui/hooks/useThemeCommand.js +7 -7
  386. package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
  387. package/dist/src/ui/hooks/useToolScheduler.test.js +17 -20
  388. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  389. package/dist/src/ui/keyMatchers.test.js +6 -0
  390. package/dist/src/ui/keyMatchers.test.js.map +1 -1
  391. package/dist/src/ui/layouts/DefaultAppLayout.d.ts +23 -0
  392. package/dist/src/ui/layouts/DefaultAppLayout.js +76 -0
  393. package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -0
  394. package/dist/src/ui/noninteractive/nonInteractiveUi.d.ts +12 -0
  395. package/dist/src/ui/noninteractive/nonInteractiveUi.js +31 -0
  396. package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -0
  397. package/dist/src/ui/privacy/CloudFreePrivacyNotice.js +2 -2
  398. package/dist/src/ui/privacy/CloudFreePrivacyNotice.js.map +1 -1
  399. package/dist/src/ui/privacy/MultiProviderPrivacyNotice.d.ts +11 -0
  400. package/dist/src/ui/privacy/MultiProviderPrivacyNotice.js +92 -0
  401. package/dist/src/ui/privacy/MultiProviderPrivacyNotice.js.map +1 -0
  402. package/dist/src/ui/privacy/MultiProviderPrivacyNotice.test.d.ts +6 -0
  403. package/dist/src/ui/privacy/MultiProviderPrivacyNotice.test.js +106 -0
  404. package/dist/src/ui/privacy/MultiProviderPrivacyNotice.test.js.map +1 -0
  405. package/dist/src/ui/privacy/PrivacyNotice.js +4 -9
  406. package/dist/src/ui/privacy/PrivacyNotice.js.map +1 -1
  407. package/dist/src/ui/state/extensions.d.ts +61 -0
  408. package/dist/src/ui/state/extensions.js +84 -0
  409. package/dist/src/ui/state/extensions.js.map +1 -0
  410. package/dist/src/ui/themes/theme.js +2 -2
  411. package/dist/src/ui/themes/theme.js.map +1 -1
  412. package/dist/src/ui/types.d.ts +58 -2
  413. package/dist/src/ui/types.js +6 -0
  414. package/dist/src/ui/types.js.map +1 -1
  415. package/dist/src/ui/utils/MarkdownDisplay.js +1 -2
  416. package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
  417. package/dist/src/ui/utils/MarkdownDisplay.test.d.ts +1 -1
  418. package/dist/src/ui/utils/MarkdownDisplay.test.js +94 -104
  419. package/dist/src/ui/utils/MarkdownDisplay.test.js.map +1 -1
  420. package/dist/src/ui/utils/bracketedPaste.d.ts +9 -0
  421. package/dist/src/ui/utils/bracketedPaste.js +14 -0
  422. package/dist/src/ui/utils/bracketedPaste.js.map +1 -0
  423. package/dist/src/ui/utils/displayUtils.d.ts +1 -0
  424. package/dist/src/ui/utils/displayUtils.js +4 -1
  425. package/dist/src/ui/utils/displayUtils.js.map +1 -1
  426. package/dist/src/ui/utils/displayUtils.test.js +36 -17
  427. package/dist/src/ui/utils/displayUtils.test.js.map +1 -1
  428. package/dist/src/ui/utils/highlight.d.ts +2 -1
  429. package/dist/src/ui/utils/highlight.js +52 -8
  430. package/dist/src/ui/utils/highlight.js.map +1 -1
  431. package/dist/src/ui/utils/highlight.test.js +15 -14
  432. package/dist/src/ui/utils/highlight.test.js.map +1 -1
  433. package/dist/src/ui/utils/kittyProtocolDetector.d.ts +1 -0
  434. package/dist/src/ui/utils/kittyProtocolDetector.js +11 -3
  435. package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
  436. package/dist/src/ui/utils/terminalSequences.d.ts +22 -0
  437. package/dist/src/ui/utils/terminalSequences.js +23 -0
  438. package/dist/src/ui/utils/terminalSequences.js.map +1 -0
  439. package/dist/src/ui/utils/textUtils.d.ts +10 -0
  440. package/dist/src/ui/utils/textUtils.js +82 -1
  441. package/dist/src/ui/utils/textUtils.js.map +1 -1
  442. package/dist/src/ui/utils/textUtils.test.d.ts +6 -0
  443. package/dist/src/ui/utils/textUtils.test.js +132 -0
  444. package/dist/src/ui/utils/textUtils.test.js.map +1 -0
  445. package/dist/src/utils/bootstrap.d.ts +24 -0
  446. package/dist/src/utils/bootstrap.js +55 -0
  447. package/dist/src/utils/bootstrap.js.map +1 -0
  448. package/dist/src/utils/bootstrap.test.d.ts +6 -0
  449. package/dist/src/utils/bootstrap.test.js +103 -0
  450. package/dist/src/utils/bootstrap.test.js.map +1 -0
  451. package/dist/src/utils/cleanup.d.ts +6 -0
  452. package/dist/src/utils/cleanup.js +14 -0
  453. package/dist/src/utils/cleanup.js.map +1 -1
  454. package/dist/src/utils/commands.d.ts +17 -0
  455. package/dist/src/utils/commands.js +60 -0
  456. package/dist/src/utils/commands.js.map +1 -0
  457. package/dist/src/utils/dialogScopeUtils.d.ts +3 -0
  458. package/dist/src/utils/dialogScopeUtils.js +11 -2
  459. package/dist/src/utils/dialogScopeUtils.js.map +1 -1
  460. package/dist/src/utils/dynamicSettings.d.ts +20 -0
  461. package/dist/src/utils/dynamicSettings.js +110 -0
  462. package/dist/src/utils/dynamicSettings.js.map +1 -0
  463. package/dist/src/utils/dynamicSettings.test.d.ts +6 -0
  464. package/dist/src/utils/dynamicSettings.test.js +329 -0
  465. package/dist/src/utils/dynamicSettings.test.js.map +1 -0
  466. package/dist/src/utils/events.d.ts +3 -1
  467. package/dist/src/utils/events.js +2 -0
  468. package/dist/src/utils/events.js.map +1 -1
  469. package/dist/src/utils/handleAutoUpdate.js +4 -1
  470. package/dist/src/utils/handleAutoUpdate.js.map +1 -1
  471. package/dist/src/utils/installationInfo.d.ts +1 -0
  472. package/dist/src/utils/installationInfo.js +5 -3
  473. package/dist/src/utils/installationInfo.js.map +1 -1
  474. package/dist/src/utils/relaunch.d.ts +16 -0
  475. package/dist/src/utils/relaunch.js +34 -0
  476. package/dist/src/utils/relaunch.js.map +1 -0
  477. package/dist/src/utils/relaunch.test.d.ts +6 -0
  478. package/dist/src/utils/relaunch.test.js +96 -0
  479. package/dist/src/utils/relaunch.test.js.map +1 -0
  480. package/dist/src/utils/sessionCleanup.d.ts +22 -0
  481. package/dist/src/utils/sessionCleanup.integration.test.d.ts +6 -0
  482. package/dist/src/utils/sessionCleanup.integration.test.js +174 -0
  483. package/dist/src/utils/sessionCleanup.integration.test.js.map +1 -0
  484. package/dist/src/utils/sessionCleanup.js +213 -0
  485. package/dist/src/utils/sessionCleanup.js.map +1 -0
  486. package/dist/src/utils/sessionCleanup.test.d.ts +6 -0
  487. package/dist/src/utils/sessionCleanup.test.js +1144 -0
  488. package/dist/src/utils/sessionCleanup.test.js.map +1 -0
  489. package/dist/src/utils/sessionUtils.d.ts +37 -0
  490. package/dist/src/utils/sessionUtils.js +71 -0
  491. package/dist/src/utils/sessionUtils.js.map +1 -0
  492. package/dist/src/utils/settingsUtils.d.ts +6 -3
  493. package/dist/src/utils/settingsUtils.js +39 -11
  494. package/dist/src/utils/settingsUtils.js.map +1 -1
  495. package/dist/src/utils/settingsUtils.test.js +96 -96
  496. package/dist/src/utils/settingsUtils.test.js.map +1 -1
  497. package/dist/src/utils/singleSettingSaver.js +5 -0
  498. package/dist/src/utils/singleSettingSaver.js.map +1 -1
  499. package/dist/src/utils/windowTitle.d.ts +12 -0
  500. package/dist/src/utils/windowTitle.js +19 -0
  501. package/dist/src/utils/windowTitle.js.map +1 -0
  502. package/dist/src/utils/windowTitle.test.d.ts +6 -0
  503. package/dist/src/utils/windowTitle.test.js +49 -0
  504. package/dist/src/utils/windowTitle.test.js.map +1 -0
  505. package/dist/src/zed-integration/acp.js +1 -2
  506. package/dist/src/zed-integration/acp.js.map +1 -1
  507. package/dist/src/zed-integration/fileSystemService.d.ts +1 -0
  508. package/dist/src/zed-integration/fileSystemService.js +3 -0
  509. package/dist/src/zed-integration/fileSystemService.js.map +1 -1
  510. package/dist/src/zed-integration/schema.d.ts +212 -212
  511. package/dist/tsconfig.build.tsbuildinfo +1 -1
  512. package/package.json +15 -6
  513. package/dist/src/ui/App.quittingMessages.test.d.ts +0 -1
  514. package/dist/src/ui/App.quittingMessages.test.js +0 -14
  515. package/dist/src/ui/App.quittingMessages.test.js.map +0 -1
@@ -3,12 +3,14 @@
3
3
  * Copyright 2025 Vybestack LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ /* eslint-disable @typescript-eslint/no-explicit-any */
6
7
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
- import { parseBootstrapArgs, prepareRuntimeForProfile, createBootstrapResult, } from '../profileBootstrap.js';
8
+ import { parseBootstrapArgs, prepareRuntimeForProfile, createBootstrapResult, parseInlineProfile, } from '../profileBootstrap.js';
8
9
  vi.mock('../../runtime/runtimeSettings.js', () => ({
9
10
  registerCliProviderInfrastructure: vi.fn(),
10
11
  }));
11
12
  const parseArgs = parseBootstrapArgs;
13
+ const parseArgsWithMeta = parseBootstrapArgs;
12
14
  const prepareRuntime = prepareRuntimeForProfile;
13
15
  const finalizeBootstrap = createBootstrapResult;
14
16
  describe('profileBootstrap helpers', () => {
@@ -78,6 +80,7 @@ describe('profileBootstrap helpers', () => {
78
80
  const parsed = {
79
81
  bootstrapArgs: {
80
82
  profileName: 'synthetic',
83
+ profileJson: null,
81
84
  providerOverride: null,
82
85
  modelOverride: null,
83
86
  keyOverride: null,
@@ -112,4 +115,1351 @@ describe('profileBootstrap helpers', () => {
112
115
  });
113
116
  });
114
117
  });
118
+ describe('--profile flag parsing @plan:PLAN-20251118-ISSUE533.P04', () => {
119
+ let originalArgv;
120
+ beforeEach(() => {
121
+ originalArgv = [...process.argv];
122
+ });
123
+ afterEach(() => {
124
+ process.argv = originalArgv;
125
+ });
126
+ // Group 1: Basic Parsing (5 tests)
127
+ /**
128
+ * @plan:PLAN-20251118-ISSUE533.P04
129
+ * @requirement:REQ-PROF-001.1
130
+ * @scenario: Parse --profile with space-separated JSON string
131
+ * @given: Command line with --profile followed by JSON string
132
+ * @when: parseBootstrapArgs is called
133
+ * @then: profileJson is populated with the JSON string and profileName is null
134
+ */
135
+ it('should parse --profile with space-separated JSON string', () => {
136
+ process.argv = [
137
+ 'node',
138
+ 'llxprt',
139
+ '--profile',
140
+ '{"provider":"openai","model":"gpt-4"}',
141
+ ];
142
+ const result = parseBootstrapArgs();
143
+ expect(result.bootstrapArgs.profileJson).toBe('{"provider":"openai","model":"gpt-4"}');
144
+ expect(result.bootstrapArgs.profileName).toBeNull();
145
+ });
146
+ /**
147
+ * @plan:PLAN-20251118-ISSUE533.P04
148
+ * @requirement:REQ-PROF-001.1
149
+ * @scenario: Parse --profile with equals syntax
150
+ * @given: Command line with --profile=<JSON>
151
+ * @when: parseBootstrapArgs is called
152
+ * @then: profileJson is populated with the JSON string
153
+ */
154
+ it('should parse --profile with equals syntax', () => {
155
+ process.argv = [
156
+ 'node',
157
+ 'llxprt',
158
+ '--profile={"provider":"anthropic","model":"claude-3"}',
159
+ ];
160
+ const result = parseBootstrapArgs();
161
+ expect(result.bootstrapArgs.profileJson).toBe('{"provider":"anthropic","model":"claude-3"}');
162
+ expect(result.bootstrapArgs.profileName).toBeNull();
163
+ });
164
+ /**
165
+ * @plan:PLAN-20251118-ISSUE533.P04
166
+ * @requirement:REQ-PROF-001.1
167
+ * @scenario: Accept empty JSON object
168
+ * @given: Command line with --profile {}
169
+ * @when: parseBootstrapArgs is called
170
+ * @then: profileJson is set to "{}"
171
+ */
172
+ it('should accept empty JSON object', () => {
173
+ process.argv = ['node', 'llxprt', '--profile', '{}'];
174
+ const result = parseBootstrapArgs();
175
+ expect(result.bootstrapArgs.profileJson).toBe('{}');
176
+ });
177
+ /**
178
+ * @plan:PLAN-20251118-ISSUE533.P04
179
+ * @requirement:REQ-PROF-001.1
180
+ * @scenario: Preserve whitespace in JSON string
181
+ * @given: Command line with JSON containing various whitespace
182
+ * @when: parseBootstrapArgs is called
183
+ * @then: profileJson preserves all whitespace characters
184
+ */
185
+ it('should preserve whitespace in JSON string', () => {
186
+ const jsonWithWhitespace = '{\n "provider": "openai",\n "model": "gpt-4"\n}';
187
+ process.argv = ['node', 'llxprt', '--profile', jsonWithWhitespace];
188
+ const result = parseBootstrapArgs();
189
+ expect(result.bootstrapArgs.profileJson).toBe(jsonWithWhitespace);
190
+ });
191
+ /**
192
+ * @plan:PLAN-20251118-ISSUE533.P04
193
+ * @requirement:REQ-PROF-001.1
194
+ * @scenario: Parse --profile alongside other flags
195
+ * @given: Command line with --profile and other flags like --provider
196
+ * @when: parseBootstrapArgs is called
197
+ * @then: Both profileJson and other args are correctly populated
198
+ */
199
+ it('should parse --profile alongside other flags', () => {
200
+ process.argv = [
201
+ 'node',
202
+ 'llxprt',
203
+ '--profile',
204
+ '{"model":"gpt-4"}',
205
+ '--key',
206
+ 'test-key',
207
+ '--set',
208
+ 'debug=true',
209
+ ];
210
+ const result = parseBootstrapArgs();
211
+ expect(result.bootstrapArgs.profileJson).toBe('{"model":"gpt-4"}');
212
+ expect(result.bootstrapArgs.keyOverride).toBe('test-key');
213
+ expect(result.bootstrapArgs.setOverrides).toEqual(['debug=true']);
214
+ });
215
+ // Group 2: Error Cases (4 tests)
216
+ /**
217
+ * @plan:PLAN-20251118-ISSUE533.P04
218
+ * @requirement:REQ-PROF-001.2
219
+ * @scenario: Throw error when --profile has no value
220
+ * @given: Command line with --profile at the end without a value
221
+ * @when: parseBootstrapArgs is called
222
+ * @then: Error is thrown indicating missing value
223
+ */
224
+ it('should throw error when --profile has no value', () => {
225
+ process.argv = ['node', 'llxprt', '--profile'];
226
+ expect(() => parseBootstrapArgs()).toThrow();
227
+ });
228
+ /**
229
+ * @plan:PLAN-20251118-ISSUE533.P04
230
+ * @requirement:REQ-PROF-001.2
231
+ * @scenario: Throw error when --profile is followed by another flag
232
+ * @given: Command line with --profile followed by --provider
233
+ * @when: parseBootstrapArgs is called
234
+ * @then: Error is thrown indicating missing value
235
+ */
236
+ it('should throw error when --profile is followed by another flag', () => {
237
+ process.argv = ['node', 'llxprt', '--profile', '--provider', 'openai'];
238
+ expect(() => parseBootstrapArgs()).toThrow();
239
+ });
240
+ /**
241
+ * @plan:PLAN-20251118-ISSUE533.P04
242
+ * @requirement:REQ-PROF-001.2
243
+ * @scenario: Accept empty string (validation fails later)
244
+ * @given: Command line with --profile ""
245
+ * @when: parseBootstrapArgs is called
246
+ * @then: profileJson is set to empty string (validation happens in Phase 05)
247
+ */
248
+ it('should accept empty string for --profile', () => {
249
+ process.argv = ['node', 'llxprt', '--profile', ''];
250
+ const result = parseBootstrapArgs();
251
+ expect(result.bootstrapArgs.profileJson).toBe('');
252
+ });
253
+ /**
254
+ * @plan:PLAN-20251118-ISSUE533.P04
255
+ * @requirement:REQ-PROF-003.3
256
+ * @scenario: Throw error for JSON exceeding 10KB
257
+ * @given: Command line with JSON string larger than 10KB
258
+ * @when: parseBootstrapArgs is called
259
+ * @then: Error message indicates size limit exceeded
260
+ */
261
+ it('should throw error for JSON exceeding 10KB', () => {
262
+ const largeJson = '{"data":"' + 'x'.repeat(10 * 1024) + '"}';
263
+ process.argv = ['node', 'llxprt', '--profile', largeJson];
264
+ expect(() => parseBootstrapArgs()).toThrow(/exceeds maximum size of 10KB/i);
265
+ });
266
+ // Group 3: Mutual Exclusivity (4 tests)
267
+ /**
268
+ * @plan:PLAN-20251118-ISSUE533.P04
269
+ * @requirement:REQ-INT-001.2
270
+ * @scenario: Throw error when both --profile and --profile-load are used
271
+ * @given: Command line with both --profile and --profile-load
272
+ * @when: parseBootstrapArgs is called
273
+ * @then: Error message indicates mutual exclusivity
274
+ */
275
+ it('should throw error when both --profile and --profile-load are used', () => {
276
+ process.argv = [
277
+ 'node',
278
+ 'llxprt',
279
+ '--profile',
280
+ '{"provider":"openai"}',
281
+ '--profile-load',
282
+ 'my-profile',
283
+ ];
284
+ expect(() => parseBootstrapArgs()).toThrow(/cannot use both.*--profile.*--profile-load/i);
285
+ });
286
+ /**
287
+ * @plan:PLAN-20251118-ISSUE533.P04
288
+ * @requirement:REQ-INT-001.2
289
+ * @scenario: Throw error regardless of flag order
290
+ * @given: Command line with --profile-load before --profile
291
+ * @when: parseBootstrapArgs is called
292
+ * @then: Error message indicates mutual exclusivity
293
+ */
294
+ it('should throw error regardless of flag order', () => {
295
+ process.argv = [
296
+ 'node',
297
+ 'llxprt',
298
+ '--profile-load',
299
+ 'my-profile',
300
+ '--profile',
301
+ '{"provider":"openai"}',
302
+ ];
303
+ expect(() => parseBootstrapArgs()).toThrow(/cannot use both.*--profile.*--profile-load/i);
304
+ });
305
+ /**
306
+ * @plan:PLAN-20251118-ISSUE533.P04
307
+ * @requirement:REQ-INT-001.2
308
+ * @scenario: Error message includes helpful guidance
309
+ * @given: Command line with both flags
310
+ * @when: parseBootstrapArgs is called
311
+ * @then: Error message suggests using one flag at a time
312
+ */
313
+ it('should provide helpful error message for mutual exclusivity', () => {
314
+ process.argv = [
315
+ 'node',
316
+ 'llxprt',
317
+ '--profile',
318
+ '{"provider":"openai"}',
319
+ '--profile-load',
320
+ 'my-profile',
321
+ ];
322
+ expect(() => parseBootstrapArgs()).toThrow(/cannot use both.*--profile.*--profile-load.*use one at a time/i);
323
+ });
324
+ /**
325
+ * @plan:PLAN-20251118-ISSUE533.P04
326
+ * @requirement:REQ-INT-001.2
327
+ * @scenario: No error when only --profile is used
328
+ * @given: Command line with only --profile flag
329
+ * @when: parseBootstrapArgs is called
330
+ * @then: Parsing succeeds without error
331
+ */
332
+ it('should not throw error when only --profile is used', () => {
333
+ process.argv = ['node', 'llxprt', '--profile', '{"provider":"openai"}'];
334
+ expect(() => parseBootstrapArgs()).not.toThrow();
335
+ });
336
+ // Group 4: Edge Cases (2 tests)
337
+ /**
338
+ * @plan:PLAN-20251118-ISSUE533.P04
339
+ * @requirement:REQ-PROF-001.3
340
+ * @scenario: Use last --profile value when multiple specified
341
+ * @given: Command line with --profile specified multiple times
342
+ * @when: parseBootstrapArgs is called
343
+ * @then: profileJson contains the last specified value
344
+ */
345
+ it('should use last --profile value when multiple specified', () => {
346
+ process.argv = [
347
+ 'node',
348
+ 'llxprt',
349
+ '--profile',
350
+ '{"provider":"openai"}',
351
+ '--profile',
352
+ '{"provider":"anthropic"}',
353
+ ];
354
+ const result = parseBootstrapArgs();
355
+ expect(result.bootstrapArgs.profileJson).toBe('{"provider":"anthropic"}');
356
+ });
357
+ /**
358
+ * @plan:PLAN-20251118-ISSUE533.P04
359
+ * @requirement:REQ-PROF-001.1
360
+ * @scenario: Preserve JSON with special characters
361
+ * @given: Command line with JSON containing quotes, backslashes, and unicode
362
+ * @when: parseBootstrapArgs is called
363
+ * @then: profileJson preserves all special characters exactly
364
+ */
365
+ it('should preserve JSON with special characters', () => {
366
+ const jsonWithSpecialChars = '{"message":"Hello \\"World\\"","emoji":"🚀","path":"C:\\\\Users"}';
367
+ process.argv = ['node', 'llxprt', '--profile', jsonWithSpecialChars];
368
+ const result = parseBootstrapArgs();
369
+ expect(result.bootstrapArgs.profileJson).toBe(jsonWithSpecialChars);
370
+ });
371
+ });
372
+ describe('parseInlineProfile() @plan:PLAN-20251118-ISSUE533.P07', () => {
373
+ /**
374
+ * @plan:PLAN-20251118-ISSUE533.P07
375
+ * @requirement:REQ-PROF-002.1
376
+ * @scenario: Parse minimal valid profile with only required fields
377
+ * @given: JSON string with provider, model, and key
378
+ * @when: parseInlineProfile() is called
379
+ * @then: Returns ProfileApplicationResult with provider and model names
380
+ */
381
+ it('should parse minimal valid profile with required fields', () => {
382
+ const jsonString = JSON.stringify({
383
+ provider: 'anthropic',
384
+ model: 'claude-3-5-sonnet-20241022',
385
+ key: 'sk-test-key-123',
386
+ });
387
+ const result = parseInlineProfile(jsonString);
388
+ expect(result.providerName).toBe('anthropic');
389
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
390
+ expect(result.warnings).toEqual([]);
391
+ });
392
+ /**
393
+ * @plan:PLAN-20251118-ISSUE533.P07
394
+ * @requirement:REQ-PROF-002.1
395
+ * @scenario: Parse profile with optional configuration fields
396
+ * @given: JSON string with provider, model, key, temperature, and maxTokens
397
+ * @when: parseInlineProfile() is called
398
+ * @then: Returns ProfileApplicationResult with provider and model names
399
+ */
400
+ it('should parse profile with optional fields like temperature and maxTokens', () => {
401
+ const jsonString = JSON.stringify({
402
+ provider: 'openai',
403
+ model: 'gpt-4',
404
+ key: 'sk-test-key-456',
405
+ temperature: 0.7,
406
+ maxTokens: 2000,
407
+ });
408
+ const result = parseInlineProfile(jsonString);
409
+ expect(result.providerName).toBe('openai');
410
+ expect(result.modelName).toBe('gpt-4');
411
+ expect(result.warnings).toEqual([]);
412
+ });
413
+ /**
414
+ * @plan:PLAN-20251118-ISSUE533.P07
415
+ * @requirement:REQ-PROF-002.1
416
+ * @scenario: Parse profile with whitespace formatting
417
+ * @given: JSON string with extra whitespace and newlines
418
+ * @when: parseInlineProfile() is called
419
+ * @then: Returns ProfileApplicationResult ignoring whitespace
420
+ */
421
+ it('should parse profile with whitespace formatting', () => {
422
+ const jsonString = `
423
+ {
424
+ "provider": "anthropic",
425
+ "model": "claude-3-5-sonnet-20241022",
426
+ "key": "sk-test-key-789"
427
+ }
428
+ `;
429
+ const result = parseInlineProfile(jsonString);
430
+ expect(result.providerName).toBe('anthropic');
431
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
432
+ expect(result.warnings).toEqual([]);
433
+ });
434
+ /**
435
+ * @plan:PLAN-20251118-ISSUE533.P07
436
+ * @requirement:REQ-PROF-002.1
437
+ * @scenario: Parse profile with nested objects
438
+ * @given: JSON string with nested tool_choice configuration
439
+ * @when: parseInlineProfile() is called
440
+ * @then: Returns ProfileApplicationResult with provider and model names
441
+ */
442
+ it('should parse profile with nested objects like tool_choice', () => {
443
+ const jsonString = JSON.stringify({
444
+ provider: 'openai',
445
+ model: 'gpt-4',
446
+ key: 'sk-test-key-abc',
447
+ tool_choice: {
448
+ type: 'function',
449
+ function: { name: 'get_weather' },
450
+ },
451
+ });
452
+ const result = parseInlineProfile(jsonString);
453
+ expect(result.providerName).toBe('openai');
454
+ expect(result.modelName).toBe('gpt-4');
455
+ expect(result.warnings).toEqual([]);
456
+ });
457
+ /**
458
+ * @plan:PLAN-20251118-ISSUE533.P07
459
+ * @requirement:REQ-PROF-002.1
460
+ * @scenario: Parse profiles for all supported providers
461
+ * @given: JSON strings for openai, anthropic, google, and azure providers
462
+ * @when: parseInlineProfile() is called for each
463
+ * @then: Returns correct provider and model names for each
464
+ */
465
+ it('should parse profiles for all supported providers', () => {
466
+ const providers = [
467
+ { provider: 'openai', model: 'gpt-4', key: 'key1' },
468
+ {
469
+ provider: 'anthropic',
470
+ model: 'claude-3-5-sonnet-20241022',
471
+ key: 'key2',
472
+ },
473
+ { provider: 'google', model: 'gemini-pro', key: 'key3' },
474
+ { provider: 'azure', model: 'gpt-4', key: 'key4' },
475
+ ];
476
+ providers.forEach((profile) => {
477
+ const result = parseInlineProfile(JSON.stringify(profile));
478
+ expect(result.providerName).toBe(profile.provider);
479
+ expect(result.modelName).toBe(profile.model);
480
+ expect(result.warnings).toEqual([]);
481
+ });
482
+ });
483
+ /**
484
+ * @plan:PLAN-20251118-ISSUE533.P07
485
+ * @requirement:REQ-PROF-001.3
486
+ * @scenario: Handle invalid JSON syntax
487
+ * @given: Malformed JSON string with syntax errors
488
+ * @when: parseInlineProfile() is called
489
+ * @then: Returns error in ProfileApplicationResult
490
+ */
491
+ it('should handle invalid JSON syntax', () => {
492
+ const invalidJson = '{provider: "anthropic", invalid syntax}';
493
+ const result = parseInlineProfile(invalidJson);
494
+ expect(result.providerName).toBe('');
495
+ expect(result.modelName).toBe('');
496
+ expect(result.error).toBeDefined();
497
+ expect(result.error).toContain('JSON');
498
+ });
499
+ /**
500
+ * @plan:PLAN-20251118-ISSUE533.P07
501
+ * @requirement:REQ-PROF-001.3
502
+ * @scenario: Handle empty string input
503
+ * @given: Empty string
504
+ * @when: parseInlineProfile() is called
505
+ * @then: Returns error in ProfileApplicationResult
506
+ */
507
+ it('should handle empty string', () => {
508
+ const result = parseInlineProfile('');
509
+ expect(result.providerName).toBe('');
510
+ expect(result.modelName).toBe('');
511
+ expect(result.error).toBeDefined();
512
+ expect(result.error).toContain('JSON');
513
+ });
514
+ /**
515
+ * @plan:PLAN-20251118-ISSUE533.P07
516
+ * @requirement:REQ-PROF-001.3
517
+ * @scenario: Handle whitespace-only string input
518
+ * @given: String containing only whitespace
519
+ * @when: parseInlineProfile() is called
520
+ * @then: Returns error in ProfileApplicationResult
521
+ */
522
+ it('should handle whitespace-only string', () => {
523
+ const result = parseInlineProfile(' \n\t ');
524
+ expect(result.providerName).toBe('');
525
+ expect(result.modelName).toBe('');
526
+ expect(result.error).toBeDefined();
527
+ expect(result.error).toContain('JSON');
528
+ });
529
+ /**
530
+ * @plan:PLAN-20251118-ISSUE533.P07
531
+ * @requirement:REQ-PROF-002.3
532
+ * @scenario: Validate missing provider field
533
+ * @given: JSON string without provider field
534
+ * @when: parseInlineProfile() is called
535
+ * @then: Returns validation error in ProfileApplicationResult
536
+ */
537
+ it('should reject profile missing provider field', () => {
538
+ const jsonString = JSON.stringify({
539
+ model: 'claude-3-5-sonnet-20241022',
540
+ key: 'sk-test-key-missing-provider',
541
+ });
542
+ const result = parseInlineProfile(jsonString);
543
+ expect(result.providerName).toBe('');
544
+ expect(result.modelName).toBe('');
545
+ expect(result.error).toBeDefined();
546
+ expect(result.error).toContain('provider');
547
+ });
548
+ /**
549
+ * @plan:PLAN-20251118-ISSUE533.P07
550
+ * @requirement:REQ-PROF-002.3
551
+ * @scenario: Validate missing model field
552
+ * @given: JSON string without model field
553
+ * @when: parseInlineProfile() is called
554
+ * @then: Returns validation error in ProfileApplicationResult
555
+ */
556
+ it('should reject profile missing model field', () => {
557
+ const jsonString = JSON.stringify({
558
+ provider: 'anthropic',
559
+ key: 'sk-test-key-missing-model',
560
+ });
561
+ const result = parseInlineProfile(jsonString);
562
+ expect(result.providerName).toBe('');
563
+ expect(result.modelName).toBe('');
564
+ expect(result.error).toBeDefined();
565
+ expect(result.error).toContain('model');
566
+ });
567
+ /**
568
+ * @plan:PLAN-20251118-ISSUE533.P07
569
+ * @requirement:REQ-PROF-002.3
570
+ * @scenario: Accept custom/alias provider names
571
+ * @given: JSON string with custom provider name (e.g., alias like "Synthetic")
572
+ * @when: parseInlineProfile() is called
573
+ * @then: Parses successfully - provider validation happens later during application
574
+ */
575
+ it('should accept custom provider names for aliases', () => {
576
+ const jsonString = JSON.stringify({
577
+ provider: 'Synthetic',
578
+ model: 'hf:zai-org/GLM-4.6',
579
+ key: 'sk-test-key-custom-provider',
580
+ });
581
+ const result = parseInlineProfile(jsonString);
582
+ expect(result.error).toBeUndefined();
583
+ expect(result.providerName).toBe('Synthetic');
584
+ expect(result.modelName).toBe('hf:zai-org/GLM-4.6');
585
+ expect(result.warnings).toEqual([]);
586
+ });
587
+ /**
588
+ * @plan:PLAN-20251118-ISSUE533.P07
589
+ * @requirement:REQ-PROF-002.3
590
+ * @scenario: Validate invalid field types
591
+ * @given: JSON string with temperature as string instead of number
592
+ * @when: parseInlineProfile() is called
593
+ * @then: Returns validation error in ProfileApplicationResult
594
+ */
595
+ it('should reject profile with invalid field types', () => {
596
+ const jsonString = JSON.stringify({
597
+ provider: 'anthropic',
598
+ model: 'claude-3-5-sonnet-20241022',
599
+ key: 'sk-test-key-type-error',
600
+ temperature: 'hot',
601
+ });
602
+ const result = parseInlineProfile(jsonString);
603
+ expect(result.providerName).toBe('');
604
+ expect(result.modelName).toBe('');
605
+ expect(result.error).toBeDefined();
606
+ expect(result.error).toContain('temperature');
607
+ });
608
+ /**
609
+ * @plan:PLAN-20251118-ISSUE533.P07
610
+ * @requirement:REQ-PROF-003.3
611
+ * @scenario: Reject nesting depth exceeding limit
612
+ * @given: JSON string with >5 levels of nesting
613
+ * @when: parseInlineProfile() is called
614
+ * @then: Returns security error in ProfileApplicationResult
615
+ */
616
+ it('should reject nesting depth exceeding limit of 5 levels', () => {
617
+ const deeplyNested = {
618
+ provider: 'anthropic',
619
+ model: 'claude-3-5-sonnet-20241022',
620
+ key: 'sk-test-key-deep',
621
+ level1: {
622
+ level2: {
623
+ level3: {
624
+ level4: {
625
+ level5: {
626
+ level6: 'too deep',
627
+ },
628
+ },
629
+ },
630
+ },
631
+ },
632
+ };
633
+ const result = parseInlineProfile(JSON.stringify(deeplyNested));
634
+ expect(result.providerName).toBe('');
635
+ expect(result.modelName).toBe('');
636
+ expect(result.error).toBeDefined();
637
+ expect(result.error).toContain('nesting');
638
+ });
639
+ /**
640
+ * @plan:PLAN-20251118-ISSUE533.P07
641
+ * @requirement:REQ-PROF-003.3
642
+ * @scenario: Accept nesting depth at limit
643
+ * @given: JSON string with exactly 5 levels of nesting
644
+ * @when: parseInlineProfile() is called
645
+ * @then: Returns successful ProfileApplicationResult
646
+ */
647
+ it('should accept nesting depth at limit of 5 levels', () => {
648
+ const atLimitNested = {
649
+ provider: 'anthropic',
650
+ model: 'claude-3-5-sonnet-20241022',
651
+ key: 'sk-test-key-limit',
652
+ level1: {
653
+ level2: {
654
+ level3: {
655
+ level4: {
656
+ level5: 'at limit',
657
+ },
658
+ },
659
+ },
660
+ },
661
+ };
662
+ const result = parseInlineProfile(JSON.stringify(atLimitNested));
663
+ expect(result.providerName).toBe('anthropic');
664
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
665
+ expect(result.warnings).toEqual([]);
666
+ });
667
+ /**
668
+ * @plan:PLAN-20251118-ISSUE533.P07
669
+ * @requirement:REQ-PROF-003.2
670
+ * @scenario: Reject profile with __proto__ field
671
+ * @given: JSON string containing __proto__ field
672
+ * @when: parseInlineProfile() is called
673
+ * @then: Returns security error in ProfileApplicationResult
674
+ */
675
+ it('should reject profile with disallowed __proto__ field', () => {
676
+ // JSON.stringify does not serialize __proto__, so construct the string directly
677
+ const jsonString = '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test-key-proto","__proto__":{"isAdmin":true}}';
678
+ const result = parseInlineProfile(jsonString);
679
+ expect(result.providerName).toBe('');
680
+ expect(result.modelName).toBe('');
681
+ expect(result.error).toBeDefined();
682
+ expect(result.error).toContain('__proto__');
683
+ });
684
+ /**
685
+ * @plan:PLAN-20251118-ISSUE533.P07
686
+ * @requirement:REQ-PROF-003.2
687
+ * @scenario: Reject profile with constructor field
688
+ * @given: JSON string containing constructor field
689
+ * @when: parseInlineProfile() is called
690
+ * @then: Returns security error in ProfileApplicationResult
691
+ */
692
+ it('should reject profile with disallowed constructor field', () => {
693
+ const jsonString = JSON.stringify({
694
+ provider: 'anthropic',
695
+ model: 'claude-3-5-sonnet-20241022',
696
+ key: 'sk-test-key-constructor',
697
+ constructor: { prototype: {} },
698
+ });
699
+ const result = parseInlineProfile(jsonString);
700
+ expect(result.providerName).toBe('');
701
+ expect(result.modelName).toBe('');
702
+ expect(result.error).toBeDefined();
703
+ expect(result.error).toContain('constructor');
704
+ });
705
+ });
706
+ describe('applyBootstrapProfile() with --profile @plan:PLAN-20251118-ISSUE533.P09', () => {
707
+ let mockSettingsService;
708
+ let mockConfig;
709
+ let mockOAuthManager;
710
+ beforeEach(() => {
711
+ vi.clearAllMocks();
712
+ mockSettingsService = {
713
+ listProfiles: vi.fn(),
714
+ getProfile: vi.fn(),
715
+ getProfileByID: vi.fn(),
716
+ isLoaded: vi.fn().mockReturnValue(true),
717
+ };
718
+ mockConfig = {
719
+ profiles: [],
720
+ };
721
+ mockOAuthManager = {};
722
+ });
723
+ afterEach(() => {
724
+ vi.clearAllMocks();
725
+ });
726
+ // Group 1: Basic Profile Application (4 tests)
727
+ /**
728
+ * @plan:PLAN-20251118-ISSUE533.P09
729
+ * @requirement:REQ-INT-001.1
730
+ * @scenario: Apply a complete inline profile successfully
731
+ * @given: profileJson contains provider, model, and key
732
+ * @when: applyBootstrapProfile() is called
733
+ * @then: Returns bootstrap result with provider and model set correctly
734
+ */
735
+ it('should apply inline profile successfully with provider, model, and key', () => {
736
+ const args = {
737
+ profileName: null,
738
+ profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test123"}',
739
+ providerOverride: null,
740
+ modelOverride: null,
741
+ keyOverride: null,
742
+ keyfileOverride: null,
743
+ baseurlOverride: null,
744
+ setOverrides: null,
745
+ };
746
+ const parsed = parseArgsWithMeta(args, {
747
+ settingsService: mockSettingsService,
748
+ config: mockConfig,
749
+ oauthManager: mockOAuthManager,
750
+ });
751
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
752
+ expect(result.providerName).toBe('openai');
753
+ expect(result.modelName).toBe('gpt-4');
754
+ expect(result.warnings).toEqual([]);
755
+ });
756
+ /**
757
+ * @plan:PLAN-20251118-ISSUE533.P09
758
+ * @requirement:REQ-INT-001.1
759
+ * @scenario: Apply Anthropic inline profile
760
+ * @given: profileJson contains Anthropic provider and claude model
761
+ * @when: applyBootstrapProfile() is called
762
+ * @then: Returns bootstrap result with Anthropic provider and model
763
+ */
764
+ it('should apply Anthropic inline profile', () => {
765
+ const args = {
766
+ profileName: null,
767
+ profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-ant-test"}',
768
+ providerOverride: null,
769
+ modelOverride: null,
770
+ keyOverride: null,
771
+ keyfileOverride: null,
772
+ baseurlOverride: null,
773
+ setOverrides: null,
774
+ };
775
+ const parsed = parseArgsWithMeta(args, {
776
+ settingsService: mockSettingsService,
777
+ config: mockConfig,
778
+ oauthManager: mockOAuthManager,
779
+ });
780
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
781
+ expect(result.providerName).toBe('anthropic');
782
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
783
+ expect(result.warnings).toEqual([]);
784
+ });
785
+ /**
786
+ * @plan:PLAN-20251118-ISSUE533.P09
787
+ * @requirement:REQ-INT-001.1
788
+ * @scenario: Apply inline profile with optional fields
789
+ * @given: profileJson contains provider, model, key, temperature, and maxTokens
790
+ * @when: applyBootstrapProfile() is called
791
+ * @then: Returns bootstrap result with all fields set correctly
792
+ */
793
+ it('should apply inline profile with optional fields (temperature, maxTokens)', () => {
794
+ const args = {
795
+ profileName: null,
796
+ profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test","temperature":0.7,"maxTokens":2000}',
797
+ providerOverride: null,
798
+ modelOverride: null,
799
+ keyOverride: null,
800
+ keyfileOverride: null,
801
+ baseurlOverride: null,
802
+ setOverrides: null,
803
+ };
804
+ const parsed = parseArgsWithMeta(args, {
805
+ settingsService: mockSettingsService,
806
+ config: mockConfig,
807
+ oauthManager: mockOAuthManager,
808
+ });
809
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
810
+ expect(result.providerName).toBe('openai');
811
+ expect(result.modelName).toBe('gpt-4');
812
+ expect(result.warnings).toEqual([]);
813
+ });
814
+ /**
815
+ * @plan:PLAN-20251118-ISSUE533.P09
816
+ * @requirement:REQ-INT-001.1
817
+ * @scenario: Return empty result when no profile specified
818
+ * @given: Both profileName and profileJson are null
819
+ * @when: applyBootstrapProfile() is called
820
+ * @then: Returns empty bootstrap result with no provider or model
821
+ */
822
+ it('should return empty result when no profile specified', () => {
823
+ const args = {
824
+ profileName: null,
825
+ profileJson: null,
826
+ providerOverride: null,
827
+ modelOverride: null,
828
+ keyOverride: null,
829
+ keyfileOverride: null,
830
+ baseurlOverride: null,
831
+ setOverrides: null,
832
+ };
833
+ const parsed = parseArgsWithMeta(args, {
834
+ settingsService: mockSettingsService,
835
+ config: mockConfig,
836
+ oauthManager: mockOAuthManager,
837
+ });
838
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
839
+ expect(result.providerName).toBeNull();
840
+ expect(result.modelName).toBeNull();
841
+ expect(result.warnings).toEqual([]);
842
+ });
843
+ // Group 2: Override Precedence (4 tests)
844
+ /**
845
+ * @plan:PLAN-20251118-ISSUE533.P09
846
+ * @requirement:REQ-INT-001.1
847
+ * @scenario: Model override takes precedence over inline profile
848
+ * @given: profileJson contains model "gpt-4" and modelOverride is "gpt-3.5-turbo"
849
+ * @when: applyBootstrapProfile() is called
850
+ * @then: Returns bootstrap result with model "gpt-3.5-turbo" from override
851
+ */
852
+ it('should apply model override over inline profile model', () => {
853
+ const args = {
854
+ profileName: null,
855
+ profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
856
+ providerOverride: null,
857
+ modelOverride: 'gpt-3.5-turbo',
858
+ keyOverride: null,
859
+ keyfileOverride: null,
860
+ baseurlOverride: null,
861
+ setOverrides: null,
862
+ };
863
+ const parsed = parseArgsWithMeta(args, {
864
+ settingsService: mockSettingsService,
865
+ config: mockConfig,
866
+ oauthManager: mockOAuthManager,
867
+ });
868
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
869
+ expect(result.providerName).toBe('openai');
870
+ expect(result.modelName).toBe('gpt-3.5-turbo');
871
+ expect(result.warnings).toEqual([]);
872
+ });
873
+ /**
874
+ * @plan:PLAN-20251118-ISSUE533.P09
875
+ * @requirement:REQ-INT-001.1
876
+ * @scenario: Provider override takes precedence over inline profile
877
+ * @given: profileJson contains provider "openai" and providerOverride is "anthropic"
878
+ * @when: applyBootstrapProfile() is called
879
+ * @then: Returns bootstrap result with provider "anthropic" from override
880
+ */
881
+ it('should apply provider override over inline profile provider', () => {
882
+ const args = {
883
+ profileName: null,
884
+ profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
885
+ providerOverride: 'anthropic',
886
+ modelOverride: null,
887
+ keyOverride: null,
888
+ keyfileOverride: null,
889
+ baseurlOverride: null,
890
+ setOverrides: null,
891
+ };
892
+ const parsed = parseArgsWithMeta(args, {
893
+ settingsService: mockSettingsService,
894
+ config: mockConfig,
895
+ oauthManager: mockOAuthManager,
896
+ });
897
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
898
+ expect(result.providerName).toBe('anthropic');
899
+ expect(result.modelName).toBe('gpt-4');
900
+ expect(result.warnings.length).toBeGreaterThan(0);
901
+ });
902
+ /**
903
+ * @plan:PLAN-20251118-ISSUE533.P09
904
+ * @requirement:REQ-INT-001.1
905
+ * @scenario: Key override with warning generated
906
+ * @given: profileJson contains key and keyOverride is provided
907
+ * @when: applyBootstrapProfile() is called
908
+ * @then: Returns bootstrap result with warning about key override
909
+ */
910
+ it('should generate warning when key override is applied', () => {
911
+ const args = {
912
+ profileName: null,
913
+ profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
914
+ providerOverride: null,
915
+ modelOverride: null,
916
+ keyOverride: 'sk-override',
917
+ keyfileOverride: null,
918
+ baseurlOverride: null,
919
+ setOverrides: null,
920
+ };
921
+ const parsed = parseArgsWithMeta(args, {
922
+ settingsService: mockSettingsService,
923
+ config: mockConfig,
924
+ oauthManager: mockOAuthManager,
925
+ });
926
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
927
+ expect(result.providerName).toBe('openai');
928
+ expect(result.modelName).toBe('gpt-4');
929
+ expect(result.warnings.length).toBeGreaterThan(0);
930
+ });
931
+ /**
932
+ * @plan:PLAN-20251118-ISSUE533.P09
933
+ * @requirement:REQ-INT-001.1
934
+ * @scenario: Multiple overrides with warnings
935
+ * @given: profileJson contains provider, model, key and all have overrides
936
+ * @when: applyBootstrapProfile() is called
937
+ * @then: Returns bootstrap result with multiple warnings for overrides
938
+ */
939
+ it('should generate multiple warnings when multiple overrides are applied', () => {
940
+ const args = {
941
+ profileName: null,
942
+ profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
943
+ providerOverride: 'anthropic',
944
+ modelOverride: 'claude-3-5-sonnet-20241022',
945
+ keyOverride: 'sk-override',
946
+ keyfileOverride: null,
947
+ baseurlOverride: null,
948
+ setOverrides: null,
949
+ };
950
+ const parsed = parseArgsWithMeta(args, {
951
+ settingsService: mockSettingsService,
952
+ config: mockConfig,
953
+ oauthManager: mockOAuthManager,
954
+ });
955
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
956
+ expect(result.providerName).toBe('anthropic');
957
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
958
+ expect(result.warnings.length).toBeGreaterThan(0);
959
+ });
960
+ // Group 3: Validation Error Handling (2 tests)
961
+ /**
962
+ * @plan:PLAN-20251118-ISSUE533.P09
963
+ * @requirement:REQ-INT-001.1
964
+ * @scenario: Throw error for invalid JSON in profileJson
965
+ * @given: profileJson contains invalid JSON syntax
966
+ * @when: applyBootstrapProfile() is called
967
+ * @then: Throws an error about invalid JSON
968
+ */
969
+ it('should throw error for invalid JSON in profileJson', () => {
970
+ const args = {
971
+ profileName: null,
972
+ profileJson: '{invalid json}',
973
+ providerOverride: null,
974
+ modelOverride: null,
975
+ keyOverride: null,
976
+ keyfileOverride: null,
977
+ baseurlOverride: null,
978
+ setOverrides: null,
979
+ };
980
+ expect(() => {
981
+ parseArgsWithMeta(args, {
982
+ settingsService: mockSettingsService,
983
+ config: mockConfig,
984
+ oauthManager: mockOAuthManager,
985
+ });
986
+ }).toThrow();
987
+ });
988
+ /**
989
+ * @plan:PLAN-20251118-ISSUE533.P09
990
+ * @requirement:REQ-INT-001.1
991
+ * @scenario: Throw error for profile validation failure
992
+ * @given: profileJson is valid JSON but missing required field (provider)
993
+ * @when: applyBootstrapProfile() is called
994
+ * @then: Throws an error about missing required field
995
+ */
996
+ it('should throw error for profile validation failure (missing required field)', () => {
997
+ const args = {
998
+ profileName: null,
999
+ profileJson: '{"model":"gpt-4","key":"sk-test"}',
1000
+ providerOverride: null,
1001
+ modelOverride: null,
1002
+ keyOverride: null,
1003
+ keyfileOverride: null,
1004
+ baseurlOverride: null,
1005
+ setOverrides: null,
1006
+ };
1007
+ expect(() => {
1008
+ parseArgsWithMeta(args, {
1009
+ settingsService: mockSettingsService,
1010
+ config: mockConfig,
1011
+ oauthManager: mockOAuthManager,
1012
+ });
1013
+ }).toThrow();
1014
+ });
1015
+ // Group 4: Backward Compatibility (2 tests)
1016
+ /**
1017
+ * @plan:PLAN-20251118-ISSUE533.P09
1018
+ * @requirement:REQ-INT-001.1
1019
+ * @scenario: Maintain --profile-load behavior
1020
+ * @given: profileName is specified (not profileJson)
1021
+ * @when: applyBootstrapProfile() is called
1022
+ * @then: Returns bootstrap result from named profile as before
1023
+ */
1024
+ it('should maintain --profile-load behavior when profileName is specified', () => {
1025
+ const mockProfile = {
1026
+ id: 'test-profile',
1027
+ name: 'test-profile',
1028
+ provider: 'openai',
1029
+ model: 'gpt-4',
1030
+ key: 'sk-test',
1031
+ };
1032
+ mockSettingsService.getProfile.mockReturnValue(mockProfile);
1033
+ const args = {
1034
+ profileName: 'test-profile',
1035
+ profileJson: null,
1036
+ providerOverride: null,
1037
+ modelOverride: null,
1038
+ keyOverride: null,
1039
+ keyfileOverride: null,
1040
+ baseurlOverride: null,
1041
+ setOverrides: null,
1042
+ };
1043
+ const parsed = parseArgsWithMeta(args, {
1044
+ settingsService: mockSettingsService,
1045
+ config: mockConfig,
1046
+ oauthManager: mockOAuthManager,
1047
+ });
1048
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1049
+ expect(result.providerName).toBe('openai');
1050
+ expect(result.modelName).toBe('gpt-4');
1051
+ expect(mockSettingsService.getProfile).toHaveBeenCalledWith('test-profile');
1052
+ });
1053
+ /**
1054
+ * @plan:PLAN-20251118-ISSUE533.P09
1055
+ * @requirement:REQ-INT-001.1
1056
+ * @scenario: Apply overrides without profile
1057
+ * @given: No profileName or profileJson, but overrides are provided
1058
+ * @when: applyBootstrapProfile() is called
1059
+ * @then: Returns bootstrap result with command-line overrides only
1060
+ */
1061
+ it('should apply overrides without profile (command-line only overrides)', () => {
1062
+ const args = {
1063
+ profileName: null,
1064
+ profileJson: null,
1065
+ providerOverride: 'openai',
1066
+ modelOverride: 'gpt-4',
1067
+ keyOverride: null,
1068
+ keyfileOverride: null,
1069
+ baseurlOverride: null,
1070
+ setOverrides: null,
1071
+ };
1072
+ const parsed = parseArgsWithMeta(args, {
1073
+ settingsService: mockSettingsService,
1074
+ config: mockConfig,
1075
+ oauthManager: mockOAuthManager,
1076
+ });
1077
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1078
+ expect(result.providerName).toBe('openai');
1079
+ expect(result.modelName).toBe('gpt-4');
1080
+ expect(result.warnings).toEqual([]);
1081
+ });
1082
+ });
1083
+ /**
1084
+ * @plan:PLAN-20251118-ISSUE533.P09
1085
+ * Test suite for applyBootstrapProfile() with --profile flag integration (alternative mock setup)
1086
+ */
1087
+ describe('applyBootstrapProfile() with --profile - alternative tests @plan:PLAN-20251118-ISSUE533.P09', () => {
1088
+ let mockSettings;
1089
+ let mockConfig;
1090
+ beforeEach(() => {
1091
+ vi.clearAllMocks();
1092
+ mockConfig = { profiles: [] };
1093
+ mockSettings = {
1094
+ listProfiles: vi.fn(),
1095
+ getProfile: vi.fn(),
1096
+ getProfileByID: vi.fn(),
1097
+ isLoaded: vi.fn().mockReturnValue(true),
1098
+ };
1099
+ });
1100
+ afterEach(() => {
1101
+ vi.clearAllMocks();
1102
+ });
1103
+ /**
1104
+ * Group 1: Basic Profile Application (4 tests)
1105
+ */
1106
+ /**
1107
+ * @plan:PLAN-20251118-ISSUE533.P09
1108
+ * @requirement:REQ-INT-001.1
1109
+ * @scenario: Apply inline profile without overrides
1110
+ * @given: profileJson with provider, model, key
1111
+ * @when: applyBootstrapProfile() called
1112
+ * @then: Returns BootstrapRuntimeState with profile values
1113
+ */
1114
+ it('should apply inline profile without overrides', () => {
1115
+ const args = {
1116
+ profileName: null,
1117
+ profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
1118
+ providerOverride: null,
1119
+ modelOverride: null,
1120
+ keyOverride: null,
1121
+ keyfileOverride: null,
1122
+ baseurlOverride: null,
1123
+ setOverrides: null,
1124
+ };
1125
+ const parsed = parseArgsWithMeta(args, {
1126
+ settingsService: mockSettings,
1127
+ config: mockConfig,
1128
+ oauthManager: {},
1129
+ });
1130
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1131
+ expect(result.providerName).toBe('anthropic');
1132
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
1133
+ });
1134
+ /**
1135
+ * @plan:PLAN-20251118-ISSUE533.P09
1136
+ * @requirement:REQ-INT-001.2
1137
+ * @scenario: Apply inline profile with provider override
1138
+ * @given: profileJson with anthropic + --provider openai
1139
+ * @when: applyBootstrapProfile() called
1140
+ * @then: Override takes precedence
1141
+ */
1142
+ it('should apply provider override over inline profile', () => {
1143
+ const args = {
1144
+ profileName: null,
1145
+ profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
1146
+ providerOverride: 'openai',
1147
+ modelOverride: null,
1148
+ keyOverride: null,
1149
+ keyfileOverride: null,
1150
+ baseurlOverride: null,
1151
+ setOverrides: null,
1152
+ };
1153
+ const parsed = parseArgsWithMeta(args, {
1154
+ settingsService: mockSettings,
1155
+ config: mockConfig,
1156
+ oauthManager: {},
1157
+ });
1158
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1159
+ expect(result.providerName).toBe('openai');
1160
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022'); // Original model preserved
1161
+ });
1162
+ /**
1163
+ * @plan:PLAN-20251118-ISSUE533.P09
1164
+ * @requirement:REQ-INT-001.3
1165
+ * @scenario: Apply inline profile with model override
1166
+ * @given: profileJson with sonnet + --model gpt-4
1167
+ * @when: applyBootstrapProfile() called
1168
+ * @then: Override takes precedence
1169
+ */
1170
+ it('should apply model override over inline profile', () => {
1171
+ const args = {
1172
+ profileName: null,
1173
+ profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
1174
+ providerOverride: null,
1175
+ modelOverride: 'gpt-4',
1176
+ keyOverride: null,
1177
+ keyfileOverride: null,
1178
+ baseurlOverride: null,
1179
+ setOverrides: null,
1180
+ };
1181
+ const parsed = parseArgsWithMeta(args, {
1182
+ settingsService: mockSettings,
1183
+ config: mockConfig,
1184
+ oauthManager: {},
1185
+ });
1186
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1187
+ expect(result.providerName).toBe('anthropic'); // Original provider preserved
1188
+ expect(result.modelName).toBe('gpt-4');
1189
+ });
1190
+ /**
1191
+ * @plan:PLAN-20251118-ISSUE533.P09
1192
+ * @requirement:REQ-INT-001.4
1193
+ * @scenario: Apply inline profile with key override
1194
+ * @given: profileJson with sk-test + --key sk-override
1195
+ * @when: applyBootstrapProfile() called
1196
+ * @then: Override takes precedence
1197
+ */
1198
+ it('should apply key override over inline profile', () => {
1199
+ const args = {
1200
+ profileName: null,
1201
+ profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
1202
+ providerOverride: null,
1203
+ modelOverride: null,
1204
+ keyOverride: 'sk-override',
1205
+ keyfileOverride: null,
1206
+ baseurlOverride: null,
1207
+ setOverrides: null,
1208
+ };
1209
+ const parsed = parseArgsWithMeta(args, {
1210
+ settingsService: mockSettings,
1211
+ config: mockConfig,
1212
+ oauthManager: {},
1213
+ });
1214
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1215
+ expect(result.providerName).toBe('anthropic');
1216
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
1217
+ });
1218
+ /**
1219
+ * Group 2: Profile Source Priority (4 tests)
1220
+ */
1221
+ /**
1222
+ * @plan:PLAN-20251118-ISSUE533.P09
1223
+ * @requirement:REQ-INT-002.1
1224
+ * @scenario: Inline profile takes precedence over named profile
1225
+ * @given: Both --profile myprofile and --profile {...}
1226
+ * @when: applyBootstrapProfile() called
1227
+ * @then: Inline profile values used
1228
+ */
1229
+ it('should prioritize inline profile over named profile', () => {
1230
+ const args = {
1231
+ profileName: 'myprofile',
1232
+ profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-inline"}',
1233
+ providerOverride: null,
1234
+ modelOverride: null,
1235
+ keyOverride: null,
1236
+ keyfileOverride: null,
1237
+ baseurlOverride: null,
1238
+ setOverrides: null,
1239
+ };
1240
+ mockSettings.getProfile.mockReturnValue({
1241
+ provider: 'anthropic',
1242
+ model: 'claude-3-5-sonnet-20241022',
1243
+ key: 'sk-named',
1244
+ });
1245
+ const parsed = parseArgsWithMeta(args, {
1246
+ settingsService: mockSettings,
1247
+ config: mockConfig,
1248
+ oauthManager: {},
1249
+ });
1250
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1251
+ expect(result.providerName).toBe('openai');
1252
+ expect(result.modelName).toBe('gpt-4');
1253
+ });
1254
+ /**
1255
+ * @plan:PLAN-20251118-ISSUE533.P09
1256
+ * @requirement:REQ-INT-002.2
1257
+ * @scenario: Named profile used when no inline profile
1258
+ * @given: --profile myprofile only
1259
+ * @when: applyBootstrapProfile() called
1260
+ * @then: Named profile values used
1261
+ */
1262
+ it('should use named profile when no inline profile provided', () => {
1263
+ const args = {
1264
+ profileName: 'myprofile',
1265
+ profileJson: null,
1266
+ providerOverride: null,
1267
+ modelOverride: null,
1268
+ keyOverride: null,
1269
+ keyfileOverride: null,
1270
+ baseurlOverride: null,
1271
+ setOverrides: null,
1272
+ };
1273
+ mockSettings.getProfile.mockReturnValue({
1274
+ provider: 'anthropic',
1275
+ model: 'claude-3-5-sonnet-20241022',
1276
+ key: 'sk-named',
1277
+ });
1278
+ const parsed = parseArgsWithMeta(args, {
1279
+ settingsService: mockSettings,
1280
+ config: mockConfig,
1281
+ oauthManager: {},
1282
+ });
1283
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1284
+ expect(result.providerName).toBe('anthropic');
1285
+ expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
1286
+ });
1287
+ /**
1288
+ * @plan:PLAN-20251118-ISSUE533.P09
1289
+ * @requirement:REQ-INT-002.3
1290
+ * @scenario: Override flags work with inline profiles
1291
+ * @given: --profile {...} + --model override
1292
+ * @when: applyBootstrapProfile() called
1293
+ * @then: Override takes precedence over inline
1294
+ */
1295
+ it('should apply overrides to inline profile values', () => {
1296
+ const args = {
1297
+ profileName: null,
1298
+ profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
1299
+ providerOverride: 'openai',
1300
+ modelOverride: 'gpt-4',
1301
+ keyOverride: 'sk-override',
1302
+ keyfileOverride: null,
1303
+ baseurlOverride: null,
1304
+ setOverrides: null,
1305
+ };
1306
+ const parsed = parseArgsWithMeta(args, {
1307
+ settingsService: mockSettings,
1308
+ config: mockConfig,
1309
+ oauthManager: {},
1310
+ });
1311
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1312
+ expect(result.providerName).toBe('openai');
1313
+ expect(result.modelName).toBe('gpt-4');
1314
+ });
1315
+ /**
1316
+ * @plan:PLAN-20251118-ISSUE533.P09
1317
+ * @requirement:REQ-INT-002.4
1318
+ * @scenario: Override flags work with named profiles
1319
+ * @given: --profile myprofile + --model override
1320
+ * @when: applyBootstrapProfile() called
1321
+ * @then: Override takes precedence over named
1322
+ */
1323
+ it('should apply overrides to named profile values', () => {
1324
+ const args = {
1325
+ profileName: 'myprofile',
1326
+ profileJson: null,
1327
+ providerOverride: 'openai',
1328
+ modelOverride: 'gpt-4',
1329
+ keyOverride: null,
1330
+ keyfileOverride: null,
1331
+ baseurlOverride: null,
1332
+ setOverrides: null,
1333
+ };
1334
+ mockSettings.getProfile.mockReturnValue({
1335
+ provider: 'anthropic',
1336
+ model: 'claude-3-5-sonnet-20241022',
1337
+ key: 'sk-named',
1338
+ });
1339
+ const parsed = parseArgsWithMeta(args, {
1340
+ settingsService: mockSettings,
1341
+ config: mockConfig,
1342
+ oauthManager: {},
1343
+ });
1344
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1345
+ expect(result.providerName).toBe('openai');
1346
+ expect(result.modelName).toBe('gpt-4');
1347
+ });
1348
+ /**
1349
+ * Group 3: Error Handling (4 tests)
1350
+ */
1351
+ /**
1352
+ * @plan:PLAN-20251118-ISSUE533.P09
1353
+ * @requirement:REQ-INT-003.1
1354
+ * @scenario: Invalid JSON in inline profile
1355
+ * @given: --profile {bad json}
1356
+ * @when: applyBootstrapProfile() called
1357
+ * @then: Throws ProfileBootstrapError
1358
+ */
1359
+ it('should throw error for invalid JSON in inline profile', () => {
1360
+ const args = {
1361
+ profileName: null,
1362
+ profileJson: '{bad json}',
1363
+ providerOverride: null,
1364
+ modelOverride: null,
1365
+ keyOverride: null,
1366
+ keyfileOverride: null,
1367
+ baseurlOverride: null,
1368
+ setOverrides: null,
1369
+ };
1370
+ expect(() => {
1371
+ parseArgsWithMeta(args, {
1372
+ settingsService: mockSettings,
1373
+ config: mockConfig,
1374
+ oauthManager: {},
1375
+ });
1376
+ }).toThrow();
1377
+ });
1378
+ /**
1379
+ * @plan:PLAN-20251118-ISSUE533.P09
1380
+ * @requirement:REQ-INT-003.2
1381
+ * @scenario: Missing required fields in inline profile
1382
+ * @given: --profile {"provider":"anthropic"} (no model/key)
1383
+ * @when: applyBootstrapProfile() called
1384
+ * @then: Throws ProfileBootstrapError
1385
+ */
1386
+ it('should throw error for missing required fields in inline profile', () => {
1387
+ const args = {
1388
+ profileName: null,
1389
+ profileJson: '{"provider":"anthropic"}',
1390
+ providerOverride: null,
1391
+ modelOverride: null,
1392
+ keyOverride: null,
1393
+ keyfileOverride: null,
1394
+ baseurlOverride: null,
1395
+ setOverrides: null,
1396
+ };
1397
+ expect(() => {
1398
+ parseArgsWithMeta(args, {
1399
+ settingsService: mockSettings,
1400
+ config: mockConfig,
1401
+ oauthManager: {},
1402
+ });
1403
+ }).toThrow();
1404
+ });
1405
+ /**
1406
+ * @plan:PLAN-20251118-ISSUE533.P09
1407
+ * @requirement:REQ-INT-003.3
1408
+ * @scenario: Named profile not found
1409
+ * @given: --profile nonexistent
1410
+ * @when: applyBootstrapProfile() called
1411
+ * @then: Throws ProfileBootstrapError
1412
+ */
1413
+ it('should throw error when named profile not found', () => {
1414
+ const args = {
1415
+ profileName: 'nonexistent',
1416
+ profileJson: null,
1417
+ providerOverride: null,
1418
+ modelOverride: null,
1419
+ keyOverride: null,
1420
+ keyfileOverride: null,
1421
+ baseurlOverride: null,
1422
+ setOverrides: null,
1423
+ };
1424
+ mockSettings.getProfile.mockReturnValue(null);
1425
+ const parsed = parseArgsWithMeta(args, {
1426
+ settingsService: mockSettings,
1427
+ config: mockConfig,
1428
+ oauthManager: {},
1429
+ });
1430
+ const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
1431
+ // When profile is not found, the result should have empty provider/model and a warning
1432
+ expect(result.providerName).toBe('');
1433
+ expect(result.modelName).toBe('');
1434
+ expect(result.warnings.length).toBeGreaterThan(0);
1435
+ expect(result.warnings[0]).toContain('not found');
1436
+ });
1437
+ /**
1438
+ * @plan:PLAN-20251118-ISSUE533.P09
1439
+ * @requirement:REQ-INT-003.4
1440
+ * @scenario: Empty inline profile object
1441
+ * @given: --profile {}
1442
+ * @when: applyBootstrapProfile() called
1443
+ * @then: Throws ProfileBootstrapError
1444
+ */
1445
+ it('should throw error for empty inline profile object', () => {
1446
+ const args = {
1447
+ profileName: null,
1448
+ profileJson: '{}',
1449
+ providerOverride: null,
1450
+ modelOverride: null,
1451
+ keyOverride: null,
1452
+ keyfileOverride: null,
1453
+ baseurlOverride: null,
1454
+ setOverrides: null,
1455
+ };
1456
+ expect(() => {
1457
+ parseArgsWithMeta(args, {
1458
+ settingsService: mockSettings,
1459
+ config: mockConfig,
1460
+ oauthManager: {},
1461
+ });
1462
+ }).toThrow();
1463
+ });
1464
+ });
115
1465
  //# sourceMappingURL=profileBootstrap.test.js.map