fss-link 1.0.40 → 1.0.45

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 (428) hide show
  1. package/dist/commands/mcp/add.test.ts +122 -0
  2. package/dist/commands/mcp/add.ts +222 -0
  3. package/dist/commands/mcp/list.test.ts +154 -0
  4. package/dist/commands/mcp/list.ts +139 -0
  5. package/dist/commands/mcp/remove.test.ts +69 -0
  6. package/dist/commands/mcp/remove.ts +60 -0
  7. package/dist/commands/mcp.test.ts +55 -0
  8. package/dist/commands/mcp.ts +27 -0
  9. package/dist/config/apiValidation.test.ts +118 -0
  10. package/dist/config/auth.test.ts +79 -0
  11. package/dist/config/auth.ts +100 -0
  12. package/dist/config/config.integration.test.ts +407 -0
  13. package/dist/config/config.test.ts +1952 -0
  14. package/dist/config/config.ts +690 -0
  15. package/dist/config/database.test.ts +96 -0
  16. package/dist/config/database.ts +752 -0
  17. package/dist/config/extension.test.ts +236 -0
  18. package/dist/config/extension.ts +180 -0
  19. package/dist/config/keyBindings.test.ts +62 -0
  20. package/dist/config/keyBindings.ts +184 -0
  21. package/dist/config/modelManager.ts +275 -0
  22. package/dist/config/providerManager.ts +244 -0
  23. package/dist/config/providerPersistence.test.ts +377 -0
  24. package/dist/config/providerPersistence.ts +105 -0
  25. package/dist/config/sandboxConfig.ts +107 -0
  26. package/dist/config/settings.test.ts +1424 -0
  27. package/dist/config/settings.ts +517 -0
  28. package/dist/config/settingsSchema.test.ts +252 -0
  29. package/dist/config/settingsSchema.ts +728 -0
  30. package/dist/config/trustedFolders.test.ts +208 -0
  31. package/dist/config/trustedFolders.ts +167 -0
  32. package/dist/gemini.test.tsx +252 -0
  33. package/dist/gemini.tsx +357 -0
  34. package/dist/generated/git-commit.ts +10 -0
  35. package/dist/index.ts +21 -0
  36. package/dist/nonInteractiveCli.test.ts +276 -0
  37. package/dist/nonInteractiveCli.ts +143 -0
  38. package/dist/package.json +87 -87
  39. package/dist/patches/is-in-ci.ts +17 -0
  40. package/dist/services/BuiltinCommandLoader.test.ts +127 -0
  41. package/dist/services/BuiltinCommandLoader.ts +95 -0
  42. package/dist/services/CommandService.test.ts +352 -0
  43. package/dist/services/CommandService.ts +103 -0
  44. package/dist/services/FileCommandLoader.test.ts +1002 -0
  45. package/dist/services/FileCommandLoader.ts +289 -0
  46. package/dist/services/McpPromptLoader.ts +231 -0
  47. package/dist/services/SearchEngineConfigProvider.ts +100 -0
  48. package/dist/services/prompt-processors/argumentProcessor.test.ts +41 -0
  49. package/dist/services/prompt-processors/argumentProcessor.ts +23 -0
  50. package/dist/services/prompt-processors/shellProcessor.test.ts +709 -0
  51. package/dist/services/prompt-processors/shellProcessor.ts +248 -0
  52. package/dist/services/prompt-processors/types.ts +44 -0
  53. package/dist/services/types.ts +24 -0
  54. package/dist/src/config/apiValidation.test.d.ts +6 -0
  55. package/dist/src/config/apiValidation.test.js +99 -0
  56. package/dist/src/config/apiValidation.test.js.map +1 -0
  57. package/dist/src/config/database.d.ts +32 -0
  58. package/dist/src/config/database.js +281 -2
  59. package/dist/src/config/database.js.map +1 -1
  60. package/dist/src/config/database.test.d.ts +6 -0
  61. package/dist/src/config/database.test.js +80 -0
  62. package/dist/src/config/database.test.js.map +1 -0
  63. package/dist/src/config/providerManager.d.ts +74 -0
  64. package/dist/src/config/providerManager.js +203 -0
  65. package/dist/src/config/providerManager.js.map +1 -0
  66. package/dist/src/config/providerPersistence.d.ts +75 -0
  67. package/dist/src/config/providerPersistence.js +55 -0
  68. package/dist/src/config/providerPersistence.js.map +1 -0
  69. package/dist/src/config/providerPersistence.test.d.ts +6 -0
  70. package/dist/src/config/providerPersistence.test.js +283 -0
  71. package/dist/src/config/providerPersistence.test.js.map +1 -0
  72. package/dist/src/config/settingsSchema.d.ts +9 -0
  73. package/dist/src/config/settingsSchema.js +9 -0
  74. package/dist/src/config/settingsSchema.js.map +1 -1
  75. package/dist/src/generated/git-commit.d.ts +1 -1
  76. package/dist/src/generated/git-commit.js +1 -1
  77. package/dist/src/services/BuiltinCommandLoader.js +2 -0
  78. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  79. package/dist/src/ui/App.js +14 -2
  80. package/dist/src/ui/App.js.map +1 -1
  81. package/dist/src/ui/commands/contextCommand.d.ts +7 -0
  82. package/dist/src/ui/commands/contextCommand.js +115 -0
  83. package/dist/src/ui/commands/contextCommand.js.map +1 -0
  84. package/dist/src/ui/components/ContextUsageDisplay.d.ts +3 -1
  85. package/dist/src/ui/components/ContextUsageDisplay.js +43 -3
  86. package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
  87. package/dist/src/ui/components/Footer.d.ts +1 -0
  88. package/dist/src/ui/components/Footer.js +2 -2
  89. package/dist/src/ui/components/Footer.js.map +1 -1
  90. package/dist/src/ui/components/GeminiKeyDialog.d.ts +11 -0
  91. package/dist/src/ui/components/GeminiKeyDialog.js +156 -0
  92. package/dist/src/ui/components/GeminiKeyDialog.js.map +1 -0
  93. package/dist/src/ui/components/OpenAIEndpointDialog.d.ts +19 -0
  94. package/dist/src/ui/components/OpenAIEndpointDialog.js +163 -0
  95. package/dist/src/ui/components/OpenAIEndpointDialog.js.map +1 -0
  96. package/dist/src/ui/components/WelcomeBackDialog.d.ts +36 -0
  97. package/dist/src/ui/components/WelcomeBackDialog.js +109 -0
  98. package/dist/src/ui/components/WelcomeBackDialog.js.map +1 -0
  99. package/dist/src/ui/hooks/useWelcomeBack.d.ts +52 -0
  100. package/dist/src/ui/hooks/useWelcomeBack.js +214 -0
  101. package/dist/src/ui/hooks/useWelcomeBack.js.map +1 -0
  102. package/dist/src/zed-integration/schema.d.ts +1516 -1516
  103. package/dist/test-setup.ts +12 -0
  104. package/dist/test-utils/customMatchers.ts +65 -0
  105. package/dist/test-utils/mockCommandContext.test.ts +62 -0
  106. package/dist/test-utils/mockCommandContext.ts +105 -0
  107. package/dist/test-utils/render.tsx +18 -0
  108. package/dist/tsconfig.tsbuildinfo +1 -1
  109. package/dist/ui/App.test.tsx +2181 -0
  110. package/dist/ui/App.tsx +1344 -0
  111. package/dist/ui/IdeIntegrationNudge.tsx +98 -0
  112. package/dist/ui/__snapshots__/App.test.tsx.snap +124 -0
  113. package/dist/ui/colors.ts +56 -0
  114. package/dist/ui/commands/aboutCommand.test.ts +153 -0
  115. package/dist/ui/commands/aboutCommand.ts +49 -0
  116. package/dist/ui/commands/authCommand.test.ts +36 -0
  117. package/dist/ui/commands/authCommand.ts +17 -0
  118. package/dist/ui/commands/bugCommand.test.ts +114 -0
  119. package/dist/ui/commands/bugCommand.ts +92 -0
  120. package/dist/ui/commands/chatCommand.test.ts +414 -0
  121. package/dist/ui/commands/chatCommand.ts +280 -0
  122. package/dist/ui/commands/clearCommand.test.ts +100 -0
  123. package/dist/ui/commands/clearCommand.ts +29 -0
  124. package/dist/ui/commands/compressCommand.test.ts +129 -0
  125. package/dist/ui/commands/compressCommand.ts +78 -0
  126. package/dist/ui/commands/contextCommand.ts +132 -0
  127. package/dist/ui/commands/copyCommand.test.ts +296 -0
  128. package/dist/ui/commands/copyCommand.ts +67 -0
  129. package/dist/ui/commands/corgiCommand.test.ts +34 -0
  130. package/dist/ui/commands/corgiCommand.ts +16 -0
  131. package/dist/ui/commands/directoryCommand.test.tsx +185 -0
  132. package/dist/ui/commands/directoryCommand.tsx +179 -0
  133. package/dist/ui/commands/docsCommand.test.ts +99 -0
  134. package/dist/ui/commands/docsCommand.ts +42 -0
  135. package/dist/ui/commands/editorCommand.test.ts +30 -0
  136. package/dist/ui/commands/editorCommand.ts +21 -0
  137. package/dist/ui/commands/extensionsCommand.test.ts +67 -0
  138. package/dist/ui/commands/extensionsCommand.ts +46 -0
  139. package/dist/ui/commands/helpCommand.test.ts +52 -0
  140. package/dist/ui/commands/helpCommand.ts +23 -0
  141. package/dist/ui/commands/ideCommand.test.ts +255 -0
  142. package/dist/ui/commands/ideCommand.ts +283 -0
  143. package/dist/ui/commands/initCommand.test.ts +127 -0
  144. package/dist/ui/commands/initCommand.ts +117 -0
  145. package/dist/ui/commands/mcpCommand.test.ts +1057 -0
  146. package/dist/ui/commands/mcpCommand.ts +531 -0
  147. package/dist/ui/commands/memoryCommand.test.ts +344 -0
  148. package/dist/ui/commands/memoryCommand.ts +305 -0
  149. package/dist/ui/commands/privacyCommand.test.ts +38 -0
  150. package/dist/ui/commands/privacyCommand.ts +17 -0
  151. package/dist/ui/commands/quitCommand.test.ts +55 -0
  152. package/dist/ui/commands/quitCommand.ts +36 -0
  153. package/dist/ui/commands/restoreCommand.test.ts +250 -0
  154. package/dist/ui/commands/restoreCommand.ts +157 -0
  155. package/dist/ui/commands/searchEngineSetupCommand.ts +18 -0
  156. package/dist/ui/commands/settingsCommand.test.ts +36 -0
  157. package/dist/ui/commands/settingsCommand.ts +17 -0
  158. package/dist/ui/commands/setupGithubCommand.test.ts +238 -0
  159. package/dist/ui/commands/setupGithubCommand.ts +212 -0
  160. package/dist/ui/commands/speakCommand.ts +175 -0
  161. package/dist/ui/commands/statsCommand.test.ts +78 -0
  162. package/dist/ui/commands/statsCommand.ts +70 -0
  163. package/dist/ui/commands/terminalSetupCommand.test.ts +85 -0
  164. package/dist/ui/commands/terminalSetupCommand.ts +45 -0
  165. package/dist/ui/commands/themeCommand.test.ts +38 -0
  166. package/dist/ui/commands/themeCommand.ts +17 -0
  167. package/dist/ui/commands/toolsCommand.test.ts +105 -0
  168. package/dist/ui/commands/toolsCommand.ts +71 -0
  169. package/dist/ui/commands/ttsCommand.ts +143 -0
  170. package/dist/ui/commands/types.ts +204 -0
  171. package/dist/ui/commands/vimCommand.ts +25 -0
  172. package/dist/ui/commands/voiceCommand.ts +125 -0
  173. package/dist/ui/components/AboutBox.tsx +133 -0
  174. package/dist/ui/components/AsciiArt.ts +54 -0
  175. package/dist/ui/components/AuthDialog.test.tsx +334 -0
  176. package/dist/ui/components/AuthDialog.tsx +289 -0
  177. package/dist/ui/components/AuthInProgress.tsx +62 -0
  178. package/dist/ui/components/AutoAcceptIndicator.tsx +47 -0
  179. package/dist/ui/components/ConsoleSummaryDisplay.tsx +35 -0
  180. package/dist/ui/components/ContextSummaryDisplay.test.tsx +85 -0
  181. package/dist/ui/components/ContextSummaryDisplay.tsx +120 -0
  182. package/dist/ui/components/ContextUsageDisplay.tsx +77 -0
  183. package/dist/ui/components/DebugProfiler.tsx +36 -0
  184. package/dist/ui/components/DetailedMessagesDisplay.tsx +82 -0
  185. package/dist/ui/components/EditorSettingsDialog.tsx +172 -0
  186. package/dist/ui/components/FolderTrustDialog.test.tsx +36 -0
  187. package/dist/ui/components/FolderTrustDialog.tsx +74 -0
  188. package/dist/ui/components/Footer.test.tsx +159 -0
  189. package/dist/ui/components/Footer.tsx +158 -0
  190. package/dist/ui/components/GeminiKeyDialog.tsx +252 -0
  191. package/dist/ui/components/GeminiRespondingSpinner.tsx +34 -0
  192. package/dist/ui/components/Header.test.tsx +44 -0
  193. package/dist/ui/components/Header.tsx +70 -0
  194. package/dist/ui/components/Help.tsx +174 -0
  195. package/dist/ui/components/HistoryItemDisplay.test.tsx +125 -0
  196. package/dist/ui/components/HistoryItemDisplay.tsx +98 -0
  197. package/dist/ui/components/InputPrompt.test.tsx +1467 -0
  198. package/dist/ui/components/InputPrompt.tsx +641 -0
  199. package/dist/ui/components/LMStudioModelPrompt.tsx +215 -0
  200. package/dist/ui/components/LoadingIndicator.test.tsx +296 -0
  201. package/dist/ui/components/LoadingIndicator.tsx +82 -0
  202. package/dist/ui/components/MemoryUsageDisplay.tsx +36 -0
  203. package/dist/ui/components/ModelStatsDisplay.test.tsx +252 -0
  204. package/dist/ui/components/ModelStatsDisplay.tsx +197 -0
  205. package/dist/ui/components/OllamaModelPrompt.tsx +206 -0
  206. package/dist/ui/components/OpenAIEndpointDialog.tsx +261 -0
  207. package/dist/ui/components/OpenAIKeyPrompt.test.tsx +64 -0
  208. package/dist/ui/components/OpenAIKeyPrompt.tsx +197 -0
  209. package/dist/ui/components/PrepareLabel.tsx +48 -0
  210. package/dist/ui/components/SearchEngineConfigDialog.tsx +280 -0
  211. package/dist/ui/components/SessionSummaryDisplay.test.tsx +75 -0
  212. package/dist/ui/components/SessionSummaryDisplay.tsx +18 -0
  213. package/dist/ui/components/SettingsDialog.test.tsx +865 -0
  214. package/dist/ui/components/SettingsDialog.tsx +753 -0
  215. package/dist/ui/components/ShellConfirmationDialog.test.tsx +53 -0
  216. package/dist/ui/components/ShellConfirmationDialog.tsx +103 -0
  217. package/dist/ui/components/ShellModeIndicator.tsx +18 -0
  218. package/dist/ui/components/ShowMoreLines.tsx +40 -0
  219. package/dist/ui/components/StatsDisplay.test.tsx +401 -0
  220. package/dist/ui/components/StatsDisplay.tsx +273 -0
  221. package/dist/ui/components/SuggestionsDisplay.tsx +102 -0
  222. package/dist/ui/components/ThemeDialog.tsx +310 -0
  223. package/dist/ui/components/Tips.tsx +45 -0
  224. package/dist/ui/components/TodoDisplay.test.tsx +97 -0
  225. package/dist/ui/components/TodoDisplay.tsx +72 -0
  226. package/dist/ui/components/ToolStatsDisplay.test.tsx +180 -0
  227. package/dist/ui/components/ToolStatsDisplay.tsx +208 -0
  228. package/dist/ui/components/UpdateNotification.tsx +23 -0
  229. package/dist/ui/components/WelcomeBackDialog.tsx +290 -0
  230. package/dist/ui/components/__snapshots__/IDEContextDetailDisplay.test.tsx.snap +24 -0
  231. package/dist/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap +121 -0
  232. package/dist/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap +30 -0
  233. package/dist/ui/components/__snapshots__/ShellConfirmationDialog.test.tsx.snap +21 -0
  234. package/dist/ui/components/__snapshots__/StatsDisplay.test.tsx.snap +264 -0
  235. package/dist/ui/components/__snapshots__/ToolStatsDisplay.test.tsx.snap +91 -0
  236. package/dist/ui/components/messages/CompressionMessage.tsx +49 -0
  237. package/dist/ui/components/messages/DiffRenderer.test.tsx +365 -0
  238. package/dist/ui/components/messages/DiffRenderer.tsx +358 -0
  239. package/dist/ui/components/messages/ErrorMessage.tsx +31 -0
  240. package/dist/ui/components/messages/GeminiMessage.tsx +43 -0
  241. package/dist/ui/components/messages/GeminiMessageContent.tsx +43 -0
  242. package/dist/ui/components/messages/InfoMessage.tsx +32 -0
  243. package/dist/ui/components/messages/ToolConfirmationMessage.test.tsx +58 -0
  244. package/dist/ui/components/messages/ToolConfirmationMessage.tsx +297 -0
  245. package/dist/ui/components/messages/ToolGroupMessage.tsx +126 -0
  246. package/dist/ui/components/messages/ToolMessage.test.tsx +183 -0
  247. package/dist/ui/components/messages/ToolMessage.tsx +296 -0
  248. package/dist/ui/components/messages/UserMessage.tsx +43 -0
  249. package/dist/ui/components/messages/UserShellMessage.tsx +25 -0
  250. package/dist/ui/components/shared/MaxSizedBox.test.tsx +425 -0
  251. package/dist/ui/components/shared/MaxSizedBox.tsx +624 -0
  252. package/dist/ui/components/shared/RadioButtonSelect.test.tsx +181 -0
  253. package/dist/ui/components/shared/RadioButtonSelect.tsx +234 -0
  254. package/dist/ui/components/shared/__snapshots__/RadioButtonSelect.test.tsx.snap +47 -0
  255. package/dist/ui/components/shared/text-buffer.test.ts +1728 -0
  256. package/dist/ui/components/shared/text-buffer.ts +2227 -0
  257. package/dist/ui/components/shared/vim-buffer-actions.test.ts +1119 -0
  258. package/dist/ui/components/shared/vim-buffer-actions.ts +814 -0
  259. package/dist/ui/constants.ts +17 -0
  260. package/dist/ui/contexts/KeypressContext.test.tsx +391 -0
  261. package/dist/ui/contexts/KeypressContext.tsx +440 -0
  262. package/dist/ui/contexts/OverflowContext.tsx +87 -0
  263. package/dist/ui/contexts/SessionContext.test.tsx +132 -0
  264. package/dist/ui/contexts/SessionContext.tsx +143 -0
  265. package/dist/ui/contexts/SettingsContext.tsx +20 -0
  266. package/dist/ui/contexts/StreamingContext.tsx +22 -0
  267. package/dist/ui/contexts/VimModeContext.tsx +79 -0
  268. package/dist/ui/editors/editorSettingsManager.ts +66 -0
  269. package/dist/ui/hooks/atCommandProcessor.test.ts +1102 -0
  270. package/dist/ui/hooks/atCommandProcessor.ts +485 -0
  271. package/dist/ui/hooks/shellCommandProcessor.test.ts +481 -0
  272. package/dist/ui/hooks/shellCommandProcessor.ts +314 -0
  273. package/dist/ui/hooks/slashCommandProcessor.test.ts +1044 -0
  274. package/dist/ui/hooks/slashCommandProcessor.ts +595 -0
  275. package/dist/ui/hooks/useAtCompletion.test.ts +497 -0
  276. package/dist/ui/hooks/useAtCompletion.ts +244 -0
  277. package/dist/ui/hooks/useAuthCommand.ts +129 -0
  278. package/dist/ui/hooks/useAutoAcceptIndicator.test.ts +300 -0
  279. package/dist/ui/hooks/useAutoAcceptIndicator.ts +52 -0
  280. package/dist/ui/hooks/useBracketedPaste.ts +37 -0
  281. package/dist/ui/hooks/useCommandCompletion.test.ts +518 -0
  282. package/dist/ui/hooks/useCommandCompletion.tsx +238 -0
  283. package/dist/ui/hooks/useCompletion.ts +128 -0
  284. package/dist/ui/hooks/useConsoleMessages.test.ts +147 -0
  285. package/dist/ui/hooks/useConsoleMessages.ts +110 -0
  286. package/dist/ui/hooks/useEditorSettings.test.ts +283 -0
  287. package/dist/ui/hooks/useEditorSettings.ts +75 -0
  288. package/dist/ui/hooks/useFocus.test.ts +119 -0
  289. package/dist/ui/hooks/useFocus.ts +48 -0
  290. package/dist/ui/hooks/useFolderTrust.test.ts +159 -0
  291. package/dist/ui/hooks/useFolderTrust.ts +72 -0
  292. package/dist/ui/hooks/useGeminiStream.test.tsx +1998 -0
  293. package/dist/ui/hooks/useGeminiStream.ts +1017 -0
  294. package/dist/ui/hooks/useGitBranchName.test.ts +280 -0
  295. package/dist/ui/hooks/useGitBranchName.ts +79 -0
  296. package/dist/ui/hooks/useHistoryManager.test.ts +202 -0
  297. package/dist/ui/hooks/useHistoryManager.ts +111 -0
  298. package/dist/ui/hooks/useInputHistory.test.ts +261 -0
  299. package/dist/ui/hooks/useInputHistory.ts +111 -0
  300. package/dist/ui/hooks/useKeypress.test.ts +280 -0
  301. package/dist/ui/hooks/useKeypress.ts +39 -0
  302. package/dist/ui/hooks/useKittyKeyboardProtocol.ts +31 -0
  303. package/dist/ui/hooks/useLoadingIndicator.test.ts +139 -0
  304. package/dist/ui/hooks/useLoadingIndicator.ts +57 -0
  305. package/dist/ui/hooks/useLogger.ts +32 -0
  306. package/dist/ui/hooks/useMessageQueue.test.ts +226 -0
  307. package/dist/ui/hooks/useMessageQueue.ts +69 -0
  308. package/dist/ui/hooks/usePhraseCycler.test.ts +145 -0
  309. package/dist/ui/hooks/usePhraseCycler.ts +198 -0
  310. package/dist/ui/hooks/usePrivacySettings.test.ts +242 -0
  311. package/dist/ui/hooks/usePrivacySettings.ts +150 -0
  312. package/dist/ui/hooks/useReactToolScheduler.ts +309 -0
  313. package/dist/ui/hooks/useRefreshMemoryCommand.ts +7 -0
  314. package/dist/ui/hooks/useReverseSearchCompletion.test.tsx +260 -0
  315. package/dist/ui/hooks/useReverseSearchCompletion.tsx +95 -0
  316. package/dist/ui/hooks/useSettingsCommand.ts +25 -0
  317. package/dist/ui/hooks/useShellHistory.test.ts +219 -0
  318. package/dist/ui/hooks/useShellHistory.ts +133 -0
  319. package/dist/ui/hooks/useShowMemoryCommand.ts +75 -0
  320. package/dist/ui/hooks/useSlashCompletion.test.ts +434 -0
  321. package/dist/ui/hooks/useSlashCompletion.ts +187 -0
  322. package/dist/ui/hooks/useStateAndRef.ts +36 -0
  323. package/dist/ui/hooks/useTerminalSize.ts +32 -0
  324. package/dist/ui/hooks/useThemeCommand.ts +110 -0
  325. package/dist/ui/hooks/useTimer.test.ts +120 -0
  326. package/dist/ui/hooks/useTimer.ts +65 -0
  327. package/dist/ui/hooks/useToolScheduler.test.ts +1123 -0
  328. package/dist/ui/hooks/useWelcomeBack.ts +253 -0
  329. package/dist/ui/hooks/vim.test.ts +1691 -0
  330. package/dist/ui/hooks/vim.ts +784 -0
  331. package/dist/ui/keyMatchers.test.ts +337 -0
  332. package/dist/ui/keyMatchers.ts +105 -0
  333. package/dist/ui/privacy/CloudFreePrivacyNotice.tsx +117 -0
  334. package/dist/ui/privacy/CloudPaidPrivacyNotice.tsx +59 -0
  335. package/dist/ui/privacy/GeminiPrivacyNotice.tsx +62 -0
  336. package/dist/ui/privacy/PrivacyNotice.tsx +42 -0
  337. package/dist/ui/semantic-colors.ts +26 -0
  338. package/dist/ui/themes/ansi-light.ts +150 -0
  339. package/dist/ui/themes/ansi.ts +159 -0
  340. package/dist/ui/themes/atom-one-dark.ts +147 -0
  341. package/dist/ui/themes/ayu-light.ts +139 -0
  342. package/dist/ui/themes/ayu.ts +113 -0
  343. package/dist/ui/themes/color-utils.test.ts +221 -0
  344. package/dist/ui/themes/color-utils.ts +231 -0
  345. package/dist/ui/themes/default-light.ts +108 -0
  346. package/dist/ui/themes/default.ts +151 -0
  347. package/dist/ui/themes/dracula.ts +124 -0
  348. package/dist/ui/themes/fss-code-dark.ts +156 -0
  349. package/dist/ui/themes/fss-dark.ts +113 -0
  350. package/dist/ui/themes/fss-light.ts +139 -0
  351. package/dist/ui/themes/github-dark.ts +147 -0
  352. package/dist/ui/themes/github-light.ts +149 -0
  353. package/dist/ui/themes/googlecode.ts +146 -0
  354. package/dist/ui/themes/no-color.ts +125 -0
  355. package/dist/ui/themes/qwen-dark.ts +118 -0
  356. package/dist/ui/themes/qwen-light.ts +144 -0
  357. package/dist/ui/themes/semantic-tokens.ts +127 -0
  358. package/dist/ui/themes/shades-of-purple.ts +352 -0
  359. package/dist/ui/themes/theme-manager.test.ts +99 -0
  360. package/dist/ui/themes/theme-manager.ts +257 -0
  361. package/dist/ui/themes/theme.test.ts +97 -0
  362. package/dist/ui/themes/theme.ts +451 -0
  363. package/dist/ui/themes/xcode.ts +154 -0
  364. package/dist/ui/types.ts +255 -0
  365. package/dist/ui/utils/CodeColorizer.tsx +217 -0
  366. package/dist/ui/utils/ConsolePatcher.ts +71 -0
  367. package/dist/ui/utils/InlineMarkdownRenderer.tsx +173 -0
  368. package/dist/ui/utils/MarkdownDisplay.test.tsx +244 -0
  369. package/dist/ui/utils/MarkdownDisplay.tsx +415 -0
  370. package/dist/ui/utils/TableRenderer.tsx +159 -0
  371. package/dist/ui/utils/__snapshots__/MarkdownDisplay.test.tsx.snap +93 -0
  372. package/dist/ui/utils/clipboardUtils.test.ts +76 -0
  373. package/dist/ui/utils/clipboardUtils.ts +149 -0
  374. package/dist/ui/utils/commandUtils.test.ts +384 -0
  375. package/dist/ui/utils/commandUtils.ts +106 -0
  376. package/dist/ui/utils/computeStats.test.ts +292 -0
  377. package/dist/ui/utils/computeStats.ts +86 -0
  378. package/dist/ui/utils/displayUtils.test.ts +58 -0
  379. package/dist/ui/utils/displayUtils.ts +32 -0
  380. package/dist/ui/utils/formatters.test.ts +72 -0
  381. package/dist/ui/utils/formatters.ts +63 -0
  382. package/dist/ui/utils/isNarrowWidth.ts +9 -0
  383. package/dist/ui/utils/kittyProtocolDetector.ts +105 -0
  384. package/dist/ui/utils/markdownUtilities.test.ts +50 -0
  385. package/dist/ui/utils/markdownUtilities.ts +125 -0
  386. package/dist/ui/utils/platformConstants.ts +52 -0
  387. package/dist/ui/utils/terminalSetup.ts +342 -0
  388. package/dist/ui/utils/textUtils.ts +40 -0
  389. package/dist/ui/utils/updateCheck.test.ts +163 -0
  390. package/dist/ui/utils/updateCheck.ts +100 -0
  391. package/dist/utils/checks.ts +28 -0
  392. package/dist/utils/cleanup.test.ts +68 -0
  393. package/dist/utils/cleanup.ts +36 -0
  394. package/dist/utils/dialogScopeUtils.ts +64 -0
  395. package/dist/utils/events.ts +14 -0
  396. package/dist/utils/gitUtils.test.ts +149 -0
  397. package/dist/utils/gitUtils.ts +116 -0
  398. package/dist/utils/handleAutoUpdate.test.ts +272 -0
  399. package/dist/utils/handleAutoUpdate.ts +145 -0
  400. package/dist/utils/installationInfo.test.ts +315 -0
  401. package/dist/utils/installationInfo.ts +176 -0
  402. package/dist/utils/package.ts +38 -0
  403. package/dist/utils/readStdin.ts +51 -0
  404. package/dist/utils/resolvePath.ts +21 -0
  405. package/dist/utils/sandbox-macos-permissive-closed.sb +32 -0
  406. package/dist/utils/sandbox-macos-permissive-open.sb +25 -0
  407. package/dist/utils/sandbox-macos-permissive-proxied.sb +37 -0
  408. package/dist/utils/sandbox-macos-restrictive-closed.sb +93 -0
  409. package/dist/utils/sandbox-macos-restrictive-open.sb +96 -0
  410. package/dist/utils/sandbox-macos-restrictive-proxied.sb +98 -0
  411. package/dist/utils/sandbox.ts +962 -0
  412. package/dist/utils/settingsUtils.test.ts +797 -0
  413. package/dist/utils/settingsUtils.ts +489 -0
  414. package/dist/utils/spawnWrapper.ts +9 -0
  415. package/dist/utils/startupWarnings.test.ts +83 -0
  416. package/dist/utils/startupWarnings.ts +40 -0
  417. package/dist/utils/updateEventEmitter.ts +13 -0
  418. package/dist/utils/userStartupWarnings.test.ts +87 -0
  419. package/dist/utils/userStartupWarnings.ts +69 -0
  420. package/dist/utils/version.ts +12 -0
  421. package/dist/validateNonInterActiveAuth.test.ts +260 -0
  422. package/dist/validateNonInterActiveAuth.ts +51 -0
  423. package/dist/vitest.config.ts +37 -0
  424. package/dist/zed-integration/acp.ts +366 -0
  425. package/dist/zed-integration/fileSystemService.ts +47 -0
  426. package/dist/zed-integration/schema.ts +466 -0
  427. package/dist/zed-integration/zedIntegration.ts +944 -0
  428. package/package.json +2 -2
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
8
+ import { directoryCommand, expandHomeDir } from './directoryCommand.js';
9
+ import { Config, WorkspaceContext } from 'fss-link-core';
10
+ import { CommandContext } from './types.js';
11
+ import { MessageType } from '../types.js';
12
+ import * as os from 'os';
13
+ import * as path from 'path';
14
+
15
+ describe('directoryCommand', () => {
16
+ let mockContext: CommandContext;
17
+ let mockConfig: Config;
18
+ let mockWorkspaceContext: WorkspaceContext;
19
+ const addCommand = directoryCommand.subCommands?.find(
20
+ (c) => c.name === 'add',
21
+ );
22
+ const showCommand = directoryCommand.subCommands?.find(
23
+ (c) => c.name === 'show',
24
+ );
25
+
26
+ beforeEach(() => {
27
+ mockWorkspaceContext = {
28
+ addDirectory: vi.fn(),
29
+ getDirectories: vi
30
+ .fn()
31
+ .mockReturnValue([
32
+ path.normalize('/home/user/project1'),
33
+ path.normalize('/home/user/project2'),
34
+ ]),
35
+ } as unknown as WorkspaceContext;
36
+
37
+ mockConfig = {
38
+ getWorkspaceContext: () => mockWorkspaceContext,
39
+ isRestrictiveSandbox: vi.fn().mockReturnValue(false),
40
+ getGeminiClient: vi.fn().mockReturnValue({
41
+ addDirectoryContext: vi.fn(),
42
+ }),
43
+ getWorkingDir: () => '/test/dir',
44
+ shouldLoadMemoryFromIncludeDirectories: () => false,
45
+ getDebugMode: () => false,
46
+ getFileService: () => ({}),
47
+ getExtensionContextFilePaths: () => [],
48
+ getFileFilteringOptions: () => ({ ignore: [], include: [] }),
49
+ setUserMemory: vi.fn(),
50
+ setGeminiMdFileCount: vi.fn(),
51
+ } as unknown as Config;
52
+
53
+ mockContext = {
54
+ services: {
55
+ config: mockConfig,
56
+ settings: {
57
+ merged: {
58
+ memoryDiscoveryMaxDirs: 1000,
59
+ },
60
+ },
61
+ },
62
+ ui: {
63
+ addItem: vi.fn(),
64
+ },
65
+ } as unknown as CommandContext;
66
+ });
67
+
68
+ describe('show', () => {
69
+ it('should display the list of directories', () => {
70
+ if (!showCommand?.action) throw new Error('No action');
71
+ showCommand.action(mockContext, '');
72
+ expect(mockWorkspaceContext.getDirectories).toHaveBeenCalled();
73
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
74
+ expect.objectContaining({
75
+ type: MessageType.INFO,
76
+ text: `Current workspace directories:\n- ${path.normalize(
77
+ '/home/user/project1',
78
+ )}\n- ${path.normalize('/home/user/project2')}`,
79
+ }),
80
+ expect.any(Number),
81
+ );
82
+ });
83
+ });
84
+
85
+ describe('add', () => {
86
+ it('should show an error if no path is provided', () => {
87
+ if (!addCommand?.action) throw new Error('No action');
88
+ addCommand.action(mockContext, '');
89
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
90
+ expect.objectContaining({
91
+ type: MessageType.ERROR,
92
+ text: 'Please provide at least one path to add.',
93
+ }),
94
+ expect.any(Number),
95
+ );
96
+ });
97
+
98
+ it('should call addDirectory and show a success message for a single path', async () => {
99
+ const newPath = path.normalize('/home/user/new-project');
100
+ if (!addCommand?.action) throw new Error('No action');
101
+ await addCommand.action(mockContext, newPath);
102
+ expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith(newPath);
103
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
104
+ expect.objectContaining({
105
+ type: MessageType.INFO,
106
+ text: `Successfully added directories:\n- ${newPath}`,
107
+ }),
108
+ expect.any(Number),
109
+ );
110
+ });
111
+
112
+ it('should call addDirectory for each path and show a success message for multiple paths', async () => {
113
+ const newPath1 = path.normalize('/home/user/new-project1');
114
+ const newPath2 = path.normalize('/home/user/new-project2');
115
+ if (!addCommand?.action) throw new Error('No action');
116
+ await addCommand.action(mockContext, `${newPath1},${newPath2}`);
117
+ expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith(newPath1);
118
+ expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith(newPath2);
119
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
120
+ expect.objectContaining({
121
+ type: MessageType.INFO,
122
+ text: `Successfully added directories:\n- ${newPath1}\n- ${newPath2}`,
123
+ }),
124
+ expect.any(Number),
125
+ );
126
+ });
127
+
128
+ it('should show an error if addDirectory throws an exception', async () => {
129
+ const error = new Error('Directory does not exist');
130
+ vi.mocked(mockWorkspaceContext.addDirectory).mockImplementation(() => {
131
+ throw error;
132
+ });
133
+ const newPath = path.normalize('/home/user/invalid-project');
134
+ if (!addCommand?.action) throw new Error('No action');
135
+ await addCommand.action(mockContext, newPath);
136
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
137
+ expect.objectContaining({
138
+ type: MessageType.ERROR,
139
+ text: `Error adding '${newPath}': ${error.message}`,
140
+ }),
141
+ expect.any(Number),
142
+ );
143
+ });
144
+
145
+ it('should handle a mix of successful and failed additions', async () => {
146
+ const validPath = path.normalize('/home/user/valid-project');
147
+ const invalidPath = path.normalize('/home/user/invalid-project');
148
+ const error = new Error('Directory does not exist');
149
+ vi.mocked(mockWorkspaceContext.addDirectory).mockImplementation(
150
+ (p: string) => {
151
+ if (p === invalidPath) {
152
+ throw error;
153
+ }
154
+ },
155
+ );
156
+
157
+ if (!addCommand?.action) throw new Error('No action');
158
+ await addCommand.action(mockContext, `${validPath},${invalidPath}`);
159
+
160
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
161
+ expect.objectContaining({
162
+ type: MessageType.INFO,
163
+ text: `Successfully added directories:\n- ${validPath}`,
164
+ }),
165
+ expect.any(Number),
166
+ );
167
+
168
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
169
+ expect.objectContaining({
170
+ type: MessageType.ERROR,
171
+ text: `Error adding '${invalidPath}': ${error.message}`,
172
+ }),
173
+ expect.any(Number),
174
+ );
175
+ });
176
+ });
177
+ it('should correctly expand a Windows-style home directory path', () => {
178
+ const windowsPath = '%userprofile%\\Documents';
179
+ const expectedPath = path.win32.join(os.homedir(), 'Documents');
180
+ const result = expandHomeDir(windowsPath);
181
+ expect(path.win32.normalize(result)).toBe(
182
+ path.win32.normalize(expectedPath),
183
+ );
184
+ });
185
+ });
@@ -0,0 +1,179 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { SlashCommand, CommandContext, CommandKind } from './types.js';
8
+ import { MessageType } from '../types.js';
9
+ import * as os from 'os';
10
+ import * as path from 'path';
11
+ import { loadServerHierarchicalMemory } from 'fss-link-core';
12
+
13
+ export function expandHomeDir(p: string): string {
14
+ if (!p) {
15
+ return '';
16
+ }
17
+ let expandedPath = p;
18
+ if (p.toLowerCase().startsWith('%userprofile%')) {
19
+ expandedPath = os.homedir() + p.substring('%userprofile%'.length);
20
+ } else if (p === '~' || p.startsWith('~/')) {
21
+ expandedPath = os.homedir() + p.substring(1);
22
+ }
23
+ return path.normalize(expandedPath);
24
+ }
25
+
26
+ export const directoryCommand: SlashCommand = {
27
+ name: 'directory',
28
+ altNames: ['dir'],
29
+ description: 'Manage workspace directories',
30
+ kind: CommandKind.BUILT_IN,
31
+ subCommands: [
32
+ {
33
+ name: 'add',
34
+ description:
35
+ 'Add directories to the workspace. Use comma to separate multiple paths',
36
+ kind: CommandKind.BUILT_IN,
37
+ action: async (context: CommandContext, args: string) => {
38
+ const {
39
+ ui: { addItem },
40
+ services: { config },
41
+ } = context;
42
+ const [...rest] = args.split(' ');
43
+
44
+ if (!config) {
45
+ addItem(
46
+ {
47
+ type: MessageType.ERROR,
48
+ text: 'Configuration is not available.',
49
+ },
50
+ Date.now(),
51
+ );
52
+ return;
53
+ }
54
+
55
+ const workspaceContext = config.getWorkspaceContext();
56
+
57
+ const pathsToAdd = rest
58
+ .join(' ')
59
+ .split(',')
60
+ .filter((p) => p);
61
+ if (pathsToAdd.length === 0) {
62
+ addItem(
63
+ {
64
+ type: MessageType.ERROR,
65
+ text: 'Please provide at least one path to add.',
66
+ },
67
+ Date.now(),
68
+ );
69
+ return;
70
+ }
71
+
72
+ if (config.isRestrictiveSandbox()) {
73
+ return {
74
+ type: 'message' as const,
75
+ messageType: 'error' as const,
76
+ content:
77
+ 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
78
+ };
79
+ }
80
+
81
+ const added: string[] = [];
82
+ const errors: string[] = [];
83
+
84
+ for (const pathToAdd of pathsToAdd) {
85
+ try {
86
+ workspaceContext.addDirectory(expandHomeDir(pathToAdd.trim()));
87
+ added.push(pathToAdd.trim());
88
+ } catch (e) {
89
+ const error = e as Error;
90
+ errors.push(`Error adding '${pathToAdd.trim()}': ${error.message}`);
91
+ }
92
+ }
93
+
94
+ if (added.length > 0) {
95
+ try {
96
+ if (config.shouldLoadMemoryFromIncludeDirectories()) {
97
+ const { memoryContent, fileCount } =
98
+ await loadServerHierarchicalMemory(
99
+ config.getWorkingDir(),
100
+ [...config.getWorkspaceContext().getDirectories()],
101
+ config.getDebugMode(),
102
+ config.getFileService(),
103
+ config.getExtensionContextFilePaths(),
104
+ context.services.settings.merged.memoryImportFormat || 'tree', // Use setting or default to 'tree'
105
+ config.getFileFilteringOptions(),
106
+ context.services.settings.merged.memoryDiscoveryMaxDirs,
107
+ );
108
+ config.setUserMemory(memoryContent);
109
+ config.setGeminiMdFileCount(fileCount);
110
+ context.ui.setGeminiMdFileCount(fileCount);
111
+ }
112
+ addItem(
113
+ {
114
+ type: MessageType.INFO,
115
+ text: `Successfully added memory files from the following directories if there are:\n- ${added.join('\n- ')}`,
116
+ },
117
+ Date.now(),
118
+ );
119
+ } catch (error) {
120
+ errors.push(`Error refreshing memory: ${(error as Error).message}`);
121
+ }
122
+ }
123
+
124
+ if (added.length > 0) {
125
+ const gemini = config.getGeminiClient();
126
+ if (gemini) {
127
+ await gemini.addDirectoryContext();
128
+ }
129
+ addItem(
130
+ {
131
+ type: MessageType.INFO,
132
+ text: `Successfully added directories:\n- ${added.join('\n- ')}`,
133
+ },
134
+ Date.now(),
135
+ );
136
+ }
137
+
138
+ if (errors.length > 0) {
139
+ addItem(
140
+ { type: MessageType.ERROR, text: errors.join('\n') },
141
+ Date.now(),
142
+ );
143
+ }
144
+ return;
145
+ },
146
+ },
147
+ {
148
+ name: 'show',
149
+ description: 'Show all directories in the workspace',
150
+ kind: CommandKind.BUILT_IN,
151
+ action: async (context: CommandContext) => {
152
+ const {
153
+ ui: { addItem },
154
+ services: { config },
155
+ } = context;
156
+ if (!config) {
157
+ addItem(
158
+ {
159
+ type: MessageType.ERROR,
160
+ text: 'Configuration is not available.',
161
+ },
162
+ Date.now(),
163
+ );
164
+ return;
165
+ }
166
+ const workspaceContext = config.getWorkspaceContext();
167
+ const directories = workspaceContext.getDirectories();
168
+ const directoryList = directories.map((dir) => `- ${dir}`).join('\n');
169
+ addItem(
170
+ {
171
+ type: MessageType.INFO,
172
+ text: `Current workspace directories:\n${directoryList}`,
173
+ },
174
+ Date.now(),
175
+ );
176
+ },
177
+ },
178
+ ],
179
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
8
+ import open from 'open';
9
+ import { docsCommand } from './docsCommand.js';
10
+ import { type CommandContext } from './types.js';
11
+ import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
12
+ import { MessageType } from '../types.js';
13
+
14
+ // Mock the 'open' library
15
+ vi.mock('open', () => ({
16
+ default: vi.fn(),
17
+ }));
18
+
19
+ describe('docsCommand', () => {
20
+ let mockContext: CommandContext;
21
+ beforeEach(() => {
22
+ // Create a fresh mock context before each test
23
+ mockContext = createMockCommandContext();
24
+ // Reset the `open` mock
25
+ vi.mocked(open).mockClear();
26
+ });
27
+
28
+ afterEach(() => {
29
+ // Restore any stubbed environment variables
30
+ vi.unstubAllEnvs();
31
+ });
32
+
33
+ it("should add an info message and call 'open' in a non-sandbox environment", async () => {
34
+ if (!docsCommand.action) {
35
+ throw new Error('docsCommand must have an action.');
36
+ }
37
+
38
+ const docsUrl = 'https://docs.anthropic.com/en/docs/claude-code';
39
+
40
+ await docsCommand.action(mockContext, '');
41
+
42
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
43
+ {
44
+ type: MessageType.INFO,
45
+ text: `Opening documentation in your browser: ${docsUrl}`,
46
+ },
47
+ expect.any(Number),
48
+ );
49
+
50
+ expect(open).toHaveBeenCalledWith(docsUrl);
51
+ });
52
+
53
+ it('should only add an info message in a sandbox environment', async () => {
54
+ if (!docsCommand.action) {
55
+ throw new Error('docsCommand must have an action.');
56
+ }
57
+
58
+ // Simulate a sandbox environment
59
+ vi.stubEnv('SANDBOX', 'gemini-sandbox');
60
+ const docsUrl = 'https://docs.anthropic.com/en/docs/claude-code';
61
+
62
+ await docsCommand.action(mockContext, '');
63
+
64
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
65
+ {
66
+ type: MessageType.INFO,
67
+ text: `Please open the following URL in your browser to view the documentation:\n${docsUrl}`,
68
+ },
69
+ expect.any(Number),
70
+ );
71
+
72
+ // Ensure 'open' was not called in the sandbox
73
+ expect(open).not.toHaveBeenCalled();
74
+ });
75
+
76
+ it("should not open browser for 'sandbox-exec'", async () => {
77
+ if (!docsCommand.action) {
78
+ throw new Error('docsCommand must have an action.');
79
+ }
80
+
81
+ // Simulate the specific 'sandbox-exec' environment
82
+ vi.stubEnv('SANDBOX', 'sandbox-exec');
83
+ const docsUrl = 'https://docs.anthropic.com/en/docs/claude-code';
84
+
85
+ await docsCommand.action(mockContext, '');
86
+
87
+ // The logic should fall through to the 'else' block
88
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
89
+ {
90
+ type: MessageType.INFO,
91
+ text: `Opening documentation in your browser: ${docsUrl}`,
92
+ },
93
+ expect.any(Number),
94
+ );
95
+
96
+ // 'open' should be called in this specific sandbox case
97
+ expect(open).toHaveBeenCalledWith(docsUrl);
98
+ });
99
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import open from 'open';
8
+ import process from 'node:process';
9
+ import {
10
+ type CommandContext,
11
+ type SlashCommand,
12
+ CommandKind,
13
+ } from './types.js';
14
+ import { MessageType } from '../types.js';
15
+
16
+ export const docsCommand: SlashCommand = {
17
+ name: 'docs',
18
+ description: 'open full FSS Link documentation in your browser',
19
+ kind: CommandKind.BUILT_IN,
20
+ action: async (context: CommandContext): Promise<void> => {
21
+ const docsUrl = 'https://docs.anthropic.com/en/docs/claude-code';
22
+
23
+ if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') {
24
+ context.ui.addItem(
25
+ {
26
+ type: MessageType.INFO,
27
+ text: `Please open the following URL in your browser to view the documentation:\n${docsUrl}`,
28
+ },
29
+ Date.now(),
30
+ );
31
+ } else {
32
+ context.ui.addItem(
33
+ {
34
+ type: MessageType.INFO,
35
+ text: `Opening documentation in your browser: ${docsUrl}`,
36
+ },
37
+ Date.now(),
38
+ );
39
+ await open(docsUrl);
40
+ }
41
+ },
42
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { describe, it, expect } from 'vitest';
8
+ import { editorCommand } from './editorCommand.js';
9
+ // 1. Import the mock context utility
10
+ import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
11
+
12
+ describe('editorCommand', () => {
13
+ it('should return a dialog action to open the editor dialog', () => {
14
+ if (!editorCommand.action) {
15
+ throw new Error('The editor command must have an action.');
16
+ }
17
+ const mockContext = createMockCommandContext();
18
+ const result = editorCommand.action(mockContext, '');
19
+
20
+ expect(result).toEqual({
21
+ type: 'dialog',
22
+ dialog: 'editor',
23
+ });
24
+ });
25
+
26
+ it('should have the correct name and description', () => {
27
+ expect(editorCommand.name).toBe('editor');
28
+ expect(editorCommand.description).toBe('set external editor preference');
29
+ });
30
+ });
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import {
8
+ CommandKind,
9
+ type OpenDialogActionReturn,
10
+ type SlashCommand,
11
+ } from './types.js';
12
+
13
+ export const editorCommand: SlashCommand = {
14
+ name: 'editor',
15
+ description: 'set external editor preference',
16
+ kind: CommandKind.BUILT_IN,
17
+ action: (): OpenDialogActionReturn => ({
18
+ type: 'dialog',
19
+ dialog: 'editor',
20
+ }),
21
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { describe, it, expect } from 'vitest';
8
+ import { extensionsCommand } from './extensionsCommand.js';
9
+ import { type CommandContext } from './types.js';
10
+ import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
11
+ import { MessageType } from '../types.js';
12
+
13
+ describe('extensionsCommand', () => {
14
+ let mockContext: CommandContext;
15
+
16
+ it('should display "No active extensions." when none are found', async () => {
17
+ mockContext = createMockCommandContext({
18
+ services: {
19
+ config: {
20
+ getExtensions: () => [],
21
+ },
22
+ },
23
+ });
24
+
25
+ if (!extensionsCommand.action) throw new Error('Action not defined');
26
+ await extensionsCommand.action(mockContext, '');
27
+
28
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
29
+ {
30
+ type: MessageType.INFO,
31
+ text: 'No active extensions.',
32
+ },
33
+ expect.any(Number),
34
+ );
35
+ });
36
+
37
+ it('should list active extensions when they are found', async () => {
38
+ const mockExtensions = [
39
+ { name: 'ext-one', version: '1.0.0', isActive: true },
40
+ { name: 'ext-two', version: '2.1.0', isActive: true },
41
+ { name: 'ext-three', version: '3.0.0', isActive: false },
42
+ ];
43
+ mockContext = createMockCommandContext({
44
+ services: {
45
+ config: {
46
+ getExtensions: () => mockExtensions,
47
+ },
48
+ },
49
+ });
50
+
51
+ if (!extensionsCommand.action) throw new Error('Action not defined');
52
+ await extensionsCommand.action(mockContext, '');
53
+
54
+ const expectedMessage =
55
+ 'Active extensions:\n\n' +
56
+ ` - \u001b[36mext-one (v1.0.0)\u001b[0m\n` +
57
+ ` - \u001b[36mext-two (v2.1.0)\u001b[0m\n`;
58
+
59
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
60
+ {
61
+ type: MessageType.INFO,
62
+ text: expectedMessage,
63
+ },
64
+ expect.any(Number),
65
+ );
66
+ });
67
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import {
8
+ type CommandContext,
9
+ type SlashCommand,
10
+ CommandKind,
11
+ } from './types.js';
12
+ import { MessageType } from '../types.js';
13
+
14
+ export const extensionsCommand: SlashCommand = {
15
+ name: 'extensions',
16
+ description: 'list active extensions',
17
+ kind: CommandKind.BUILT_IN,
18
+ action: async (context: CommandContext): Promise<void> => {
19
+ const activeExtensions = context.services.config
20
+ ?.getExtensions()
21
+ .filter((ext) => ext.isActive);
22
+ if (!activeExtensions || activeExtensions.length === 0) {
23
+ context.ui.addItem(
24
+ {
25
+ type: MessageType.INFO,
26
+ text: 'No active extensions.',
27
+ },
28
+ Date.now(),
29
+ );
30
+ return;
31
+ }
32
+
33
+ const extensionLines = activeExtensions.map(
34
+ (ext) => ` - \u001b[36m${ext.name} (v${ext.version})\u001b[0m`,
35
+ );
36
+ const message = `Active extensions:\n\n${extensionLines.join('\n')}\n`;
37
+
38
+ context.ui.addItem(
39
+ {
40
+ type: MessageType.INFO,
41
+ text: message,
42
+ },
43
+ Date.now(),
44
+ );
45
+ },
46
+ };