@google/gemini-cli-core 0.1.12-nightly.250714.b018e2d3 → 0.1.13-nightly.250726.fb751c54
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.
- package/README.md +30 -3
- package/dist/google-gemini-cli-core-0.1.13.tgz +0 -0
- package/dist/src/code_assist/codeAssist.js +2 -2
- package/dist/src/code_assist/codeAssist.js.map +1 -1
- package/dist/src/code_assist/oauth2.js +46 -5
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +107 -10
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/code_assist/server.d.ts +4 -6
- package/dist/src/code_assist/server.js +4 -69
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +10 -2
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/setup.d.ts +6 -1
- package/dist/src/code_assist/setup.js +4 -1
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/code_assist/setup.test.js +4 -1
- package/dist/src/code_assist/setup.test.js.map +1 -1
- package/dist/src/code_assist/types.d.ts +2 -2
- package/dist/src/config/config.d.ts +55 -11
- package/dist/src/config/config.js +85 -33
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +29 -26
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/flashFallback.test.js +1 -1
- package/dist/src/config/flashFallback.test.js.map +1 -1
- package/dist/src/core/client.d.ts +8 -3
- package/dist/src/core/client.js +62 -3
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +145 -37
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +3 -2
- package/dist/src/core/contentGenerator.js +5 -4
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +12 -5
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.js +14 -1
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +84 -2
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +4 -3
- package/dist/src/core/geminiChat.js +8 -11
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiRequest.js +2 -37
- package/dist/src/core/geminiRequest.js.map +1 -1
- package/dist/src/core/logger.js +6 -0
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/logger.test.js +1 -1
- package/dist/src/core/logger.test.js.map +1 -1
- package/dist/src/core/modelCheck.d.ts +1 -1
- package/dist/src/core/modelCheck.js +10 -3
- package/dist/src/core/modelCheck.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +8 -5
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts.js +42 -18
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +121 -4
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/turn.d.ts +12 -3
- package/dist/src/core/turn.js +10 -0
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +129 -0
- package/dist/src/core/turn.test.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +28 -0
- package/dist/src/ide/ide-client.js +79 -0
- package/dist/src/ide/ide-client.js.map +1 -0
- package/dist/src/ide/ideContext.d.ts +174 -0
- package/dist/src/ide/ideContext.js +101 -0
- package/dist/src/ide/ideContext.js.map +1 -0
- package/dist/src/ide/ideContext.test.js +111 -0
- package/dist/src/ide/ideContext.test.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +13 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.d.ts +23 -0
- package/dist/src/mcp/google-auth-provider.js +63 -0
- package/dist/src/mcp/google-auth-provider.js.map +1 -0
- package/dist/src/mcp/google-auth-provider.test.d.ts +6 -0
- package/dist/src/mcp/google-auth-provider.test.js +54 -0
- package/dist/src/mcp/google-auth-provider.test.js.map +1 -0
- package/dist/src/mcp/oauth-provider.d.ts +142 -0
- package/dist/src/mcp/oauth-provider.js +446 -0
- package/dist/src/mcp/oauth-provider.js.map +1 -0
- package/dist/src/mcp/oauth-provider.test.d.ts +6 -0
- package/dist/src/mcp/oauth-provider.test.js +520 -0
- package/dist/src/mcp/oauth-provider.test.js.map +1 -0
- package/dist/src/mcp/oauth-token-storage.d.ts +81 -0
- package/dist/src/mcp/oauth-token-storage.js +149 -0
- package/dist/src/mcp/oauth-token-storage.js.map +1 -0
- package/dist/src/mcp/oauth-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/oauth-token-storage.test.js +205 -0
- package/dist/src/mcp/oauth-token-storage.test.js.map +1 -0
- package/dist/src/mcp/oauth-utils.d.ts +109 -0
- package/dist/src/mcp/oauth-utils.js +183 -0
- package/dist/src/mcp/oauth-utils.js.map +1 -0
- package/dist/src/mcp/oauth-utils.test.d.ts +6 -0
- package/dist/src/mcp/oauth-utils.test.js +144 -0
- package/dist/src/mcp/oauth-utils.test.js.map +1 -0
- package/dist/src/prompts/mcp-prompts.d.ts +8 -0
- package/dist/src/prompts/mcp-prompts.js +13 -0
- package/dist/src/prompts/mcp-prompts.js.map +1 -0
- package/dist/src/prompts/prompt-registry.d.ts +26 -0
- package/dist/src/prompts/prompt-registry.js +47 -0
- package/dist/src/prompts/prompt-registry.js.map +1 -0
- package/dist/src/services/fileDiscoveryService.test.js +101 -60
- package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
- package/dist/src/services/gitService.js +1 -5
- package/dist/src/services/gitService.js.map +1 -1
- package/dist/src/services/gitService.test.js +68 -92
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/loopDetectionService.d.ts +94 -0
- package/dist/src/services/loopDetectionService.js +318 -0
- package/dist/src/services/loopDetectionService.js.map +1 -0
- package/dist/src/services/loopDetectionService.test.d.ts +6 -0
- package/dist/src/services/loopDetectionService.test.js +266 -0
- package/dist/src/services/loopDetectionService.test.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +5 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +69 -4
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +4 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +9 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/constants.d.ts +1 -0
- package/dist/src/telemetry/constants.js +1 -0
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/file-exporters.d.ts +28 -0
- package/dist/src/telemetry/file-exporters.js +62 -0
- package/dist/src/telemetry/file-exporters.js.map +1 -0
- package/dist/src/telemetry/integration.test.circular.d.ts +6 -0
- package/dist/src/telemetry/integration.test.circular.js +53 -0
- package/dist/src/telemetry/integration.test.circular.js.map +1 -0
- package/dist/src/telemetry/loggers.d.ts +3 -1
- package/dist/src/telemetry/loggers.js +34 -2
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.circular.d.ts +6 -0
- package/dist/src/telemetry/loggers.test.circular.js +100 -0
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -0
- package/dist/src/telemetry/sdk.js +17 -6
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +19 -1
- package/dist/src/telemetry/types.js +28 -0
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +1 -0
- package/dist/src/telemetry/uiTelemetry.js +7 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +92 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/tools/edit.d.ts +7 -12
- package/dist/src/tools/edit.js +34 -32
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +12 -0
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.d.ts +1 -14
- package/dist/src/tools/glob.js +13 -36
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +11 -7
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.d.ts +3 -6
- package/dist/src/tools/grep.js +12 -18
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +9 -6
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/ls.d.ts +6 -14
- package/dist/src/tools/ls.js +47 -40
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +83 -1
- package/dist/src/tools/mcp-client.js +613 -148
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +211 -616
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts +11 -5
- package/dist/src/tools/mcp-tool.js +34 -10
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +74 -24
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.js +2 -2
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +51 -62
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/read-file.d.ts +3 -3
- package/dist/src/tools/read-file.js +10 -10
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +100 -70
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.d.ts +6 -10
- package/dist/src/tools/read-many-files.js +74 -43
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +7 -3
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +2 -23
- package/dist/src/tools/shell.js +58 -138
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +86 -311
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/tool-registry.d.ts +13 -2
- package/dist/src/tools/tool-registry.js +57 -10
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +112 -41
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +37 -2
- package/dist/src/tools/tools.js +25 -2
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/web-fetch.js +7 -2
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +1 -0
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/web-search.js +2 -2
- package/dist/src/tools/web-search.js.map +1 -1
- package/dist/src/tools/write-file.d.ts +0 -8
- package/dist/src/tools/write-file.js +14 -23
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.d.ts +2 -0
- package/dist/src/utils/bfsFileSearch.js +4 -1
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.test.js +108 -105
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
- package/dist/src/utils/browser.d.ts +13 -0
- package/dist/src/utils/browser.js +49 -0
- package/dist/src/utils/browser.js.map +1 -0
- package/dist/src/utils/editCorrector.js +4 -4
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editCorrector.test.js +1 -1
- package/dist/src/utils/editor.js +16 -10
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +128 -28
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/errorReporting.d.ts +1 -1
- package/dist/src/utils/errorReporting.js +2 -2
- package/dist/src/utils/errorReporting.js.map +1 -1
- package/dist/src/utils/errorReporting.test.js +44 -38
- package/dist/src/utils/errorReporting.test.js.map +1 -1
- package/dist/src/utils/errors.js +4 -4
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/fileUtils.d.ts +4 -4
- package/dist/src/utils/fileUtils.js +33 -17
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +37 -37
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/getFolderStructure.d.ts +3 -2
- package/dist/src/utils/getFolderStructure.js +27 -28
- package/dist/src/utils/getFolderStructure.js.map +1 -1
- package/dist/src/utils/getFolderStructure.test.js +169 -187
- package/dist/src/utils/getFolderStructure.test.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.js +4 -7
- package/dist/src/utils/gitIgnoreParser.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.test.js +70 -61
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.d.ts +2 -1
- package/dist/src/utils/memoryDiscovery.js +11 -5
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +160 -371
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/partUtils.d.ts +14 -0
- package/dist/src/utils/partUtils.js +65 -0
- package/dist/src/utils/partUtils.js.map +1 -0
- package/dist/src/utils/partUtils.test.d.ts +6 -0
- package/dist/src/utils/partUtils.test.js +130 -0
- package/dist/src/utils/partUtils.test.js.map +1 -0
- package/dist/src/utils/paths.d.ts +11 -0
- package/dist/src/utils/paths.js +17 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/quotaErrorDetection.js +2 -11
- package/dist/src/utils/quotaErrorDetection.js.map +1 -1
- package/dist/src/utils/retry.d.ts +6 -0
- package/dist/src/utils/retry.js +2 -2
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/safeJsonStringify.d.ts +13 -0
- package/dist/src/utils/safeJsonStringify.js +25 -0
- package/dist/src/utils/safeJsonStringify.js.map +1 -0
- package/dist/src/utils/safeJsonStringify.test.d.ts +6 -0
- package/dist/src/utils/safeJsonStringify.test.js +61 -0
- package/dist/src/utils/safeJsonStringify.test.js.map +1 -0
- package/dist/src/utils/schemaValidator.d.ts +1 -1
- package/dist/src/utils/schemaValidator.js +6 -3
- package/dist/src/utils/schemaValidator.js.map +1 -1
- package/dist/src/utils/shell-utils.d.ts +44 -0
- package/dist/src/utils/shell-utils.js +243 -0
- package/dist/src/utils/shell-utils.js.map +1 -0
- package/dist/src/utils/shell-utils.test.d.ts +6 -0
- package/dist/src/utils/shell-utils.test.js +450 -0
- package/dist/src/utils/shell-utils.test.js.map +1 -0
- package/dist/src/utils/summarizer.d.ts +1 -1
- package/dist/src/utils/summarizer.js +11 -39
- package/dist/src/utils/summarizer.js.map +1 -1
- package/dist/src/utils/summarizer.test.js +1 -1
- package/dist/src/utils/systemEncoding.d.ts +40 -0
- package/dist/src/utils/systemEncoding.js +149 -0
- package/dist/src/utils/systemEncoding.js.map +1 -0
- package/dist/src/utils/systemEncoding.test.d.ts +6 -0
- package/dist/src/utils/systemEncoding.test.js +368 -0
- package/dist/src/utils/systemEncoding.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/dist/google-gemini-cli-core-0.1.12.tgz +0 -0
- package/dist/src/core/geminiRequest.test.js +0 -72
- package/dist/src/core/geminiRequest.test.js.map +0 -1
- /package/dist/src/{core/geminiRequest.test.d.ts → ide/ideContext.test.d.ts} +0 -0
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { vi, describe, it, expect, beforeEach, afterEach
|
|
6
|
+
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
7
|
import { modifyWithEditor, isModifiableTool, } from './modifiable-tool.js';
|
|
8
8
|
import fs from 'fs';
|
|
9
|
+
import fsp from 'fs/promises';
|
|
9
10
|
import os from 'os';
|
|
10
11
|
import * as path from 'path';
|
|
11
12
|
// Mock dependencies
|
|
@@ -17,25 +18,23 @@ vi.mock('../utils/editor.js', () => ({
|
|
|
17
18
|
vi.mock('diff', () => ({
|
|
18
19
|
createPatch: mockCreatePatch,
|
|
19
20
|
}));
|
|
20
|
-
vi.mock('fs');
|
|
21
|
-
vi.mock('os');
|
|
22
21
|
describe('modifyWithEditor', () => {
|
|
23
|
-
let
|
|
22
|
+
let testProjectDir;
|
|
24
23
|
let mockModifyContext;
|
|
25
24
|
let mockParams;
|
|
26
25
|
let currentContent;
|
|
27
26
|
let proposedContent;
|
|
28
27
|
let modifiedContent;
|
|
29
28
|
let abortSignal;
|
|
30
|
-
beforeEach(() => {
|
|
29
|
+
beforeEach(async () => {
|
|
31
30
|
vi.resetAllMocks();
|
|
32
|
-
|
|
31
|
+
testProjectDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'modifiable-tool-test-'));
|
|
33
32
|
abortSignal = new AbortController().signal;
|
|
34
33
|
currentContent = 'original content\nline 2\nline 3';
|
|
35
34
|
proposedContent = 'modified content\nline 2\nline 3';
|
|
36
35
|
modifiedContent = 'user modified content\nline 2\nline 3\nnew line';
|
|
37
36
|
mockParams = {
|
|
38
|
-
filePath: path.join(
|
|
37
|
+
filePath: path.join(testProjectDir, 'test.txt'),
|
|
39
38
|
someOtherParam: 'value',
|
|
40
39
|
};
|
|
41
40
|
mockModifyContext = {
|
|
@@ -50,22 +49,16 @@ describe('modifyWithEditor', () => {
|
|
|
50
49
|
oldContent,
|
|
51
50
|
})),
|
|
52
51
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
fs.mkdirSync.mockImplementation(() => undefined);
|
|
56
|
-
fs.writeFileSync.mockImplementation(() => { });
|
|
57
|
-
fs.unlinkSync.mockImplementation(() => { });
|
|
58
|
-
fs.readFileSync.mockImplementation((filePath) => {
|
|
59
|
-
if (filePath.includes('-new-')) {
|
|
60
|
-
return modifiedContent;
|
|
61
|
-
}
|
|
62
|
-
return currentContent;
|
|
52
|
+
mockOpenDiff.mockImplementation(async (_oldPath, newPath) => {
|
|
53
|
+
await fsp.writeFile(newPath, modifiedContent, 'utf8');
|
|
63
54
|
});
|
|
64
55
|
mockCreatePatch.mockReturnValue('mock diff content');
|
|
65
|
-
mockOpenDiff.mockResolvedValue(undefined);
|
|
66
56
|
});
|
|
67
|
-
afterEach(() => {
|
|
57
|
+
afterEach(async () => {
|
|
68
58
|
vi.restoreAllMocks();
|
|
59
|
+
await fsp.rm(testProjectDir, { recursive: true, force: true });
|
|
60
|
+
const diffDir = path.join(os.tmpdir(), 'gemini-cli-tool-modify-diffs');
|
|
61
|
+
await fsp.rm(diffDir, { recursive: true, force: true });
|
|
69
62
|
});
|
|
70
63
|
describe('successful modification', () => {
|
|
71
64
|
it('should successfully modify content with VSCode editor', async () => {
|
|
@@ -73,20 +66,16 @@ describe('modifyWithEditor', () => {
|
|
|
73
66
|
expect(mockModifyContext.getCurrentContent).toHaveBeenCalledWith(mockParams);
|
|
74
67
|
expect(mockModifyContext.getProposedContent).toHaveBeenCalledWith(mockParams);
|
|
75
68
|
expect(mockModifyContext.getFilePath).toHaveBeenCalledWith(mockParams);
|
|
76
|
-
expect(
|
|
77
|
-
|
|
78
|
-
expect(fs.writeFileSync).toHaveBeenNthCalledWith(2, expect.stringContaining(path.join(tempDir, 'gemini-cli-tool-modify-diffs')), proposedContent, 'utf8');
|
|
79
|
-
expect(mockOpenDiff).toHaveBeenCalledWith(expect.stringContaining('-old-'), expect.stringContaining('-new-'), 'vscode');
|
|
80
|
-
expect(fs.readFileSync).toHaveBeenCalledWith(expect.stringContaining('-old-'), 'utf8');
|
|
81
|
-
expect(fs.readFileSync).toHaveBeenCalledWith(expect.stringContaining('-new-'), 'utf8');
|
|
69
|
+
expect(mockOpenDiff).toHaveBeenCalledOnce();
|
|
70
|
+
const [oldFilePath, newFilePath] = mockOpenDiff.mock.calls[0];
|
|
82
71
|
expect(mockModifyContext.createUpdatedParams).toHaveBeenCalledWith(currentContent, modifiedContent, mockParams);
|
|
83
72
|
expect(mockCreatePatch).toHaveBeenCalledWith(path.basename(mockParams.filePath), currentContent, modifiedContent, 'Current', 'Proposed', expect.objectContaining({
|
|
84
73
|
context: 3,
|
|
85
74
|
ignoreWhitespace: true,
|
|
86
75
|
}));
|
|
87
|
-
|
|
88
|
-
expect(
|
|
89
|
-
expect(
|
|
76
|
+
// Check that temp files are deleted.
|
|
77
|
+
await expect(fsp.access(oldFilePath)).rejects.toThrow();
|
|
78
|
+
await expect(fsp.access(newFilePath)).rejects.toThrow();
|
|
90
79
|
expect(result).toEqual({
|
|
91
80
|
updatedParams: {
|
|
92
81
|
...mockParams,
|
|
@@ -97,24 +86,25 @@ describe('modifyWithEditor', () => {
|
|
|
97
86
|
});
|
|
98
87
|
});
|
|
99
88
|
it('should create temp directory if it does not exist', async () => {
|
|
100
|
-
|
|
89
|
+
const diffDir = path.join(os.tmpdir(), 'gemini-cli-tool-modify-diffs');
|
|
90
|
+
await fsp.rm(diffDir, { recursive: true, force: true }).catch(() => { });
|
|
101
91
|
await modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal);
|
|
102
|
-
|
|
92
|
+
const stats = await fsp.stat(diffDir);
|
|
93
|
+
expect(stats.isDirectory()).toBe(true);
|
|
103
94
|
});
|
|
104
95
|
it('should not create temp directory if it already exists', async () => {
|
|
105
|
-
|
|
96
|
+
const diffDir = path.join(os.tmpdir(), 'gemini-cli-tool-modify-diffs');
|
|
97
|
+
await fsp.mkdir(diffDir, { recursive: true });
|
|
98
|
+
const mkdirSpy = vi.spyOn(fs, 'mkdirSync');
|
|
106
99
|
await modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal);
|
|
107
|
-
expect(
|
|
100
|
+
expect(mkdirSpy).not.toHaveBeenCalled();
|
|
101
|
+
mkdirSpy.mockRestore();
|
|
108
102
|
});
|
|
109
103
|
});
|
|
110
104
|
it('should handle missing old temp file gracefully', async () => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
error.code = 'ENOENT';
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
return modifiedContent;
|
|
105
|
+
mockOpenDiff.mockImplementation(async (oldPath, newPath) => {
|
|
106
|
+
await fsp.writeFile(newPath, modifiedContent, 'utf8');
|
|
107
|
+
await fsp.unlink(oldPath);
|
|
118
108
|
});
|
|
119
109
|
const result = await modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal);
|
|
120
110
|
expect(mockCreatePatch).toHaveBeenCalledWith(path.basename(mockParams.filePath), '', modifiedContent, 'Current', 'Proposed', expect.objectContaining({
|
|
@@ -125,13 +115,8 @@ describe('modifyWithEditor', () => {
|
|
|
125
115
|
expect(result.updatedDiff).toBe('mock diff content');
|
|
126
116
|
});
|
|
127
117
|
it('should handle missing new temp file gracefully', async () => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const error = new Error('ENOENT: no such file or directory');
|
|
131
|
-
error.code = 'ENOENT';
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
return currentContent;
|
|
118
|
+
mockOpenDiff.mockImplementation(async (_oldPath, newPath) => {
|
|
119
|
+
await fsp.unlink(newPath);
|
|
135
120
|
});
|
|
136
121
|
const result = await modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal);
|
|
137
122
|
expect(mockCreatePatch).toHaveBeenCalledWith(path.basename(mockParams.filePath), currentContent, '', 'Current', 'Proposed', expect.objectContaining({
|
|
@@ -144,14 +129,20 @@ describe('modifyWithEditor', () => {
|
|
|
144
129
|
it('should clean up temp files even if editor fails', async () => {
|
|
145
130
|
const editorError = new Error('Editor failed to open');
|
|
146
131
|
mockOpenDiff.mockRejectedValue(editorError);
|
|
132
|
+
const writeSpy = vi.spyOn(fs, 'writeFileSync');
|
|
147
133
|
await expect(modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal)).rejects.toThrow('Editor failed to open');
|
|
148
|
-
expect(
|
|
134
|
+
expect(writeSpy).toHaveBeenCalledTimes(2);
|
|
135
|
+
const oldFilePath = writeSpy.mock.calls[0][0];
|
|
136
|
+
const newFilePath = writeSpy.mock.calls[1][0];
|
|
137
|
+
await expect(fsp.access(oldFilePath)).rejects.toThrow();
|
|
138
|
+
await expect(fsp.access(newFilePath)).rejects.toThrow();
|
|
139
|
+
writeSpy.mockRestore();
|
|
149
140
|
});
|
|
150
141
|
it('should handle temp file cleanup errors gracefully', async () => {
|
|
151
142
|
const consoleErrorSpy = vi
|
|
152
143
|
.spyOn(console, 'error')
|
|
153
144
|
.mockImplementation(() => { });
|
|
154
|
-
fs
|
|
145
|
+
vi.spyOn(fs, 'unlinkSync').mockImplementation(() => {
|
|
155
146
|
throw new Error('Failed to delete file');
|
|
156
147
|
});
|
|
157
148
|
await modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal);
|
|
@@ -160,30 +151,28 @@ describe('modifyWithEditor', () => {
|
|
|
160
151
|
consoleErrorSpy.mockRestore();
|
|
161
152
|
});
|
|
162
153
|
it('should create temp files with correct naming with extension', async () => {
|
|
163
|
-
const testFilePath = path.join(
|
|
154
|
+
const testFilePath = path.join(testProjectDir, 'subfolder', 'test-file.txt');
|
|
164
155
|
mockModifyContext.getFilePath = vi.fn().mockReturnValue(testFilePath);
|
|
165
156
|
await modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const oldFilePath = writeFileCalls[0][0];
|
|
169
|
-
const newFilePath = writeFileCalls[1][0];
|
|
157
|
+
expect(mockOpenDiff).toHaveBeenCalledOnce();
|
|
158
|
+
const [oldFilePath, newFilePath] = mockOpenDiff.mock.calls[0];
|
|
170
159
|
expect(oldFilePath).toMatch(/gemini-cli-modify-test-file-old-\d+\.txt$/);
|
|
171
160
|
expect(newFilePath).toMatch(/gemini-cli-modify-test-file-new-\d+\.txt$/);
|
|
172
|
-
|
|
173
|
-
expect(
|
|
161
|
+
const diffDir = path.join(os.tmpdir(), 'gemini-cli-tool-modify-diffs');
|
|
162
|
+
expect(path.dirname(oldFilePath)).toBe(diffDir);
|
|
163
|
+
expect(path.dirname(newFilePath)).toBe(diffDir);
|
|
174
164
|
});
|
|
175
165
|
it('should create temp files with correct naming without extension', async () => {
|
|
176
|
-
const testFilePath = path.join(
|
|
166
|
+
const testFilePath = path.join(testProjectDir, 'subfolder', 'test-file');
|
|
177
167
|
mockModifyContext.getFilePath = vi.fn().mockReturnValue(testFilePath);
|
|
178
168
|
await modifyWithEditor(mockParams, mockModifyContext, 'vscode', abortSignal);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const oldFilePath = writeFileCalls[0][0];
|
|
182
|
-
const newFilePath = writeFileCalls[1][0];
|
|
169
|
+
expect(mockOpenDiff).toHaveBeenCalledOnce();
|
|
170
|
+
const [oldFilePath, newFilePath] = mockOpenDiff.mock.calls[0];
|
|
183
171
|
expect(oldFilePath).toMatch(/gemini-cli-modify-test-file-old-\d+$/);
|
|
184
172
|
expect(newFilePath).toMatch(/gemini-cli-modify-test-file-new-\d+$/);
|
|
185
|
-
|
|
186
|
-
expect(
|
|
173
|
+
const diffDir = path.join(os.tmpdir(), 'gemini-cli-tool-modify-diffs');
|
|
174
|
+
expect(path.dirname(oldFilePath)).toBe(diffDir);
|
|
175
|
+
expect(path.dirname(newFilePath)).toBe(diffDir);
|
|
187
176
|
});
|
|
188
177
|
});
|
|
189
178
|
describe('isModifiableTool', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modifiable-tool.test.js","sourceRoot":"","sources":["../../../src/tools/modifiable-tool.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"modifiable-tool.test.js","sourceRoot":"","sources":["../../../src/tools/modifiable-tool.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { BaseTool, ToolResult } from './tools.js';
|
|
6
|
+
import { BaseTool, ToolLocation, ToolResult } from './tools.js';
|
|
7
7
|
import { Config } from '../config/config.js';
|
|
8
8
|
/**
|
|
9
9
|
* Parameters for the ReadFile tool
|
|
@@ -26,11 +26,11 @@ export interface ReadFileToolParams {
|
|
|
26
26
|
* Implementation of the ReadFile tool logic
|
|
27
27
|
*/
|
|
28
28
|
export declare class ReadFileTool extends BaseTool<ReadFileToolParams, ToolResult> {
|
|
29
|
-
private rootDirectory;
|
|
30
29
|
private config;
|
|
31
30
|
static readonly Name: string;
|
|
32
|
-
constructor(
|
|
31
|
+
constructor(config: Config);
|
|
33
32
|
validateToolParams(params: ReadFileToolParams): string | null;
|
|
34
33
|
getDescription(params: ReadFileToolParams): string;
|
|
34
|
+
toolLocations(params: ReadFileToolParams): ToolLocation[];
|
|
35
35
|
execute(params: ReadFileToolParams, _signal: AbortSignal): Promise<ToolResult>;
|
|
36
36
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { SchemaValidator } from '../utils/schemaValidator.js';
|
|
8
8
|
import { makeRelative, shortenPath } from '../utils/paths.js';
|
|
9
|
-
import { BaseTool } from './tools.js';
|
|
9
|
+
import { BaseTool, Icon } from './tools.js';
|
|
10
10
|
import { Type } from '@google/genai';
|
|
11
11
|
import { isWithinRoot, processSingleFileContent, getSpecificMimeType, } from '../utils/fileUtils.js';
|
|
12
12
|
import { recordFileOperationMetric, FileOperation, } from '../telemetry/metrics.js';
|
|
@@ -14,11 +14,10 @@ import { recordFileOperationMetric, FileOperation, } from '../telemetry/metrics.
|
|
|
14
14
|
* Implementation of the ReadFile tool logic
|
|
15
15
|
*/
|
|
16
16
|
export class ReadFileTool extends BaseTool {
|
|
17
|
-
rootDirectory;
|
|
18
17
|
config;
|
|
19
18
|
static Name = 'read_file';
|
|
20
|
-
constructor(
|
|
21
|
-
super(ReadFileTool.Name, 'ReadFile', 'Reads and returns the content of a specified file from the local filesystem. Handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges.', {
|
|
19
|
+
constructor(config) {
|
|
20
|
+
super(ReadFileTool.Name, 'ReadFile', 'Reads and returns the content of a specified file from the local filesystem. Handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges.', Icon.FileSearch, {
|
|
22
21
|
properties: {
|
|
23
22
|
absolute_path: {
|
|
24
23
|
description: "The absolute path to the file to read (e.g., '/home/user/project/file.txt'). Relative paths are not supported. You must provide an absolute path.",
|
|
@@ -36,9 +35,7 @@ export class ReadFileTool extends BaseTool {
|
|
|
36
35
|
required: ['absolute_path'],
|
|
37
36
|
type: Type.OBJECT,
|
|
38
37
|
});
|
|
39
|
-
this.rootDirectory = rootDirectory;
|
|
40
38
|
this.config = config;
|
|
41
|
-
this.rootDirectory = path.resolve(rootDirectory);
|
|
42
39
|
}
|
|
43
40
|
validateToolParams(params) {
|
|
44
41
|
const errors = SchemaValidator.validate(this.schema.parameters, params);
|
|
@@ -49,8 +46,8 @@ export class ReadFileTool extends BaseTool {
|
|
|
49
46
|
if (!path.isAbsolute(filePath)) {
|
|
50
47
|
return `File path must be absolute, but was relative: ${filePath}. You must provide an absolute path.`;
|
|
51
48
|
}
|
|
52
|
-
if (!isWithinRoot(filePath, this.
|
|
53
|
-
return `File path must be within the root directory (${this.
|
|
49
|
+
if (!isWithinRoot(filePath, this.config.getTargetDir())) {
|
|
50
|
+
return `File path must be within the root directory (${this.config.getTargetDir()}): ${filePath}`;
|
|
54
51
|
}
|
|
55
52
|
if (params.offset !== undefined && params.offset < 0) {
|
|
56
53
|
return 'Offset must be a non-negative number';
|
|
@@ -70,9 +67,12 @@ export class ReadFileTool extends BaseTool {
|
|
|
70
67
|
params.absolute_path.trim() === '') {
|
|
71
68
|
return `Path unavailable`;
|
|
72
69
|
}
|
|
73
|
-
const relativePath = makeRelative(params.absolute_path, this.
|
|
70
|
+
const relativePath = makeRelative(params.absolute_path, this.config.getTargetDir());
|
|
74
71
|
return shortenPath(relativePath);
|
|
75
72
|
}
|
|
73
|
+
toolLocations(params) {
|
|
74
|
+
return [{ path: params.absolute_path, line: params.offset }];
|
|
75
|
+
}
|
|
76
76
|
async execute(params, _signal) {
|
|
77
77
|
const validationError = this.validateToolParams(params);
|
|
78
78
|
if (validationError) {
|
|
@@ -81,7 +81,7 @@ export class ReadFileTool extends BaseTool {
|
|
|
81
81
|
returnDisplay: validationError,
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
const result = await processSingleFileContent(params.absolute_path, this.
|
|
84
|
+
const result = await processSingleFileContent(params.absolute_path, this.config.getTargetDir(), params.offset, params.limit);
|
|
85
85
|
if (result.error) {
|
|
86
86
|
return {
|
|
87
87
|
llmContent: result.error, // The detailed error for LLM
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../../src/tools/read-file.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../../src/tools/read-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,41 +3,30 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
7
|
import { ReadFileTool } from './read-file.js';
|
|
8
|
-
import * as fileUtils from '../utils/fileUtils.js';
|
|
9
8
|
import path from 'path';
|
|
10
9
|
import os from 'os';
|
|
11
|
-
import fs from 'fs';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import fsp from 'fs/promises';
|
|
12
12
|
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
|
13
|
-
// Mock fileUtils.processSingleFileContent
|
|
14
|
-
vi.mock('../utils/fileUtils', async () => {
|
|
15
|
-
const actualFileUtils = await vi.importActual('../utils/fileUtils');
|
|
16
|
-
return {
|
|
17
|
-
...actualFileUtils, // Spread actual implementations
|
|
18
|
-
processSingleFileContent: vi.fn(), // Mock specific function
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
const mockProcessSingleFileContent = fileUtils.processSingleFileContent;
|
|
22
13
|
describe('ReadFileTool', () => {
|
|
23
14
|
let tempRootDir;
|
|
24
15
|
let tool;
|
|
25
16
|
const abortSignal = new AbortController().signal;
|
|
26
|
-
beforeEach(() => {
|
|
17
|
+
beforeEach(async () => {
|
|
27
18
|
// Create a unique temporary root directory for each test run
|
|
28
|
-
tempRootDir =
|
|
29
|
-
fs.writeFileSync(path.join(tempRootDir, '.geminiignore'), ['foo.*'].join('\n'));
|
|
30
|
-
const fileService = new FileDiscoveryService(tempRootDir);
|
|
19
|
+
tempRootDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'read-file-tool-root-'));
|
|
31
20
|
const mockConfigInstance = {
|
|
32
|
-
getFileService: () =>
|
|
21
|
+
getFileService: () => new FileDiscoveryService(tempRootDir),
|
|
22
|
+
getTargetDir: () => tempRootDir,
|
|
33
23
|
};
|
|
34
|
-
tool = new ReadFileTool(
|
|
35
|
-
mockProcessSingleFileContent.mockReset();
|
|
24
|
+
tool = new ReadFileTool(mockConfigInstance);
|
|
36
25
|
});
|
|
37
|
-
afterEach(() => {
|
|
26
|
+
afterEach(async () => {
|
|
38
27
|
// Clean up the temporary root directory
|
|
39
28
|
if (fs.existsSync(tempRootDir)) {
|
|
40
|
-
|
|
29
|
+
await fsp.rm(tempRootDir, { recursive: true, force: true });
|
|
41
30
|
}
|
|
42
31
|
});
|
|
43
32
|
describe('validateToolParams', () => {
|
|
@@ -95,9 +84,7 @@ describe('ReadFileTool', () => {
|
|
|
95
84
|
it('should return a shortened, relative path', () => {
|
|
96
85
|
const filePath = path.join(tempRootDir, 'sub', 'dir', 'file.txt');
|
|
97
86
|
const params = { absolute_path: filePath };
|
|
98
|
-
|
|
99
|
-
// The relative path would be sub/dir/file.txt
|
|
100
|
-
expect(tool.getDescription(params)).toBe('sub/dir/file.txt');
|
|
87
|
+
expect(tool.getDescription(params)).toBe(path.join('sub', 'dir', 'file.txt'));
|
|
101
88
|
});
|
|
102
89
|
it('should return . if path is the root directory', () => {
|
|
103
90
|
const params = { absolute_path: tempRootDir };
|
|
@@ -106,74 +93,117 @@ describe('ReadFileTool', () => {
|
|
|
106
93
|
});
|
|
107
94
|
describe('execute', () => {
|
|
108
95
|
it('should return validation error if params are invalid', async () => {
|
|
109
|
-
const params = {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
expect(
|
|
96
|
+
const params = {
|
|
97
|
+
absolute_path: 'relative/path.txt',
|
|
98
|
+
};
|
|
99
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
100
|
+
llmContent: 'Error: Invalid parameters provided. Reason: File path must be absolute, but was relative: relative/path.txt. You must provide an absolute path.',
|
|
101
|
+
returnDisplay: 'File path must be absolute, but was relative: relative/path.txt. You must provide an absolute path.',
|
|
102
|
+
});
|
|
113
103
|
});
|
|
114
|
-
it('should return error
|
|
115
|
-
const filePath = path.join(tempRootDir, '
|
|
104
|
+
it('should return error if file does not exist', async () => {
|
|
105
|
+
const filePath = path.join(tempRootDir, 'nonexistent.txt');
|
|
116
106
|
const params = { absolute_path: filePath };
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
returnDisplay: `Error reading file ${filePath}: ${errorMessage}`,
|
|
121
|
-
error: errorMessage,
|
|
107
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
108
|
+
llmContent: `File not found: ${filePath}`,
|
|
109
|
+
returnDisplay: 'File not found.',
|
|
122
110
|
});
|
|
123
|
-
const result = await tool.execute(params, abortSignal);
|
|
124
|
-
expect(mockProcessSingleFileContent).toHaveBeenCalledWith(filePath, tempRootDir, undefined, undefined);
|
|
125
|
-
expect(result.llmContent).toContain(errorMessage);
|
|
126
|
-
expect(result.returnDisplay).toContain(errorMessage);
|
|
127
111
|
});
|
|
128
112
|
it('should return success result for a text file', async () => {
|
|
129
113
|
const filePath = path.join(tempRootDir, 'textfile.txt');
|
|
130
114
|
const fileContent = 'This is a test file.';
|
|
115
|
+
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
|
131
116
|
const params = { absolute_path: filePath };
|
|
132
|
-
|
|
117
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
133
118
|
llmContent: fileContent,
|
|
134
|
-
returnDisplay:
|
|
119
|
+
returnDisplay: '',
|
|
135
120
|
});
|
|
136
|
-
const result = await tool.execute(params, abortSignal);
|
|
137
|
-
expect(mockProcessSingleFileContent).toHaveBeenCalledWith(filePath, tempRootDir, undefined, undefined);
|
|
138
|
-
expect(result.llmContent).toBe(fileContent);
|
|
139
|
-
expect(result.returnDisplay).toBe(`Read text file: ${path.basename(filePath)}`);
|
|
140
121
|
});
|
|
141
122
|
it('should return success result for an image file', async () => {
|
|
123
|
+
// A minimal 1x1 transparent PNG file.
|
|
124
|
+
const pngContent = Buffer.from([
|
|
125
|
+
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
|
|
126
|
+
1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 10, 73, 68, 65,
|
|
127
|
+
84, 120, 156, 99, 0, 1, 0, 0, 5, 0, 1, 13, 10, 45, 180, 0, 0, 0, 0, 73,
|
|
128
|
+
69, 78, 68, 174, 66, 96, 130,
|
|
129
|
+
]);
|
|
142
130
|
const filePath = path.join(tempRootDir, 'image.png');
|
|
143
|
-
|
|
144
|
-
inlineData: { mimeType: 'image/png', data: 'base64...' },
|
|
145
|
-
};
|
|
131
|
+
await fsp.writeFile(filePath, pngContent);
|
|
146
132
|
const params = { absolute_path: filePath };
|
|
147
|
-
|
|
148
|
-
llmContent:
|
|
149
|
-
|
|
133
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
134
|
+
llmContent: {
|
|
135
|
+
inlineData: {
|
|
136
|
+
mimeType: 'image/png',
|
|
137
|
+
data: pngContent.toString('base64'),
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
returnDisplay: `Read image file: image.png`,
|
|
150
141
|
});
|
|
151
|
-
const result = await tool.execute(params, abortSignal);
|
|
152
|
-
expect(mockProcessSingleFileContent).toHaveBeenCalledWith(filePath, tempRootDir, undefined, undefined);
|
|
153
|
-
expect(result.llmContent).toEqual(imageData);
|
|
154
|
-
expect(result.returnDisplay).toBe(`Read image file: ${path.basename(filePath)}`);
|
|
155
142
|
});
|
|
156
|
-
it('should
|
|
143
|
+
it('should treat a non-image file with image extension as an image', async () => {
|
|
144
|
+
const filePath = path.join(tempRootDir, 'fake-image.png');
|
|
145
|
+
const fileContent = 'This is not a real png.';
|
|
146
|
+
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
|
147
|
+
const params = { absolute_path: filePath };
|
|
148
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
149
|
+
llmContent: {
|
|
150
|
+
inlineData: {
|
|
151
|
+
mimeType: 'image/png',
|
|
152
|
+
data: Buffer.from(fileContent).toString('base64'),
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
returnDisplay: `Read image file: fake-image.png`,
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
it('should pass offset and limit to read a slice of a text file', async () => {
|
|
157
159
|
const filePath = path.join(tempRootDir, 'paginated.txt');
|
|
160
|
+
const fileContent = Array.from({ length: 20 }, (_, i) => `Line ${i + 1}`).join('\n');
|
|
161
|
+
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
|
158
162
|
const params = {
|
|
159
163
|
absolute_path: filePath,
|
|
160
|
-
offset:
|
|
161
|
-
limit:
|
|
164
|
+
offset: 5, // Start from line 6
|
|
165
|
+
limit: 3,
|
|
162
166
|
};
|
|
163
|
-
|
|
164
|
-
llmContent:
|
|
165
|
-
|
|
167
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
168
|
+
llmContent: [
|
|
169
|
+
'[File content truncated: showing lines 6-8 of 20 total lines. Use offset/limit parameters to view more.]',
|
|
170
|
+
'Line 6',
|
|
171
|
+
'Line 7',
|
|
172
|
+
'Line 8',
|
|
173
|
+
].join('\n'),
|
|
174
|
+
returnDisplay: '(truncated)',
|
|
166
175
|
});
|
|
167
|
-
await tool.execute(params, abortSignal);
|
|
168
|
-
expect(mockProcessSingleFileContent).toHaveBeenCalledWith(filePath, tempRootDir, 10, 5);
|
|
169
176
|
});
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
describe('with .geminiignore', () => {
|
|
178
|
+
beforeEach(async () => {
|
|
179
|
+
await fsp.writeFile(path.join(tempRootDir, '.geminiignore'), ['foo.*', 'ignored/'].join('\n'));
|
|
180
|
+
});
|
|
181
|
+
it('should return error if path is ignored by a .geminiignore pattern', async () => {
|
|
182
|
+
const ignoredFilePath = path.join(tempRootDir, 'foo.bar');
|
|
183
|
+
await fsp.writeFile(ignoredFilePath, 'content', 'utf-8');
|
|
184
|
+
const params = {
|
|
185
|
+
absolute_path: ignoredFilePath,
|
|
186
|
+
};
|
|
187
|
+
const expectedError = `File path '${ignoredFilePath}' is ignored by .geminiignore pattern(s).`;
|
|
188
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
189
|
+
llmContent: `Error: Invalid parameters provided. Reason: ${expectedError}`,
|
|
190
|
+
returnDisplay: expectedError,
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
it('should return error if path is in an ignored directory', async () => {
|
|
194
|
+
const ignoredDirPath = path.join(tempRootDir, 'ignored');
|
|
195
|
+
await fsp.mkdir(ignoredDirPath);
|
|
196
|
+
const filePath = path.join(ignoredDirPath, 'somefile.txt');
|
|
197
|
+
await fsp.writeFile(filePath, 'content', 'utf-8');
|
|
198
|
+
const params = {
|
|
199
|
+
absolute_path: filePath,
|
|
200
|
+
};
|
|
201
|
+
const expectedError = `File path '${filePath}' is ignored by .geminiignore pattern(s).`;
|
|
202
|
+
expect(await tool.execute(params, abortSignal)).toEqual({
|
|
203
|
+
llmContent: `Error: Invalid parameters provided. Reason: ${expectedError}`,
|
|
204
|
+
returnDisplay: expectedError,
|
|
205
|
+
});
|
|
206
|
+
});
|
|
177
207
|
});
|
|
178
208
|
});
|
|
179
209
|
});
|