@google/gemini-cli 0.12.0-preview.4 → 0.13.0-nightly.20251031.c89bc30d

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 (254) hide show
  1. package/dist/google-gemini-cli-0.13.0-nightly.20251029.cca41edc.tgz +0 -0
  2. package/dist/package.json +2 -2
  3. package/dist/src/commands/extensions/install.js +2 -2
  4. package/dist/src/commands/extensions/install.js.map +1 -1
  5. package/dist/src/commands/extensions/install.test.js +27 -16
  6. package/dist/src/commands/extensions/install.test.js.map +1 -1
  7. package/dist/src/commands/extensions/link.js +2 -2
  8. package/dist/src/commands/extensions/link.js.map +1 -1
  9. package/dist/src/commands/extensions/update.js +3 -2
  10. package/dist/src/commands/extensions/update.js.map +1 -1
  11. package/dist/src/commands/extensions/validate.d.ts +12 -0
  12. package/dist/src/commands/extensions/validate.js +83 -0
  13. package/dist/src/commands/extensions/validate.js.map +1 -0
  14. package/dist/src/commands/extensions/validate.test.d.ts +6 -0
  15. package/dist/src/commands/extensions/validate.test.js +93 -0
  16. package/dist/src/commands/extensions/validate.test.js.map +1 -0
  17. package/dist/src/commands/extensions.js +2 -0
  18. package/dist/src/commands/extensions.js.map +1 -1
  19. package/dist/src/config/auth.js +0 -5
  20. package/dist/src/config/auth.js.map +1 -1
  21. package/dist/src/config/auth.test.js +1 -3
  22. package/dist/src/config/auth.test.js.map +1 -1
  23. package/dist/src/config/config.js +2 -1
  24. package/dist/src/config/config.js.map +1 -1
  25. package/dist/src/config/config.test.js +6 -10
  26. package/dist/src/config/config.test.js.map +1 -1
  27. package/dist/src/config/extensions/update.d.ts +2 -2
  28. package/dist/src/config/extensions/update.js +28 -22
  29. package/dist/src/config/extensions/update.js.map +1 -1
  30. package/dist/src/config/sandboxConfig.d.ts +1 -1
  31. package/dist/src/config/sandboxConfig.js +6 -3
  32. package/dist/src/config/sandboxConfig.js.map +1 -1
  33. package/dist/src/config/settings.js +2 -2
  34. package/dist/src/config/settings.js.map +1 -1
  35. package/dist/src/config/settings.test.js +13 -53
  36. package/dist/src/config/settings.test.js.map +1 -1
  37. package/dist/src/config/settingsSchema.d.ts +25 -7
  38. package/dist/src/config/settingsSchema.js +24 -6
  39. package/dist/src/config/settingsSchema.js.map +1 -1
  40. package/dist/src/config/settingsSchema.test.js +2 -0
  41. package/dist/src/config/settingsSchema.test.js.map +1 -1
  42. package/dist/src/gemini.js +8 -1
  43. package/dist/src/gemini.js.map +1 -1
  44. package/dist/src/gemini.test.js +1 -0
  45. package/dist/src/gemini.test.js.map +1 -1
  46. package/dist/src/generated/git-commit.d.ts +2 -2
  47. package/dist/src/generated/git-commit.js +2 -2
  48. package/dist/src/generated/git-commit.js.map +1 -1
  49. package/dist/src/nonInteractiveCli.d.ts +9 -1
  50. package/dist/src/nonInteractiveCli.js +16 -1
  51. package/dist/src/nonInteractiveCli.js.map +1 -1
  52. package/dist/src/nonInteractiveCli.test.js +209 -103
  53. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  54. package/dist/src/test-utils/async.d.ts +9 -0
  55. package/dist/src/test-utils/async.js +29 -0
  56. package/dist/src/test-utils/async.js.map +1 -0
  57. package/dist/src/test-utils/render.d.ts +2 -1
  58. package/dist/src/test-utils/render.js +23 -1
  59. package/dist/src/test-utils/render.js.map +1 -1
  60. package/dist/src/test-utils/render.test.js +29 -4
  61. package/dist/src/test-utils/render.test.js.map +1 -1
  62. package/dist/src/ui/App.test.js +1 -1
  63. package/dist/src/ui/App.test.js.map +1 -1
  64. package/dist/src/ui/AppContainer.js +33 -4
  65. package/dist/src/ui/AppContainer.js.map +1 -1
  66. package/dist/src/ui/AppContainer.test.js +205 -99
  67. package/dist/src/ui/AppContainer.test.js.map +1 -1
  68. package/dist/src/ui/auth/ApiAuthDialog.d.ts +14 -0
  69. package/dist/src/ui/auth/ApiAuthDialog.js +26 -0
  70. package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -0
  71. package/dist/src/ui/auth/ApiAuthDialog.test.d.ts +6 -0
  72. package/dist/src/ui/auth/ApiAuthDialog.test.js +91 -0
  73. package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -0
  74. package/dist/src/ui/auth/AuthDialog.js +7 -3
  75. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  76. package/dist/src/ui/auth/AuthDialog.test.js +1 -1
  77. package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
  78. package/dist/src/ui/auth/useAuth.d.ts +2 -0
  79. package/dist/src/ui/auth/useAuth.js +31 -2
  80. package/dist/src/ui/auth/useAuth.js.map +1 -1
  81. package/dist/src/ui/components/AnsiOutput.test.js +1 -1
  82. package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
  83. package/dist/src/ui/components/Composer.test.js +1 -1
  84. package/dist/src/ui/components/Composer.test.js.map +1 -1
  85. package/dist/src/ui/components/ConsentPrompt.test.js +18 -8
  86. package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -1
  87. package/dist/src/ui/components/ContextSummaryDisplay.test.js +11 -6
  88. package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -1
  89. package/dist/src/ui/components/DialogManager.js +4 -0
  90. package/dist/src/ui/components/DialogManager.js.map +1 -1
  91. package/dist/src/ui/components/FolderTrustDialog.test.js +4 -3
  92. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  93. package/dist/src/ui/components/Footer.js +3 -2
  94. package/dist/src/ui/components/Footer.js.map +1 -1
  95. package/dist/src/ui/components/Footer.test.js +59 -0
  96. package/dist/src/ui/components/Footer.test.js.map +1 -1
  97. package/dist/src/ui/components/Header.test.js +9 -5
  98. package/dist/src/ui/components/Header.test.js.map +1 -1
  99. package/dist/src/ui/components/Help.test.js +5 -3
  100. package/dist/src/ui/components/Help.test.js.map +1 -1
  101. package/dist/src/ui/components/InputPrompt.test.js +139 -111
  102. package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  103. package/dist/src/ui/components/LoadingIndicator.test.js +27 -14
  104. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  105. package/dist/src/ui/components/ModelDialog.test.js +20 -10
  106. package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
  107. package/dist/src/ui/components/ModelStatsDisplay.test.js +1 -1
  108. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -1
  109. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +11 -10
  110. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  111. package/dist/src/ui/components/PrepareLabel.test.js +13 -7
  112. package/dist/src/ui/components/PrepareLabel.test.js.map +1 -1
  113. package/dist/src/ui/components/ProQuotaDialog.test.js +14 -6
  114. package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
  115. package/dist/src/ui/components/QueuedMessageDisplay.test.js +11 -6
  116. package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
  117. package/dist/src/ui/components/SessionSummaryDisplay.test.js +1 -1
  118. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -1
  119. package/dist/src/ui/components/SettingsDialog.test.js +438 -512
  120. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  121. package/dist/src/ui/components/StatsDisplay.test.js +1 -1
  122. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -1
  123. package/dist/src/ui/components/ThemeDialog.test.js +3 -2
  124. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  125. package/dist/src/ui/components/ToolStatsDisplay.test.js +1 -1
  126. package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -1
  127. package/dist/src/ui/components/messages/CompressionMessage.test.js +25 -17
  128. package/dist/src/ui/components/messages/CompressionMessage.test.js.map +1 -1
  129. package/dist/src/ui/components/messages/DiffRenderer.test.js +1 -1
  130. package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
  131. package/dist/src/ui/components/messages/Todo.js +27 -5
  132. package/dist/src/ui/components/messages/Todo.js.map +1 -1
  133. package/dist/src/ui/components/messages/Todo.test.js +20 -8
  134. package/dist/src/ui/components/messages/Todo.test.js.map +1 -1
  135. package/dist/src/ui/components/messages/ToolGroupMessage.test.js +29 -15
  136. package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
  137. package/dist/src/ui/components/shared/BaseSelectionList.test.js +12 -11
  138. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  139. package/dist/src/ui/components/shared/MaxSizedBox.test.js +43 -22
  140. package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -1
  141. package/dist/src/ui/components/shared/TextInput.d.ts +15 -0
  142. package/dist/src/ui/components/shared/TextInput.js +38 -0
  143. package/dist/src/ui/components/shared/TextInput.js.map +1 -0
  144. package/dist/src/ui/components/shared/TextInput.test.d.ts +6 -0
  145. package/dist/src/ui/components/shared/TextInput.test.js +242 -0
  146. package/dist/src/ui/components/shared/TextInput.test.js.map +1 -0
  147. package/dist/src/ui/components/shared/text-buffer.d.ts +8 -2
  148. package/dist/src/ui/components/shared/text-buffer.js +28 -13
  149. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  150. package/dist/src/ui/components/shared/text-buffer.test.js +137 -0
  151. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
  152. package/dist/src/ui/components/views/ChatList.test.js +7 -4
  153. package/dist/src/ui/components/views/ChatList.test.js.map +1 -1
  154. package/dist/src/ui/components/views/ExtensionsList.js +1 -0
  155. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
  156. package/dist/src/ui/components/views/ExtensionsList.test.js +13 -5
  157. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
  158. package/dist/src/ui/components/views/McpStatus.test.js +23 -12
  159. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
  160. package/dist/src/ui/contexts/KeypressContext.test.js +132 -252
  161. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  162. package/dist/src/ui/contexts/SessionContext.test.js +9 -5
  163. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
  164. package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
  165. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  166. package/dist/src/ui/contexts/UIStateContext.d.ts +2 -0
  167. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  168. package/dist/src/ui/hooks/atCommandProcessor.js +29 -7
  169. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  170. package/dist/src/ui/hooks/atCommandProcessor.test.js +163 -64
  171. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  172. package/dist/src/ui/hooks/shellCommandProcessor.test.js +47 -34
  173. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  174. package/dist/src/ui/hooks/slashCommandProcessor.test.js +141 -104
  175. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
  176. package/dist/src/ui/hooks/useAtCompletion.test.js +23 -21
  177. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -1
  178. package/dist/src/ui/hooks/useCommandCompletion.test.js +16 -15
  179. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -1
  180. package/dist/src/ui/hooks/useConsoleMessages.test.js +1 -1
  181. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -1
  182. package/dist/src/ui/hooks/useEditorSettings.test.js +1 -1
  183. package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
  184. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +1 -1
  185. package/dist/src/ui/hooks/useExtensionUpdates.js +9 -3
  186. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  187. package/dist/src/ui/hooks/useExtensionUpdates.test.js +10 -9
  188. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  189. package/dist/src/ui/hooks/useFocus.test.js +1 -1
  190. package/dist/src/ui/hooks/useFocus.test.js.map +1 -1
  191. package/dist/src/ui/hooks/useFolderTrust.test.js +7 -6
  192. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
  193. package/dist/src/ui/hooks/useGeminiStream.test.js +39 -38
  194. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
  195. package/dist/src/ui/hooks/useGitBranchName.test.js +5 -4
  196. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  197. package/dist/src/ui/hooks/useIdeTrustListener.test.js +25 -11
  198. package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -1
  199. package/dist/src/ui/hooks/useKeypress.test.js +1 -1
  200. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
  201. package/dist/src/ui/hooks/useLoadingIndicator.test.js +1 -1
  202. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  203. package/dist/src/ui/hooks/useMemoryMonitor.test.js +1 -1
  204. package/dist/src/ui/hooks/useMemoryMonitor.test.js.map +1 -1
  205. package/dist/src/ui/hooks/useMessageQueue.test.js +5 -4
  206. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  207. package/dist/src/ui/hooks/useModelCommand.test.js +7 -4
  208. package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
  209. package/dist/src/ui/hooks/usePhraseCycler.test.js +46 -16
  210. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
  211. package/dist/src/ui/hooks/usePrivacySettings.test.js +11 -7
  212. package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
  213. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +28 -14
  214. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
  215. package/dist/src/ui/hooks/useSelectionList.test.js +116 -109
  216. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
  217. package/dist/src/ui/hooks/useShellHistory.test.js +25 -17
  218. package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
  219. package/dist/src/ui/hooks/useSlashCompletion.js +18 -7
  220. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  221. package/dist/src/ui/hooks/useSlashCompletion.test.js +244 -111
  222. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  223. package/dist/src/ui/hooks/useTimer.test.js +1 -1
  224. package/dist/src/ui/hooks/useTimer.test.js.map +1 -1
  225. package/dist/src/ui/hooks/useToolScheduler.test.js +6 -2
  226. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  227. package/dist/src/ui/hooks/vim.test.js +6 -21
  228. package/dist/src/ui/hooks/vim.test.js.map +1 -1
  229. package/dist/src/ui/state/extensions.d.ts +1 -0
  230. package/dist/src/ui/state/extensions.js +1 -0
  231. package/dist/src/ui/state/extensions.js.map +1 -1
  232. package/dist/src/ui/types.d.ts +1 -0
  233. package/dist/src/ui/types.js +2 -0
  234. package/dist/src/ui/types.js.map +1 -1
  235. package/dist/src/ui/utils/clipboardUtils.js +2 -2
  236. package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
  237. package/dist/src/ui/utils/updateCheck.js +6 -3
  238. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  239. package/dist/src/ui/utils/updateCheck.test.js +5 -1
  240. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  241. package/dist/src/utils/commentJson.js +2 -2
  242. package/dist/src/utils/commentJson.js.map +1 -1
  243. package/dist/src/utils/commentJson.test.js +7 -6
  244. package/dist/src/utils/commentJson.test.js.map +1 -1
  245. package/dist/src/utils/version.js +6 -2
  246. package/dist/src/utils/version.js.map +1 -1
  247. package/dist/src/zed-integration/acp.js +2 -1
  248. package/dist/src/zed-integration/acp.js.map +1 -1
  249. package/dist/tsconfig.tsbuildinfo +1 -1
  250. package/package.json +3 -3
  251. package/dist/google-gemini-cli-0.12.0-preview.3.tgz +0 -0
  252. package/dist/src/utils/package.d.ts +0 -12
  253. package/dist/src/utils/package.js +0 -24
  254. package/dist/src/utils/package.js.map +0 -1
@@ -1,12 +1,12 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
1
  /**
3
2
  * @license
4
3
  * Copyright 2025 Google LLC
5
4
  * SPDX-License-Identifier: Apache-2.0
6
5
  */
7
- import { vi, describe, it, expect, beforeEach } from 'vitest';
6
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
8
7
  import { act } from 'react';
9
- import { render } from 'ink-testing-library';
8
+ import { renderHook } from '../../test-utils/render.js';
9
+ import { waitFor } from '../../test-utils/async.js';
10
10
  import { useSlashCommandProcessor } from './slashCommandProcessor.js';
11
11
  import { CommandKind } from '../commands/types.js';
12
12
  import { MessageType } from '../types.js';
@@ -23,6 +23,12 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
23
23
  ...original,
24
24
  logSlashCommand,
25
25
  getIdeInstaller: vi.fn().mockReturnValue(null),
26
+ IdeClient: {
27
+ getInstance: vi.fn().mockResolvedValue({
28
+ addStatusChangeListener: vi.fn(),
29
+ removeStatusChangeListener: vi.fn(),
30
+ }),
31
+ },
26
32
  };
27
33
  });
28
34
  const { mockProcessExit } = vi.hoisted(() => ({
@@ -84,6 +90,7 @@ describe('useSlashCommandProcessor', () => {
84
90
  const mockSetQuittingMessages = vi.fn();
85
91
  const mockConfig = makeFakeConfig({});
86
92
  const mockSettings = {};
93
+ let unmountHook;
87
94
  beforeEach(() => {
88
95
  vi.clearAllMocks();
89
96
  vi.mocked(BuiltinCommandLoader).mockClear();
@@ -91,13 +98,21 @@ describe('useSlashCommandProcessor', () => {
91
98
  mockFileLoadCommands.mockResolvedValue([]);
92
99
  mockMcpLoadCommands.mockResolvedValue([]);
93
100
  });
94
- const setupProcessorHook = (builtinCommands = [], fileCommands = [], mcpCommands = [], setIsProcessing = vi.fn()) => {
101
+ afterEach(async () => {
102
+ if (unmountHook) {
103
+ await unmountHook();
104
+ unmountHook = undefined;
105
+ }
106
+ });
107
+ const setupProcessorHook = async (builtinCommands = [], fileCommands = [], mcpCommands = [], setIsProcessing = vi.fn()) => {
95
108
  mockBuiltinLoadCommands.mockResolvedValue(Object.freeze(builtinCommands));
96
109
  mockFileLoadCommands.mockResolvedValue(Object.freeze(fileCommands));
97
110
  mockMcpLoadCommands.mockResolvedValue(Object.freeze(mcpCommands));
98
- let hookResult;
99
- function TestComponent() {
100
- hookResult = useSlashCommandProcessor(mockConfig, mockSettings, mockAddItem, mockClearItems, mockLoadHistory, vi.fn(), // refreshStatic
111
+ let result;
112
+ let unmount;
113
+ let rerender;
114
+ await act(async () => {
115
+ const hook = renderHook(() => useSlashCommandProcessor(mockConfig, mockSettings, mockAddItem, mockClearItems, mockLoadHistory, vi.fn(), // refreshStatic
101
116
  vi.fn(), // toggleVimEnabled
102
117
  setIsProcessing, vi.fn(), // setGeminiMdFileCount
103
118
  {
@@ -115,29 +130,36 @@ describe('useSlashCommandProcessor', () => {
115
130
  dispatchExtensionStateUpdate: vi.fn(),
116
131
  addConfirmUpdateExtensionRequest: vi.fn(),
117
132
  }, new Map(), // extensionsUpdateState
118
- true);
119
- return null;
120
- }
121
- const { unmount, rerender } = render(_jsx(TestComponent, {}));
133
+ true));
134
+ result = hook.result;
135
+ unmount = hook.unmount;
136
+ rerender = hook.rerender;
137
+ });
138
+ unmountHook = async () => unmount();
139
+ await waitFor(() => {
140
+ expect(result.current.slashCommands).toBeDefined();
141
+ });
122
142
  return {
123
143
  get current() {
124
- return hookResult;
144
+ return result.current;
125
145
  },
126
146
  unmount,
127
- rerender: () => rerender(_jsx(TestComponent, {})),
147
+ rerender: async () => {
148
+ rerender();
149
+ },
128
150
  };
129
151
  };
130
152
  describe('Initialization and Command Loading', () => {
131
- it('should initialize CommandService with all required loaders', () => {
132
- setupProcessorHook();
153
+ it('should initialize CommandService with all required loaders', async () => {
154
+ await setupProcessorHook();
133
155
  expect(BuiltinCommandLoader).toHaveBeenCalledWith(mockConfig);
134
156
  expect(FileCommandLoader).toHaveBeenCalledWith(mockConfig);
135
157
  expect(McpPromptLoader).toHaveBeenCalledWith(mockConfig);
136
158
  });
137
159
  it('should call loadCommands and populate state after mounting', async () => {
138
160
  const testCommand = createTestCommand({ name: 'test' });
139
- const result = setupProcessorHook([testCommand]);
140
- await vi.waitFor(() => {
161
+ const result = await setupProcessorHook([testCommand]);
162
+ await waitFor(() => {
141
163
  expect(result.current.slashCommands).toHaveLength(1);
142
164
  });
143
165
  expect(result.current.slashCommands?.[0]?.name).toBe('test');
@@ -147,8 +169,8 @@ describe('useSlashCommandProcessor', () => {
147
169
  });
148
170
  it('should provide an immutable array of commands to consumers', async () => {
149
171
  const testCommand = createTestCommand({ name: 'test' });
150
- const result = setupProcessorHook([testCommand]);
151
- await vi.waitFor(() => {
172
+ const result = await setupProcessorHook([testCommand]);
173
+ await waitFor(() => {
152
174
  expect(result.current.slashCommands).toHaveLength(1);
153
175
  });
154
176
  const commands = result.current.slashCommands;
@@ -166,8 +188,8 @@ describe('useSlashCommandProcessor', () => {
166
188
  action: builtinAction,
167
189
  });
168
190
  const fileCommand = createTestCommand({ name: 'override', description: 'file', action: fileAction }, CommandKind.FILE);
169
- const result = setupProcessorHook([builtinCommand], [fileCommand]);
170
- await vi.waitFor(() => {
191
+ const result = await setupProcessorHook([builtinCommand], [fileCommand]);
192
+ await waitFor(() => {
171
193
  // The service should only return one command with the name 'override'
172
194
  expect(result.current.slashCommands).toHaveLength(1);
173
195
  });
@@ -181,8 +203,8 @@ describe('useSlashCommandProcessor', () => {
181
203
  });
182
204
  describe('Command Execution Logic', () => {
183
205
  it('should display an error for an unknown command', async () => {
184
- const result = setupProcessorHook();
185
- await vi.waitFor(() => expect(result.current.slashCommands).toBeDefined());
206
+ const result = await setupProcessorHook();
207
+ await waitFor(() => expect(result.current.slashCommands).toBeDefined());
186
208
  await act(async () => {
187
209
  await result.current.handleSlashCommand('/nonexistent');
188
210
  });
@@ -206,8 +228,8 @@ describe('useSlashCommandProcessor', () => {
206
228
  },
207
229
  ],
208
230
  };
209
- const result = setupProcessorHook([parentCommand]);
210
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
231
+ const result = await setupProcessorHook([parentCommand]);
232
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
211
233
  await act(async () => {
212
234
  await result.current.handleSlashCommand('/parent');
213
235
  });
@@ -232,8 +254,8 @@ describe('useSlashCommandProcessor', () => {
232
254
  },
233
255
  ],
234
256
  };
235
- const result = setupProcessorHook([parentCommand]);
236
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
257
+ const result = await setupProcessorHook([parentCommand]);
258
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
237
259
  await act(async () => {
238
260
  await result.current.handleSlashCommand('/parent child with args');
239
261
  });
@@ -249,7 +271,7 @@ describe('useSlashCommandProcessor', () => {
249
271
  });
250
272
  it('sets isProcessing to false if the the input is not a command', async () => {
251
273
  const setMockIsProcessing = vi.fn();
252
- const result = setupProcessorHook([], [], [], setMockIsProcessing);
274
+ const result = await setupProcessorHook([], [], [], setMockIsProcessing);
253
275
  await act(async () => {
254
276
  await result.current.handleSlashCommand('imnotacommand');
255
277
  });
@@ -261,8 +283,8 @@ describe('useSlashCommandProcessor', () => {
261
283
  name: 'fail',
262
284
  action: vi.fn().mockRejectedValue(new Error('oh no!')),
263
285
  });
264
- const result = setupProcessorHook([failCommand], [], [], setMockIsProcessing);
265
- await vi.waitFor(() => expect(result.current.slashCommands).toBeDefined());
286
+ const result = await setupProcessorHook([failCommand], [], [], setMockIsProcessing);
287
+ await waitFor(() => expect(result.current.slashCommands).toBeDefined());
266
288
  await act(async () => {
267
289
  await result.current.handleSlashCommand('/fail');
268
290
  });
@@ -275,8 +297,8 @@ describe('useSlashCommandProcessor', () => {
275
297
  name: 'long-running',
276
298
  action: () => new Promise((resolve) => setTimeout(resolve, 50)),
277
299
  });
278
- const result = setupProcessorHook([command], [], [], mockSetIsProcessing);
279
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
300
+ const result = await setupProcessorHook([command], [], [], mockSetIsProcessing);
301
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
280
302
  const executionPromise = act(async () => {
281
303
  await result.current.handleSlashCommand('/long-running');
282
304
  });
@@ -296,8 +318,8 @@ describe('useSlashCommandProcessor', () => {
296
318
  name: 'themecmd',
297
319
  action: vi.fn().mockResolvedValue({ type: 'dialog', dialog: 'theme' }),
298
320
  });
299
- const result = setupProcessorHook([command]);
300
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
321
+ const result = await setupProcessorHook([command]);
322
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
301
323
  await act(async () => {
302
324
  await result.current.handleSlashCommand('/themecmd');
303
325
  });
@@ -308,8 +330,8 @@ describe('useSlashCommandProcessor', () => {
308
330
  name: 'modelcmd',
309
331
  action: vi.fn().mockResolvedValue({ type: 'dialog', dialog: 'model' }),
310
332
  });
311
- const result = setupProcessorHook([command]);
312
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
333
+ const result = await setupProcessorHook([command]);
334
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
313
335
  await act(async () => {
314
336
  await result.current.handleSlashCommand('/modelcmd');
315
337
  });
@@ -329,8 +351,8 @@ describe('useSlashCommandProcessor', () => {
329
351
  clientHistory: [{ role: 'user', parts: [{ text: 'old prompt' }] }],
330
352
  }),
331
353
  });
332
- const result = setupProcessorHook([command]);
333
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
354
+ const result = await setupProcessorHook([command]);
355
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
334
356
  await act(async () => {
335
357
  await result.current.handleSlashCommand('/load');
336
358
  });
@@ -357,8 +379,8 @@ describe('useSlashCommandProcessor', () => {
357
379
  clientHistory: historyWithThoughts,
358
380
  }),
359
381
  });
360
- const result = setupProcessorHook([command]);
361
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
382
+ const result = await setupProcessorHook([command]);
383
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
362
384
  await act(async () => {
363
385
  await result.current.handleSlashCommand('/loadwiththoughts');
364
386
  });
@@ -373,8 +395,8 @@ describe('useSlashCommandProcessor', () => {
373
395
  name: 'exit',
374
396
  action: quitAction,
375
397
  });
376
- const result = setupProcessorHook([command]);
377
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
398
+ const result = await setupProcessorHook([command]);
399
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
378
400
  await act(async () => {
379
401
  await result.current.handleSlashCommand('/exit');
380
402
  });
@@ -389,8 +411,8 @@ describe('useSlashCommandProcessor', () => {
389
411
  content: [{ text: 'The actual prompt from the TOML file.' }],
390
412
  }),
391
413
  }, CommandKind.FILE);
392
- const result = setupProcessorHook([], [fileCommand]);
393
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
414
+ const result = await setupProcessorHook([], [fileCommand]);
415
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
394
416
  let actionResult;
395
417
  await act(async () => {
396
418
  actionResult = await result.current.handleSlashCommand('/filecmd');
@@ -410,8 +432,8 @@ describe('useSlashCommandProcessor', () => {
410
432
  content: [{ text: 'The actual prompt from the mcp command.' }],
411
433
  }),
412
434
  }, CommandKind.MCP_PROMPT);
413
- const result = setupProcessorHook([], [], [mcpCommand]);
414
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
435
+ const result = await setupProcessorHook([], [], [mcpCommand]);
436
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
415
437
  let actionResult;
416
438
  await act(async () => {
417
439
  actionResult = await result.current.handleSlashCommand('/mcpcmd');
@@ -441,30 +463,33 @@ describe('useSlashCommandProcessor', () => {
441
463
  });
442
464
  });
443
465
  it('should set confirmation request when action returns confirm_shell_commands', async () => {
444
- const result = setupProcessorHook([shellCommand]);
445
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
446
- // This is intentionally not awaited, because the promise it returns
447
- // will not resolve until the user responds to the confirmation.
448
- act(() => {
449
- result.current.handleSlashCommand('/shellcmd');
466
+ const result = await setupProcessorHook([shellCommand]);
467
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
468
+ // Trigger command, don't await it yet as it suspends for confirmation
469
+ await act(async () => {
470
+ void result.current.handleSlashCommand('/shellcmd');
450
471
  });
451
472
  // We now wait for the state to be updated with the request.
452
- await vi.waitFor(() => {
453
- expect(result.current.shellConfirmationRequest).not.toBeNull();
473
+ await act(async () => {
474
+ await waitFor(() => {
475
+ expect(result.current.shellConfirmationRequest).not.toBeNull();
476
+ });
454
477
  });
455
478
  expect(result.current.shellConfirmationRequest?.commands).toEqual([
456
479
  'rm -rf /',
457
480
  ]);
458
481
  });
459
482
  it('should do nothing if user cancels confirmation', async () => {
460
- const result = setupProcessorHook([shellCommand]);
461
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
462
- act(() => {
463
- result.current.handleSlashCommand('/shellcmd');
483
+ const result = await setupProcessorHook([shellCommand]);
484
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
485
+ await act(async () => {
486
+ void result.current.handleSlashCommand('/shellcmd');
464
487
  });
465
488
  // Wait for the confirmation dialog to be set
466
- await vi.waitFor(() => {
467
- expect(result.current.shellConfirmationRequest).not.toBeNull();
489
+ await act(async () => {
490
+ await waitFor(() => {
491
+ expect(result.current.shellConfirmationRequest).not.toBeNull();
492
+ });
468
493
  });
469
494
  const onConfirm = result.current.shellConfirmationRequest?.onConfirm;
470
495
  expect(onConfirm).toBeDefined();
@@ -483,13 +508,16 @@ describe('useSlashCommandProcessor', () => {
483
508
  expect(mockCommandAction).toHaveBeenCalledTimes(1);
484
509
  });
485
510
  it('should re-run command with one-time allowlist on "Proceed Once"', async () => {
486
- const result = setupProcessorHook([shellCommand]);
487
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
488
- act(() => {
489
- result.current.handleSlashCommand('/shellcmd');
511
+ const result = await setupProcessorHook([shellCommand]);
512
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
513
+ let commandPromise;
514
+ await act(async () => {
515
+ commandPromise = result.current.handleSlashCommand('/shellcmd');
490
516
  });
491
- await vi.waitFor(() => {
492
- expect(result.current.shellConfirmationRequest).not.toBeNull();
517
+ await act(async () => {
518
+ await waitFor(() => {
519
+ expect(result.current.shellConfirmationRequest).not.toBeNull();
520
+ });
493
521
  });
494
522
  const onConfirm = result.current.shellConfirmationRequest?.onConfirm;
495
523
  // **Change the mock's behavior for the SECOND run.**
@@ -502,9 +530,12 @@ describe('useSlashCommandProcessor', () => {
502
530
  await act(async () => {
503
531
  onConfirm(ToolConfirmationOutcome.ProceedOnce, ['rm -rf /']);
504
532
  });
533
+ await act(async () => {
534
+ await commandPromise;
535
+ });
505
536
  expect(result.current.shellConfirmationRequest).toBeNull();
506
537
  // The action should have been called twice (initial + re-run).
507
- await vi.waitFor(() => {
538
+ await waitFor(() => {
508
539
  expect(mockCommandAction).toHaveBeenCalledTimes(2);
509
540
  });
510
541
  // We can inspect the context of the second call to ensure the one-time list was used.
@@ -516,19 +547,22 @@ describe('useSlashCommandProcessor', () => {
516
547
  // Verify the session-wide allowlist was NOT permanently updated.
517
548
  // Re-render the hook by calling a no-op command to get the latest context.
518
549
  await act(async () => {
519
- result.current.handleSlashCommand('/no-op');
550
+ await result.current.handleSlashCommand('/no-op');
520
551
  });
521
552
  const finalContext = result.current.commandContext;
522
553
  expect(finalContext.session.sessionShellAllowlist.size).toBe(0);
523
554
  });
524
555
  it('should re-run command and update session allowlist on "Proceed Always"', async () => {
525
- const result = setupProcessorHook([shellCommand]);
526
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
527
- act(() => {
528
- result.current.handleSlashCommand('/shellcmd');
556
+ const result = await setupProcessorHook([shellCommand]);
557
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
558
+ let commandPromise;
559
+ await act(async () => {
560
+ commandPromise = result.current.handleSlashCommand('/shellcmd');
529
561
  });
530
- await vi.waitFor(() => {
531
- expect(result.current.shellConfirmationRequest).not.toBeNull();
562
+ await act(async () => {
563
+ await waitFor(() => {
564
+ expect(result.current.shellConfirmationRequest).not.toBeNull();
565
+ });
532
566
  });
533
567
  const onConfirm = result.current.shellConfirmationRequest?.onConfirm;
534
568
  mockCommandAction.mockResolvedValue({
@@ -539,13 +573,16 @@ describe('useSlashCommandProcessor', () => {
539
573
  await act(async () => {
540
574
  onConfirm(ToolConfirmationOutcome.ProceedAlways, ['rm -rf /']);
541
575
  });
576
+ await act(async () => {
577
+ await commandPromise;
578
+ });
542
579
  expect(result.current.shellConfirmationRequest).toBeNull();
543
- await vi.waitFor(() => {
580
+ await waitFor(() => {
544
581
  expect(mockCommandAction).toHaveBeenCalledTimes(2);
545
582
  });
546
583
  expect(mockAddItem).toHaveBeenCalledWith({ type: MessageType.INFO, text: 'Success!' }, expect.any(Number));
547
584
  // Check that the session-wide allowlist WAS updated.
548
- await vi.waitFor(() => {
585
+ await waitFor(() => {
549
586
  const finalContext = result.current.commandContext;
550
587
  expect(finalContext.session.sessionShellAllowlist.has('rm -rf /')).toBe(true);
551
588
  });
@@ -554,8 +591,8 @@ describe('useSlashCommandProcessor', () => {
554
591
  describe('Command Parsing and Matching', () => {
555
592
  it('should be case-sensitive', async () => {
556
593
  const command = createTestCommand({ name: 'test' });
557
- const result = setupProcessorHook([command]);
558
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
594
+ const result = await setupProcessorHook([command]);
595
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
559
596
  await act(async () => {
560
597
  // Use uppercase when command is lowercase
561
598
  await result.current.handleSlashCommand('/Test');
@@ -574,8 +611,8 @@ describe('useSlashCommandProcessor', () => {
574
611
  description: 'a command with an alias',
575
612
  action,
576
613
  });
577
- const result = setupProcessorHook([command]);
578
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
614
+ const result = await setupProcessorHook([command]);
615
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
579
616
  await act(async () => {
580
617
  await result.current.handleSlashCommand('/alias');
581
618
  });
@@ -585,8 +622,8 @@ describe('useSlashCommandProcessor', () => {
585
622
  it('should handle extra whitespace around the command', async () => {
586
623
  const action = vi.fn();
587
624
  const command = createTestCommand({ name: 'test', action });
588
- const result = setupProcessorHook([command]);
589
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
625
+ const result = await setupProcessorHook([command]);
626
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
590
627
  await act(async () => {
591
628
  await result.current.handleSlashCommand(' /test with-args ');
592
629
  });
@@ -595,8 +632,8 @@ describe('useSlashCommandProcessor', () => {
595
632
  it('should handle `?` as a command prefix', async () => {
596
633
  const action = vi.fn();
597
634
  const command = createTestCommand({ name: 'help', action });
598
- const result = setupProcessorHook([command]);
599
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
635
+ const result = await setupProcessorHook([command]);
636
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
600
637
  await act(async () => {
601
638
  await result.current.handleSlashCommand('?help');
602
639
  });
@@ -613,8 +650,8 @@ describe('useSlashCommandProcessor', () => {
613
650
  action: mcpAction,
614
651
  }, CommandKind.MCP_PROMPT);
615
652
  const fileCommand = createTestCommand({ name: 'override', description: 'file', action: fileAction }, CommandKind.FILE);
616
- const result = setupProcessorHook([], [fileCommand], [mcpCommand]);
617
- await vi.waitFor(() => {
653
+ const result = await setupProcessorHook([], [fileCommand], [mcpCommand]);
654
+ await waitFor(() => {
618
655
  // The service should only return one command with the name 'override'
619
656
  expect(result.current.slashCommands).toHaveLength(1);
620
657
  });
@@ -639,8 +676,8 @@ describe('useSlashCommandProcessor', () => {
639
676
  }, CommandKind.FILE);
640
677
  // The order of commands in the final loaded array is not guaranteed,
641
678
  // so the test must work regardless of which comes first.
642
- const result = setupProcessorHook([quitCommand], [exitCommand]);
643
- await vi.waitFor(() => {
679
+ const result = await setupProcessorHook([quitCommand], [exitCommand]);
680
+ await waitFor(() => {
644
681
  expect(result.current.slashCommands).toHaveLength(2);
645
682
  });
646
683
  await act(async () => {
@@ -658,8 +695,8 @@ describe('useSlashCommandProcessor', () => {
658
695
  action: vi.fn(),
659
696
  });
660
697
  const exitCommand = createTestCommand({ name: 'exit', action: vi.fn() }, CommandKind.FILE);
661
- const result = setupProcessorHook([quitCommand], [exitCommand]);
662
- await vi.waitFor(() => expect(result.current.slashCommands).toHaveLength(2));
698
+ const result = await setupProcessorHook([quitCommand], [exitCommand]);
699
+ await waitFor(() => expect(result.current.slashCommands).toHaveLength(2));
663
700
  await act(async () => {
664
701
  await result.current.handleSlashCommand('/exit');
665
702
  });
@@ -668,9 +705,9 @@ describe('useSlashCommandProcessor', () => {
668
705
  });
669
706
  });
670
707
  describe('Lifecycle', () => {
671
- it('should abort command loading when the hook unmounts', () => {
708
+ it('should abort command loading when the hook unmounts', async () => {
672
709
  const abortSpy = vi.spyOn(AbortController.prototype, 'abort');
673
- const { unmount } = setupProcessorHook();
710
+ const { unmount } = await setupProcessorHook();
674
711
  unmount();
675
712
  expect(abortSpy).toHaveBeenCalledTimes(1);
676
713
  });
@@ -708,8 +745,8 @@ describe('useSlashCommandProcessor', () => {
708
745
  vi.mocked(logSlashCommand).mockClear();
709
746
  });
710
747
  it('should log a simple slash command', async () => {
711
- const result = setupProcessorHook(loggingTestCommands);
712
- await vi.waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
748
+ const result = await setupProcessorHook(loggingTestCommands);
749
+ await waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
713
750
  await act(async () => {
714
751
  await result.current.handleSlashCommand('/logtest');
715
752
  });
@@ -720,16 +757,16 @@ describe('useSlashCommandProcessor', () => {
720
757
  }));
721
758
  });
722
759
  it('logs nothing for a bogus command', async () => {
723
- const result = setupProcessorHook(loggingTestCommands);
724
- await vi.waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
760
+ const result = await setupProcessorHook(loggingTestCommands);
761
+ await waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
725
762
  await act(async () => {
726
763
  await result.current.handleSlashCommand('/bogusbogusbogus');
727
764
  });
728
765
  expect(logSlashCommand).not.toHaveBeenCalled();
729
766
  });
730
767
  it('logs a failure event for a failed command', async () => {
731
- const result = setupProcessorHook(loggingTestCommands);
732
- await vi.waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
768
+ const result = await setupProcessorHook(loggingTestCommands);
769
+ await waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
733
770
  await act(async () => {
734
771
  await result.current.handleSlashCommand('/fail');
735
772
  });
@@ -740,8 +777,8 @@ describe('useSlashCommandProcessor', () => {
740
777
  }));
741
778
  });
742
779
  it('should log a slash command with a subcommand', async () => {
743
- const result = setupProcessorHook(loggingTestCommands);
744
- await vi.waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
780
+ const result = await setupProcessorHook(loggingTestCommands);
781
+ await waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
745
782
  await act(async () => {
746
783
  await result.current.handleSlashCommand('/logwithsub sub');
747
784
  });
@@ -751,8 +788,8 @@ describe('useSlashCommandProcessor', () => {
751
788
  }));
752
789
  });
753
790
  it('should log the command path when an alias is used', async () => {
754
- const result = setupProcessorHook(loggingTestCommands);
755
- await vi.waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
791
+ const result = await setupProcessorHook(loggingTestCommands);
792
+ await waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
756
793
  await act(async () => {
757
794
  await result.current.handleSlashCommand('/la');
758
795
  });
@@ -761,8 +798,8 @@ describe('useSlashCommandProcessor', () => {
761
798
  }));
762
799
  });
763
800
  it('should not log for unknown commands', async () => {
764
- const result = setupProcessorHook(loggingTestCommands);
765
- await vi.waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
801
+ const result = await setupProcessorHook(loggingTestCommands);
802
+ await waitFor(() => expect(result.current.slashCommands?.length).toBeGreaterThan(0));
766
803
  await act(async () => {
767
804
  await result.current.handleSlashCommand('/unknown');
768
805
  });