@google/gemini-cli 0.16.0-preview.4 → 0.17.0-nightly.20251116.e650a4ee5

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 (130) hide show
  1. package/dist/google-gemini-cli-0.17.0-nightly.20251114.0fcbff506.tgz +0 -0
  2. package/dist/package.json +2 -2
  3. package/dist/src/commands/extensions/disable.test.d.ts +6 -0
  4. package/dist/src/commands/extensions/disable.test.js +160 -0
  5. package/dist/src/commands/extensions/disable.test.js.map +1 -0
  6. package/dist/src/commands/extensions/enable.test.d.ts +6 -0
  7. package/dist/src/commands/extensions/enable.test.js +140 -0
  8. package/dist/src/commands/extensions/enable.test.js.map +1 -0
  9. package/dist/src/commands/extensions/examples/mcp-server/example.test.d.ts +6 -0
  10. package/dist/src/commands/extensions/examples/mcp-server/example.test.js +111 -0
  11. package/dist/src/commands/extensions/examples/mcp-server/example.test.js.map +1 -0
  12. package/dist/src/commands/extensions/examples/mcp-server/example.test.ts +143 -0
  13. package/dist/src/commands/extensions/link.test.d.ts +6 -0
  14. package/dist/src/commands/extensions/link.test.js +121 -0
  15. package/dist/src/commands/extensions/link.test.js.map +1 -0
  16. package/dist/src/commands/extensions/list.test.d.ts +6 -0
  17. package/dist/src/commands/extensions/list.test.js +102 -0
  18. package/dist/src/commands/extensions/list.test.js.map +1 -0
  19. package/dist/src/commands/extensions/uninstall.test.js +107 -9
  20. package/dist/src/commands/extensions/uninstall.test.js.map +1 -1
  21. package/dist/src/commands/extensions/update.test.d.ts +6 -0
  22. package/dist/src/commands/extensions/update.test.js +160 -0
  23. package/dist/src/commands/extensions/update.test.js.map +1 -0
  24. package/dist/src/config/auth.js +1 -1
  25. package/dist/src/config/auth.test.js +2 -2
  26. package/dist/src/config/config.js +1 -3
  27. package/dist/src/config/config.js.map +1 -1
  28. package/dist/src/config/config.test.js +5 -2
  29. package/dist/src/config/config.test.js.map +1 -1
  30. package/dist/src/config/settingsSchema.d.ts +9 -0
  31. package/dist/src/config/settingsSchema.js +10 -1
  32. package/dist/src/config/settingsSchema.js.map +1 -1
  33. package/dist/src/config/trustedFolders.d.ts +1 -1
  34. package/dist/src/config/trustedFolders.js +24 -17
  35. package/dist/src/config/trustedFolders.js.map +1 -1
  36. package/dist/src/gemini.js +6 -4
  37. package/dist/src/gemini.js.map +1 -1
  38. package/dist/src/gemini.test.js.map +1 -1
  39. package/dist/src/generated/git-commit.d.ts +2 -2
  40. package/dist/src/generated/git-commit.js +2 -2
  41. package/dist/src/generated/git-commit.js.map +1 -1
  42. package/dist/src/ui/AppContainer.js +22 -4
  43. package/dist/src/ui/AppContainer.js.map +1 -1
  44. package/dist/src/ui/auth/AuthDialog.js +11 -3
  45. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  46. package/dist/src/ui/auth/AuthDialog.test.js +32 -2
  47. package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
  48. package/dist/src/ui/commands/directoryCommand.d.ts +0 -1
  49. package/dist/src/ui/commands/directoryCommand.js +104 -43
  50. package/dist/src/ui/commands/directoryCommand.js.map +1 -1
  51. package/dist/src/ui/commands/directoryCommand.test.js +91 -2
  52. package/dist/src/ui/commands/directoryCommand.test.js.map +1 -1
  53. package/dist/src/ui/commands/permissionsCommand.js +62 -5
  54. package/dist/src/ui/commands/permissionsCommand.js.map +1 -1
  55. package/dist/src/ui/commands/permissionsCommand.test.js +60 -4
  56. package/dist/src/ui/commands/permissionsCommand.test.js.map +1 -1
  57. package/dist/src/ui/commands/types.d.ts +7 -1
  58. package/dist/src/ui/commands/types.js.map +1 -1
  59. package/dist/src/ui/components/DialogManager.js +1 -1
  60. package/dist/src/ui/components/DialogManager.js.map +1 -1
  61. package/dist/src/ui/components/HistoryItemDisplay.js +2 -1
  62. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  63. package/dist/src/ui/components/MultiFolderTrustDialog.d.ts +23 -0
  64. package/dist/src/ui/components/MultiFolderTrustDialog.js +90 -0
  65. package/dist/src/ui/components/MultiFolderTrustDialog.js.map +1 -0
  66. package/dist/src/ui/components/MultiFolderTrustDialog.test.d.ts +6 -0
  67. package/dist/src/ui/components/MultiFolderTrustDialog.test.js +161 -0
  68. package/dist/src/ui/components/MultiFolderTrustDialog.test.js.map +1 -0
  69. package/dist/src/ui/components/PermissionsModifyTrustDialog.d.ts +5 -2
  70. package/dist/src/ui/components/PermissionsModifyTrustDialog.js +12 -7
  71. package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -1
  72. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +2 -2
  73. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  74. package/dist/src/ui/components/messages/ModelMessage.d.ts +11 -0
  75. package/dist/src/ui/components/messages/ModelMessage.js +5 -0
  76. package/dist/src/ui/components/messages/ModelMessage.js.map +1 -0
  77. package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
  78. package/dist/src/ui/contexts/UIActionsContext.js +1 -0
  79. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  80. package/dist/src/ui/contexts/UIStateContext.d.ts +4 -0
  81. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  82. package/dist/src/ui/hooks/slashCommandProcessor.d.ts +4 -2
  83. package/dist/src/ui/hooks/slashCommandProcessor.js +9 -2
  84. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  85. package/dist/src/ui/hooks/slashCommandProcessor.test.js +2 -1
  86. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
  87. package/dist/src/ui/hooks/useFolderTrust.js +11 -1
  88. package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
  89. package/dist/src/ui/hooks/useFolderTrust.test.js +22 -0
  90. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
  91. package/dist/src/ui/hooks/useGeminiStream.js +17 -0
  92. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  93. package/dist/src/ui/hooks/useIncludeDirsTrust.d.ts +8 -0
  94. package/dist/src/ui/hooks/useIncludeDirsTrust.js +120 -0
  95. package/dist/src/ui/hooks/useIncludeDirsTrust.js.map +1 -0
  96. package/dist/src/ui/hooks/useIncludeDirsTrust.test.d.ts +6 -0
  97. package/dist/src/ui/hooks/useIncludeDirsTrust.test.js +151 -0
  98. package/dist/src/ui/hooks/useIncludeDirsTrust.test.js.map +1 -0
  99. package/dist/src/ui/hooks/usePermissionsModifyTrust.d.ts +2 -2
  100. package/dist/src/ui/hooks/usePermissionsModifyTrust.js +44 -7
  101. package/dist/src/ui/hooks/usePermissionsModifyTrust.js.map +1 -1
  102. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +191 -89
  103. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
  104. package/dist/src/ui/layouts/DefaultAppLayout.js +1 -1
  105. package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -1
  106. package/dist/src/ui/noninteractive/nonInteractiveUi.js +1 -0
  107. package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -1
  108. package/dist/src/ui/types.d.ts +5 -1
  109. package/dist/src/ui/types.js.map +1 -1
  110. package/dist/src/ui/utils/directoryUtils.d.ts +6 -0
  111. package/dist/src/ui/utils/directoryUtils.js +21 -0
  112. package/dist/src/ui/utils/directoryUtils.js.map +1 -0
  113. package/dist/src/ui/utils/directoryUtils.test.d.ts +6 -0
  114. package/dist/src/ui/utils/directoryUtils.test.js +52 -0
  115. package/dist/src/ui/utils/directoryUtils.test.js.map +1 -0
  116. package/dist/src/zed-integration/acp.d.ts +2 -24
  117. package/dist/src/zed-integration/acp.js +2 -156
  118. package/dist/src/zed-integration/acp.js.map +1 -1
  119. package/dist/src/zed-integration/acp.test.d.ts +6 -0
  120. package/dist/src/zed-integration/acp.test.js +227 -0
  121. package/dist/src/zed-integration/acp.test.js.map +1 -0
  122. package/dist/src/zed-integration/connection.d.ts +28 -0
  123. package/dist/src/zed-integration/connection.js +163 -0
  124. package/dist/src/zed-integration/connection.js.map +1 -0
  125. package/dist/src/zed-integration/schema.d.ts +69 -13
  126. package/dist/src/zed-integration/schema.js +6 -5
  127. package/dist/src/zed-integration/schema.js.map +1 -1
  128. package/dist/tsconfig.tsbuildinfo +1 -1
  129. package/package.json +3 -3
  130. package/dist/google-gemini-cli-0.16.0-preview.3.tgz +0 -0
@@ -0,0 +1,151 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * @license
4
+ * Copyright 2025 Google LLC
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { vi, describe, it, expect, beforeEach } from 'vitest';
8
+ import { renderHook } from '../../test-utils/render.js';
9
+ import { waitFor } from '../../test-utils/async.js';
10
+ import { useIncludeDirsTrust } from './useIncludeDirsTrust.js';
11
+ import * as trustedFolders from '../../config/trustedFolders.js';
12
+ vi.mock('../utils/directoryUtils.js', () => ({
13
+ expandHomeDir: (p) => p, // Simple pass-through for testing
14
+ loadMemoryFromDirectories: vi.fn().mockResolvedValue({ fileCount: 1 }),
15
+ }));
16
+ vi.mock('../components/MultiFolderTrustDialog.js', () => ({
17
+ MultiFolderTrustDialog: (props) => (_jsx("div", { "data-testid": "mock-dialog", children: JSON.stringify(props.folders) })),
18
+ }));
19
+ describe('useIncludeDirsTrust', () => {
20
+ let mockConfig;
21
+ let mockHistoryManager;
22
+ let mockSetCustomDialog;
23
+ let mockWorkspaceContext;
24
+ beforeEach(() => {
25
+ vi.clearAllMocks();
26
+ mockWorkspaceContext = {
27
+ addDirectory: vi.fn(),
28
+ getDirectories: vi.fn().mockReturnValue([]),
29
+ onDirectoriesChangedListeners: new Set(),
30
+ onDirectoriesChanged: vi.fn(),
31
+ notifyDirectoriesChanged: vi.fn(),
32
+ resolveAndValidateDir: vi.fn(),
33
+ getInitialDirectories: vi.fn(),
34
+ setDirectories: vi.fn(),
35
+ isPathWithinWorkspace: vi.fn(),
36
+ fullyResolvedPath: vi.fn(),
37
+ isPathWithinRoot: vi.fn(),
38
+ isFileSymlink: vi.fn(),
39
+ };
40
+ mockConfig = {
41
+ getPendingIncludeDirectories: vi.fn().mockReturnValue([]),
42
+ clearPendingIncludeDirectories: vi.fn(),
43
+ getFolderTrust: vi.fn().mockReturnValue(true),
44
+ getWorkspaceContext: () => mockWorkspaceContext,
45
+ getGeminiClient: vi
46
+ .fn()
47
+ .mockReturnValue({ addDirectoryContext: vi.fn() }),
48
+ };
49
+ mockHistoryManager = {
50
+ addItem: vi.fn(),
51
+ history: [],
52
+ updateItem: vi.fn(),
53
+ clearItems: vi.fn(),
54
+ loadHistory: vi.fn(),
55
+ };
56
+ mockSetCustomDialog = vi.fn();
57
+ });
58
+ const renderTestHook = (isTrustedFolder) => {
59
+ renderHook(() => useIncludeDirsTrust(mockConfig, isTrustedFolder, mockHistoryManager, mockSetCustomDialog));
60
+ };
61
+ it('should do nothing if isTrustedFolder is undefined', () => {
62
+ vi.mocked(mockConfig.getPendingIncludeDirectories).mockReturnValue([
63
+ '/foo',
64
+ ]);
65
+ renderTestHook(undefined);
66
+ expect(mockConfig.clearPendingIncludeDirectories).not.toHaveBeenCalled();
67
+ });
68
+ it('should do nothing if there are no pending directories', () => {
69
+ renderTestHook(true);
70
+ expect(mockConfig.clearPendingIncludeDirectories).not.toHaveBeenCalled();
71
+ });
72
+ describe('when folder trust is disabled or workspace is untrusted', () => {
73
+ it.each([
74
+ { trustEnabled: false, isTrusted: true, scenario: 'trust is disabled' },
75
+ {
76
+ trustEnabled: true,
77
+ isTrusted: false,
78
+ scenario: 'workspace is untrusted',
79
+ },
80
+ ])('should add directories directly when $scenario', async ({ trustEnabled, isTrusted }) => {
81
+ vi.mocked(mockConfig.getFolderTrust).mockReturnValue(trustEnabled);
82
+ vi.mocked(mockConfig.getPendingIncludeDirectories).mockReturnValue([
83
+ '/dir1',
84
+ '/dir2',
85
+ ]);
86
+ vi.mocked(mockWorkspaceContext.addDirectory).mockImplementation((path) => {
87
+ if (path === '/dir2') {
88
+ throw new Error('Test error');
89
+ }
90
+ });
91
+ renderTestHook(isTrusted);
92
+ await waitFor(() => {
93
+ expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith('/dir1');
94
+ expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith('/dir2');
95
+ expect(mockHistoryManager.addItem).toHaveBeenCalledWith(expect.objectContaining({
96
+ text: expect.stringContaining("Error adding '/dir2': Test error"),
97
+ }), expect.any(Number));
98
+ expect(mockConfig.clearPendingIncludeDirectories).toHaveBeenCalledTimes(1);
99
+ });
100
+ });
101
+ });
102
+ describe('when folder trust is enabled and workspace is trusted', () => {
103
+ let mockIsPathTrusted;
104
+ beforeEach(() => {
105
+ vi.spyOn(mockConfig, 'getFolderTrust').mockReturnValue(true);
106
+ mockIsPathTrusted = vi.fn();
107
+ const mockLoadedFolders = {
108
+ isPathTrusted: mockIsPathTrusted,
109
+ };
110
+ vi.spyOn(trustedFolders, 'loadTrustedFolders').mockReturnValue(mockLoadedFolders);
111
+ });
112
+ afterEach(() => {
113
+ vi.restoreAllMocks();
114
+ });
115
+ it('should add trusted dirs, collect untrusted errors, and open dialog for undefined', async () => {
116
+ const pendingDirs = ['/trusted', '/untrusted', '/undefined'];
117
+ vi.mocked(mockConfig.getPendingIncludeDirectories).mockReturnValue(pendingDirs);
118
+ mockIsPathTrusted.mockImplementation((path) => {
119
+ if (path === '/trusted')
120
+ return true;
121
+ if (path === '/untrusted')
122
+ return false;
123
+ return undefined;
124
+ });
125
+ renderTestHook(true);
126
+ // Opens dialog for undefined trust dir
127
+ expect(mockSetCustomDialog).toHaveBeenCalledTimes(1);
128
+ const customDialogAction = mockSetCustomDialog.mock.calls[0][0];
129
+ expect(customDialogAction).toBeDefined();
130
+ const dialogProps = customDialogAction.props;
131
+ expect(dialogProps.folders).toEqual(['/undefined']);
132
+ expect(dialogProps.trustedDirs).toEqual(['/trusted']);
133
+ expect(dialogProps.errors).toEqual([
134
+ `The following directories are explicitly untrusted and cannot be added to a trusted workspace:\n- /untrusted\nPlease use the permissions command to modify their trust level.`,
135
+ ]);
136
+ });
137
+ it('should only add directories and clear pending if no dialog is needed', async () => {
138
+ const pendingDirs = ['/trusted1', '/trusted2'];
139
+ vi.mocked(mockConfig.getPendingIncludeDirectories).mockReturnValue(pendingDirs);
140
+ mockIsPathTrusted.mockReturnValue(true);
141
+ renderTestHook(true);
142
+ await waitFor(() => {
143
+ expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith('/trusted1');
144
+ expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith('/trusted2');
145
+ expect(mockSetCustomDialog).not.toHaveBeenCalled();
146
+ expect(mockConfig.clearPendingIncludeDirectories).toHaveBeenCalledTimes(1);
147
+ });
148
+ });
149
+ });
150
+ });
151
+ //# sourceMappingURL=useIncludeDirsTrust.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIncludeDirsTrust.test.js","sourceRoot":"","sources":["../../../../src/ui/hooks/useIncludeDirsTrust.test.tsx"],"names":[],"mappings":"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"}
@@ -5,13 +5,13 @@
5
5
  */
6
6
  import { TrustLevel } from '../../config/trustedFolders.js';
7
7
  import { type UseHistoryManagerReturn } from './useHistoryManager.js';
8
- export declare const usePermissionsModifyTrust: (onExit: () => void, addItem: UseHistoryManagerReturn["addItem"]) => {
8
+ export declare const usePermissionsModifyTrust: (onExit: () => void, addItem: UseHistoryManagerReturn["addItem"], targetDirectory: string) => {
9
9
  cwd: string;
10
10
  currentTrustLevel: TrustLevel | undefined;
11
11
  isInheritedTrustFromParent: boolean;
12
12
  isInheritedTrustFromIde: boolean;
13
13
  needsRestart: boolean;
14
14
  updateTrustLevel: (trustLevel: TrustLevel) => void;
15
- commitTrustLevelChange: () => void;
15
+ commitTrustLevelChange: () => boolean;
16
16
  isFolderTrustEnabled: boolean;
17
17
  };
@@ -5,13 +5,22 @@
5
5
  */
6
6
  import { useState, useCallback } from 'react';
7
7
  import * as process from 'node:process';
8
+ import * as path from 'node:path';
8
9
  import { loadTrustedFolders, TrustLevel, isWorkspaceTrusted, } from '../../config/trustedFolders.js';
9
10
  import { useSettings } from '../contexts/SettingsContext.js';
10
11
  import { MessageType } from '../types.js';
11
12
  import {} from './useHistoryManager.js';
12
- function getInitialTrustState(settings, cwd) {
13
+ import { coreEvents } from '@google/gemini-cli-core';
14
+ function getInitialTrustState(settings, cwd, isCurrentWorkspace) {
13
15
  const folders = loadTrustedFolders();
14
16
  const explicitTrustLevel = folders.user.config[cwd];
17
+ if (!isCurrentWorkspace) {
18
+ return {
19
+ currentTrustLevel: explicitTrustLevel,
20
+ isInheritedTrustFromParent: false,
21
+ isInheritedTrustFromIde: false,
22
+ };
23
+ }
15
24
  const { isTrusted, source } = isWorkspaceTrusted(settings.merged);
16
25
  const isInheritedTrust = isTrusted &&
17
26
  (!explicitTrustLevel || explicitTrustLevel === TrustLevel.DO_NOT_TRUST);
@@ -21,10 +30,14 @@ function getInitialTrustState(settings, cwd) {
21
30
  isInheritedTrustFromIde: !!(source === 'ide' && isInheritedTrust),
22
31
  };
23
32
  }
24
- export const usePermissionsModifyTrust = (onExit, addItem) => {
33
+ export const usePermissionsModifyTrust = (onExit, addItem, targetDirectory) => {
25
34
  const settings = useSettings();
26
- const cwd = process.cwd();
27
- const [initialState] = useState(() => getInitialTrustState(settings, cwd));
35
+ const cwd = targetDirectory;
36
+ // Normalize paths for case-insensitive file systems (macOS/Windows) to ensure
37
+ // accurate comparison between targetDirectory and process.cwd().
38
+ const isCurrentWorkspace = path.resolve(targetDirectory).toLowerCase() ===
39
+ path.resolve(process.cwd()).toLowerCase();
40
+ const [initialState] = useState(() => getInitialTrustState(settings, cwd, isCurrentWorkspace));
28
41
  const [currentTrustLevel] = useState(initialState.currentTrustLevel);
29
42
  const [pendingTrustLevel, setPendingTrustLevel] = useState();
30
43
  const [isInheritedTrustFromParent] = useState(initialState.isInheritedTrustFromParent);
@@ -32,6 +45,15 @@ export const usePermissionsModifyTrust = (onExit, addItem) => {
32
45
  const [needsRestart, setNeedsRestart] = useState(false);
33
46
  const isFolderTrustEnabled = !!settings.merged.security?.folderTrust?.enabled;
34
47
  const updateTrustLevel = useCallback((trustLevel) => {
48
+ // If we are not editing the current workspace, the logic is simple:
49
+ // just save the setting and exit. No restart or warnings are needed.
50
+ if (!isCurrentWorkspace) {
51
+ const folders = loadTrustedFolders();
52
+ folders.setValue(cwd, trustLevel);
53
+ onExit();
54
+ return;
55
+ }
56
+ // All logic below only applies when editing the current workspace.
35
57
  const wasTrusted = isWorkspaceTrusted(settings.merged).isTrusted;
36
58
  // Create a temporary config to check the new trust status without writing
37
59
  const currentConfig = loadTrustedFolders().user.config;
@@ -54,15 +76,30 @@ export const usePermissionsModifyTrust = (onExit, addItem) => {
54
76
  }
55
77
  else {
56
78
  const folders = loadTrustedFolders();
57
- folders.setValue(cwd, trustLevel);
79
+ try {
80
+ folders.setValue(cwd, trustLevel);
81
+ }
82
+ catch (_e) {
83
+ coreEvents.emitFeedback('error', 'Failed to save trust settings. Your changes may not persist.');
84
+ }
58
85
  onExit();
59
86
  }
60
- }, [cwd, settings.merged, onExit, addItem]);
87
+ }, [cwd, settings.merged, onExit, addItem, isCurrentWorkspace]);
61
88
  const commitTrustLevelChange = useCallback(() => {
62
89
  if (pendingTrustLevel) {
63
90
  const folders = loadTrustedFolders();
64
- folders.setValue(cwd, pendingTrustLevel);
91
+ try {
92
+ folders.setValue(cwd, pendingTrustLevel);
93
+ return true;
94
+ }
95
+ catch (_e) {
96
+ coreEvents.emitFeedback('error', 'Failed to save trust settings. Your changes may not persist.');
97
+ setNeedsRestart(false);
98
+ setPendingTrustLevel(undefined);
99
+ return false;
100
+ }
65
101
  }
102
+ return true;
66
103
  }, [cwd, pendingTrustLevel]);
67
104
  return {
68
105
  cwd,
@@ -1 +1 @@
1
- {"version":3,"file":"usePermissionsModifyTrust.js","sourceRoot":"","sources":["../../../../src/ui/hooks/usePermissionsModifyTrust.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usePermissionsModifyTrust.js","sourceRoot":"","sources":["../../../../src/ui/hooks/usePermissionsModifyTrust.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { act } from 'react';
8
8
  import { renderHook } from '../../test-utils/render.js';
9
9
  import { usePermissionsModifyTrust } from './usePermissionsModifyTrust.js';
10
10
  import { TrustLevel } from '../../config/trustedFolders.js';
11
+ import { coreEvents } from '@google/gemini-cli-core';
11
12
  // Hoist mocks
12
13
  const mockedCwd = vi.hoisted(() => vi.fn());
13
14
  const mockedLoadTrustedFolders = vi.hoisted(() => vi.fn());
@@ -17,6 +18,14 @@ const mockedUseSettings = vi.hoisted(() => vi.fn());
17
18
  vi.mock('node:process', () => ({
18
19
  cwd: mockedCwd,
19
20
  }));
21
+ vi.mock('node:path', async (importOriginal) => {
22
+ const actual = await importOriginal();
23
+ return {
24
+ ...(actual && typeof actual === 'object' ? actual : {}),
25
+ resolve: vi.fn((p) => p),
26
+ join: vi.fn((...args) => args.join('/')),
27
+ };
28
+ });
20
29
  vi.mock('../../config/trustedFolders.js', () => ({
21
30
  loadTrustedFolders: mockedLoadTrustedFolders,
22
31
  isWorkspaceTrusted: mockedIsWorkspaceTrusted,
@@ -53,60 +62,185 @@ describe('usePermissionsModifyTrust', () => {
53
62
  afterEach(() => {
54
63
  vi.resetAllMocks();
55
64
  });
56
- it('should initialize with the correct trust level', () => {
57
- mockedLoadTrustedFolders.mockReturnValue({
58
- user: { config: { '/test/dir': TrustLevel.TRUST_FOLDER } },
65
+ describe('when targetDirectory is the current workspace', () => {
66
+ it('should initialize with the correct trust level', () => {
67
+ mockedLoadTrustedFolders.mockReturnValue({
68
+ user: { config: { '/test/dir': TrustLevel.TRUST_FOLDER } },
69
+ });
70
+ mockedIsWorkspaceTrusted.mockReturnValue({
71
+ isTrusted: true,
72
+ source: 'file',
73
+ });
74
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
75
+ expect(result.current.currentTrustLevel).toBe(TrustLevel.TRUST_FOLDER);
59
76
  });
60
- mockedIsWorkspaceTrusted.mockReturnValue({
61
- isTrusted: true,
62
- source: 'file',
77
+ it('should detect inherited trust from parent', () => {
78
+ mockedLoadTrustedFolders.mockReturnValue({
79
+ user: { config: {} },
80
+ setValue: vi.fn(),
81
+ });
82
+ mockedIsWorkspaceTrusted.mockReturnValue({
83
+ isTrusted: true,
84
+ source: 'file',
85
+ });
86
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
87
+ expect(result.current.isInheritedTrustFromParent).toBe(true);
88
+ expect(result.current.isInheritedTrustFromIde).toBe(false);
63
89
  });
64
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
65
- expect(result.current.currentTrustLevel).toBe(TrustLevel.TRUST_FOLDER);
66
- });
67
- it('should detect inherited trust from parent', () => {
68
- mockedLoadTrustedFolders.mockReturnValue({
69
- user: { config: {} },
70
- setValue: vi.fn(),
90
+ it('should detect inherited trust from IDE', () => {
91
+ mockedLoadTrustedFolders.mockReturnValue({
92
+ user: { config: {} }, // No explicit trust
93
+ });
94
+ mockedIsWorkspaceTrusted.mockReturnValue({
95
+ isTrusted: true,
96
+ source: 'ide',
97
+ });
98
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
99
+ expect(result.current.isInheritedTrustFromIde).toBe(true);
100
+ expect(result.current.isInheritedTrustFromParent).toBe(false);
71
101
  });
72
- mockedIsWorkspaceTrusted.mockReturnValue({
73
- isTrusted: true,
74
- source: 'file',
102
+ it('should set needsRestart but not save when trust changes', () => {
103
+ const mockSetValue = vi.fn();
104
+ mockedLoadTrustedFolders.mockReturnValue({
105
+ user: { config: {} },
106
+ setValue: mockSetValue,
107
+ });
108
+ mockedIsWorkspaceTrusted
109
+ .mockReturnValueOnce({ isTrusted: false, source: 'file' })
110
+ .mockReturnValueOnce({ isTrusted: true, source: 'file' });
111
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
112
+ act(() => {
113
+ result.current.updateTrustLevel(TrustLevel.TRUST_FOLDER);
114
+ });
115
+ expect(result.current.needsRestart).toBe(true);
116
+ expect(mockSetValue).not.toHaveBeenCalled();
75
117
  });
76
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
77
- expect(result.current.isInheritedTrustFromParent).toBe(true);
78
- expect(result.current.isInheritedTrustFromIde).toBe(false);
79
- });
80
- it('should detect inherited trust from IDE', () => {
81
- mockedLoadTrustedFolders.mockReturnValue({
82
- user: { config: {} }, // No explicit trust
118
+ it('should save immediately if trust does not change', () => {
119
+ const mockSetValue = vi.fn();
120
+ mockedLoadTrustedFolders.mockReturnValue({
121
+ user: { config: {} },
122
+ setValue: mockSetValue,
123
+ });
124
+ mockedIsWorkspaceTrusted.mockReturnValue({
125
+ isTrusted: true,
126
+ source: 'file',
127
+ });
128
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
129
+ act(() => {
130
+ result.current.updateTrustLevel(TrustLevel.TRUST_PARENT);
131
+ });
132
+ expect(result.current.needsRestart).toBe(false);
133
+ expect(mockSetValue).toHaveBeenCalledWith('/test/dir', TrustLevel.TRUST_PARENT);
134
+ expect(mockOnExit).toHaveBeenCalled();
83
135
  });
84
- mockedIsWorkspaceTrusted.mockReturnValue({
85
- isTrusted: true,
86
- source: 'ide',
136
+ it('should commit the pending trust level change', () => {
137
+ const mockSetValue = vi.fn();
138
+ mockedLoadTrustedFolders.mockReturnValue({
139
+ user: { config: {} },
140
+ setValue: mockSetValue,
141
+ });
142
+ mockedIsWorkspaceTrusted
143
+ .mockReturnValueOnce({ isTrusted: false, source: 'file' })
144
+ .mockReturnValueOnce({ isTrusted: true, source: 'file' });
145
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
146
+ act(() => {
147
+ result.current.updateTrustLevel(TrustLevel.TRUST_FOLDER);
148
+ });
149
+ expect(result.current.needsRestart).toBe(true);
150
+ act(() => {
151
+ result.current.commitTrustLevelChange();
152
+ });
153
+ expect(mockSetValue).toHaveBeenCalledWith('/test/dir', TrustLevel.TRUST_FOLDER);
154
+ });
155
+ it('should add warning when setting DO_NOT_TRUST but still trusted by parent', () => {
156
+ mockedLoadTrustedFolders.mockReturnValue({
157
+ user: { config: {} },
158
+ setValue: vi.fn(),
159
+ });
160
+ mockedIsWorkspaceTrusted.mockReturnValue({
161
+ isTrusted: true,
162
+ source: 'file',
163
+ });
164
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
165
+ act(() => {
166
+ result.current.updateTrustLevel(TrustLevel.DO_NOT_TRUST);
167
+ });
168
+ expect(mockAddItem).toHaveBeenCalledWith({
169
+ type: 'warning',
170
+ text: 'Note: This folder is still trusted because a parent folder is trusted.',
171
+ }, expect.any(Number));
172
+ });
173
+ it('should add warning when setting DO_NOT_TRUST but still trusted by IDE', () => {
174
+ mockedLoadTrustedFolders.mockReturnValue({
175
+ user: { config: {} },
176
+ setValue: vi.fn(),
177
+ });
178
+ mockedIsWorkspaceTrusted.mockReturnValue({
179
+ isTrusted: true,
180
+ source: 'ide',
181
+ });
182
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
183
+ act(() => {
184
+ result.current.updateTrustLevel(TrustLevel.DO_NOT_TRUST);
185
+ });
186
+ expect(mockAddItem).toHaveBeenCalledWith({
187
+ type: 'warning',
188
+ text: 'Note: This folder is still trusted because the connected IDE workspace is trusted.',
189
+ }, expect.any(Number));
87
190
  });
88
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
89
- expect(result.current.isInheritedTrustFromIde).toBe(true);
90
- expect(result.current.isInheritedTrustFromParent).toBe(false);
91
191
  });
92
- it('should set needsRestart but not save when trust changes', () => {
93
- const mockSetValue = vi.fn();
94
- mockedLoadTrustedFolders.mockReturnValue({
95
- user: { config: {} },
96
- setValue: mockSetValue,
192
+ describe('when targetDirectory is not the current workspace', () => {
193
+ const otherDirectory = '/other/dir';
194
+ it('should not detect inherited trust', () => {
195
+ mockedLoadTrustedFolders.mockReturnValue({
196
+ user: { config: {} },
197
+ });
198
+ mockedIsWorkspaceTrusted.mockReturnValue({
199
+ isTrusted: true,
200
+ source: 'file',
201
+ });
202
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, otherDirectory));
203
+ expect(result.current.isInheritedTrustFromParent).toBe(false);
204
+ expect(result.current.isInheritedTrustFromIde).toBe(false);
97
205
  });
98
- mockedIsWorkspaceTrusted
99
- .mockReturnValueOnce({ isTrusted: false, source: 'file' })
100
- .mockReturnValueOnce({ isTrusted: true, source: 'file' });
101
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
102
- act(() => {
103
- result.current.updateTrustLevel(TrustLevel.TRUST_FOLDER);
206
+ it('should save immediately without needing a restart', () => {
207
+ const mockSetValue = vi.fn();
208
+ mockedLoadTrustedFolders.mockReturnValue({
209
+ user: { config: {} },
210
+ setValue: mockSetValue,
211
+ });
212
+ mockedIsWorkspaceTrusted.mockReturnValue({
213
+ isTrusted: false,
214
+ source: 'file',
215
+ });
216
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, otherDirectory));
217
+ act(() => {
218
+ result.current.updateTrustLevel(TrustLevel.TRUST_FOLDER);
219
+ });
220
+ expect(result.current.needsRestart).toBe(false);
221
+ expect(mockSetValue).toHaveBeenCalledWith(otherDirectory, TrustLevel.TRUST_FOLDER);
222
+ expect(mockOnExit).toHaveBeenCalled();
223
+ });
224
+ it('should not add a warning when setting DO_NOT_TRUST', () => {
225
+ mockedLoadTrustedFolders.mockReturnValue({
226
+ user: { config: {} },
227
+ setValue: vi.fn(),
228
+ });
229
+ mockedIsWorkspaceTrusted.mockReturnValue({
230
+ isTrusted: true,
231
+ source: 'file',
232
+ });
233
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, otherDirectory));
234
+ act(() => {
235
+ result.current.updateTrustLevel(TrustLevel.DO_NOT_TRUST);
236
+ });
237
+ expect(mockAddItem).not.toHaveBeenCalled();
104
238
  });
105
- expect(result.current.needsRestart).toBe(true);
106
- expect(mockSetValue).not.toHaveBeenCalled();
107
239
  });
108
- it('should save immediately if trust does not change', () => {
109
- const mockSetValue = vi.fn();
240
+ it('should emit feedback when setValue throws in updateTrustLevel', () => {
241
+ const mockSetValue = vi.fn().mockImplementation(() => {
242
+ throw new Error('test error');
243
+ });
110
244
  mockedLoadTrustedFolders.mockReturnValue({
111
245
  user: { config: {} },
112
246
  setValue: mockSetValue,
@@ -115,16 +249,18 @@ describe('usePermissionsModifyTrust', () => {
115
249
  isTrusted: true,
116
250
  source: 'file',
117
251
  });
118
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
252
+ const emitFeedbackSpy = vi.spyOn(coreEvents, 'emitFeedback');
253
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
119
254
  act(() => {
120
255
  result.current.updateTrustLevel(TrustLevel.TRUST_PARENT);
121
256
  });
122
- expect(result.current.needsRestart).toBe(false);
123
- expect(mockSetValue).toHaveBeenCalledWith('/test/dir', TrustLevel.TRUST_PARENT);
257
+ expect(emitFeedbackSpy).toHaveBeenCalledWith('error', 'Failed to save trust settings. Your changes may not persist.');
124
258
  expect(mockOnExit).toHaveBeenCalled();
125
259
  });
126
- it('should commit the pending trust level change', () => {
127
- const mockSetValue = vi.fn();
260
+ it('should emit feedback when setValue throws in commitTrustLevelChange', () => {
261
+ const mockSetValue = vi.fn().mockImplementation(() => {
262
+ throw new Error('test error');
263
+ });
128
264
  mockedLoadTrustedFolders.mockReturnValue({
129
265
  user: { config: {} },
130
266
  setValue: mockSetValue,
@@ -132,51 +268,17 @@ describe('usePermissionsModifyTrust', () => {
132
268
  mockedIsWorkspaceTrusted
133
269
  .mockReturnValueOnce({ isTrusted: false, source: 'file' })
134
270
  .mockReturnValueOnce({ isTrusted: true, source: 'file' });
135
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
271
+ const emitFeedbackSpy = vi.spyOn(coreEvents, 'emitFeedback');
272
+ const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem, mockedCwd()));
136
273
  act(() => {
137
274
  result.current.updateTrustLevel(TrustLevel.TRUST_FOLDER);
138
275
  });
139
- expect(result.current.needsRestart).toBe(true);
140
- act(() => {
141
- result.current.commitTrustLevelChange();
142
- });
143
- expect(mockSetValue).toHaveBeenCalledWith('/test/dir', TrustLevel.TRUST_FOLDER);
144
- });
145
- it('should add warning when setting DO_NOT_TRUST but still trusted by parent', () => {
146
- mockedLoadTrustedFolders.mockReturnValue({
147
- user: { config: {} },
148
- setValue: vi.fn(),
149
- });
150
- mockedIsWorkspaceTrusted.mockReturnValue({
151
- isTrusted: true,
152
- source: 'file',
153
- });
154
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
155
- act(() => {
156
- result.current.updateTrustLevel(TrustLevel.DO_NOT_TRUST);
157
- });
158
- expect(mockAddItem).toHaveBeenCalledWith({
159
- type: 'warning',
160
- text: 'Note: This folder is still trusted because a parent folder is trusted.',
161
- }, expect.any(Number));
162
- });
163
- it('should add warning when setting DO_NOT_TRUST but still trusted by IDE', () => {
164
- mockedLoadTrustedFolders.mockReturnValue({
165
- user: { config: {} },
166
- setValue: vi.fn(),
167
- });
168
- mockedIsWorkspaceTrusted.mockReturnValue({
169
- isTrusted: true,
170
- source: 'ide',
171
- });
172
- const { result } = renderHook(() => usePermissionsModifyTrust(mockOnExit, mockAddItem));
173
276
  act(() => {
174
- result.current.updateTrustLevel(TrustLevel.DO_NOT_TRUST);
277
+ const success = result.current.commitTrustLevelChange();
278
+ expect(success).toBe(false);
175
279
  });
176
- expect(mockAddItem).toHaveBeenCalledWith({
177
- type: 'warning',
178
- text: 'Note: This folder is still trusted because the connected IDE workspace is trusted.',
179
- }, expect.any(Number));
280
+ expect(emitFeedbackSpy).toHaveBeenCalledWith('error', 'Failed to save trust settings. Your changes may not persist.');
281
+ expect(result.current.needsRestart).toBe(false);
180
282
  });
181
283
  });
182
284
  //# sourceMappingURL=usePermissionsModifyTrust.test.js.map