diff --git a/packages/a2a-server/src/config/settings.ts b/packages/a2a-server/src/config/settings.ts
index b3c44cc177..11b28fba85 100644
--- a/packages/a2a-server/src/config/settings.ts
+++ b/packages/a2a-server/src/config/settings.ts
@@ -95,10 +95,10 @@ export function loadSettings(workspaceDir: string): Settings {
// Load workspace settings
try {
if (fs.existsSync(workspaceSettingsPath)) {
- const projectContent = fs.readFileSync(workspaceSettingsPath, 'utf-8');
+ const workspaceContent = fs.readFileSync(workspaceSettingsPath, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const parsedWorkspaceSettings = JSON.parse(
- stripJsonComments(projectContent),
+ stripJsonComments(workspaceContent),
) as Settings;
workspaceSettings = resolveEnvVarsInObject(parsedWorkspaceSettings);
}
diff --git a/packages/cli/src/config/auth.test.ts b/packages/cli/src/config/auth.test.ts
index b0492527b8..02516ef052 100644
--- a/packages/cli/src/config/auth.test.ts
+++ b/packages/cli/src/config/auth.test.ts
@@ -60,7 +60,7 @@ describe('validateAuthMethod', () => {
'should return null for USE_VERTEX_AI if GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION are set',
authType: AuthType.USE_VERTEX_AI,
envs: {
- GOOGLE_CLOUD_PROJECT: 'test-project',
+ GOOGLE_CLOUD_PROJECT: 'test-workspace',
GOOGLE_CLOUD_LOCATION: 'test-location',
},
expected: null,
diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts
index 919ad86c51..511cc0b7df 100644
--- a/packages/cli/src/config/config.test.ts
+++ b/packages/cli/src/config/config.test.ts
@@ -49,7 +49,7 @@ vi.mock('fs', async (importOriginal) => {
const pathMod = await import('node:path');
const mockHome = pathMod.resolve(pathMod.sep, 'mock', 'home', 'user');
const MOCK_CWD1 = process.cwd();
- const MOCK_CWD2 = pathMod.resolve(pathMod.sep, 'home', 'user', 'project');
+ const MOCK_CWD2 = pathMod.resolve(pathMod.sep, 'home', 'user', 'workspace');
const mockPaths = new Set([
MOCK_CWD1,
@@ -1896,7 +1896,7 @@ describe('loadCliConfig with includeDirectories', () => {
);
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
vi.spyOn(process, 'cwd').mockReturnValue(
- path.resolve(path.sep, 'home', 'user', 'project'),
+ path.resolve(path.sep, 'home', 'user', 'workspace'),
);
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([]);
});
@@ -1906,7 +1906,7 @@ describe('loadCliConfig with includeDirectories', () => {
});
it.skip('should combine and resolve paths from settings and CLI arguments', async () => {
- const mockCwd = path.resolve(path.sep, 'home', 'user', 'project');
+ const mockCwd = path.resolve(path.sep, 'home', 'user', 'workspace');
process.argv = [
'node',
diff --git a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-generate-a-consent-string-with-all-fields.snap.svg b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-generate-a-consent-string-with-all-fields.snap.svg
index d42af4490c..64c5c35241 100644
--- a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-generate-a-consent-string-with-all-fields.snap.svg
+++ b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-generate-a-consent-string-with-all-fields.snap.svg
@@ -10,9 +10,9 @@
* server2 (remote): https://remote.com
This extension will append info to your gemini.md context using my-context.md
This extension will exclude the following core tools: tool1,tool2
- The extension you are about to install may have been created by a third-party developer and sourced
- from a public repository. Google does not vet, endorse, or guarantee the functionality or security
- of extensions. Please carefully inspect any extension and its source code before installing to
- understand the permissions it requires and the actions it may perform.
+ The extension you are about to install may have been created by a third-party developer and sourced
+ from a public repository. Google does not vet, endorse, or guarantee the functionality or security
+ of extensions. Please carefully inspect any extension and its source code before installing to
+ understand the permissions it requires and the actions it may perform.
\ No newline at end of file
diff --git a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-include-warning-when-hooks-are-present.snap.svg b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-include-warning-when-hooks-are-present.snap.svg
index 9f4866dbdd..298ee141e2 100644
--- a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-include-warning-when-hooks-are-present.snap.svg
+++ b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-include-warning-when-hooks-are-present.snap.svg
@@ -6,9 +6,9 @@
Installing extension "test-ext".
⚠️ This extension contains Hooks which can automatically execute commands.
- The extension you are about to install may have been created by a third-party developer and sourced
- from a public repository. Google does not vet, endorse, or guarantee the functionality or security
- of extensions. Please carefully inspect any extension and its source code before installing to
- understand the permissions it requires and the actions it may perform.
+ The extension you are about to install may have been created by a third-party developer and sourced
+ from a public repository. Google does not vet, endorse, or guarantee the functionality or security
+ of extensions. Please carefully inspect any extension and its source code before installing to
+ understand the permissions it requires and the actions it may perform.
\ No newline at end of file
diff --git a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-request-consent-if-skills-change.snap.svg b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-request-consent-if-skills-change.snap.svg
index 6f5879df4c..69ff91035c 100644
--- a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-request-consent-if-skills-change.snap.svg
+++ b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-request-consent-if-skills-change.snap.svg
@@ -16,13 +16,13 @@
(Source: /mock/temp/dir/skill1/SKILL.md) (2 items in directory)
* skill2: desc2
(Source: /mock/temp/dir/skill2/SKILL.md) (1 items in directory)
- The extension you are about to install may have been created by a third-party developer and sourced
- from a public repository. Google does not vet, endorse, or guarantee the functionality or security
- of extensions. Please carefully inspect any extension and its source code before installing to
- understand the permissions it requires and the actions it may perform.
- Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
- prompt. This can change how the agent interprets your requests and interacts with your environment.
- Review the skill definitions at the location(s) provided below to ensure they meet your security
- standards.
+ The extension you are about to install may have been created by a third-party developer and sourced
+ from a public repository. Google does not vet, endorse, or guarantee the functionality or security
+ of extensions. Please carefully inspect any extension and its source code before installing to
+ understand the permissions it requires and the actions it may perform.
+ Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
+ prompt. This can change how the agent interprets your requests and interacts with your environment.
+ Review the skill definitions at the location(s) provided below to ensure they meet your security
+ standards.
\ No newline at end of file
diff --git a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-show-a-warning-if-the-skill-directory-cannot-be-read.snap.svg b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-show-a-warning-if-the-skill-directory-cannot-be-read.snap.svg
index 3fff32664a..247f1ab7b8 100644
--- a/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-show-a-warning-if-the-skill-directory-cannot-be-read.snap.svg
+++ b/packages/cli/src/config/extensions/__snapshots__/consent-consent-maybeRequestConsentOrFail-consent-string-generation-should-show-a-warning-if-the-skill-directory-cannot-be-read.snap.svg
@@ -8,15 +8,14 @@
Agent Skills:
This extension will install the following agent skills:
* locked-skill: A skill in a locked dir
- (Source: /mock/temp/dir/locked/SKILL.md)
- ⚠️ (Could not count items in directory)
- The extension you are about to install may have been created by a third-party developer and sourced
- from a public repository. Google does not vet, endorse, or guarantee the functionality or security
- of extensions. Please carefully inspect any extension and its source code before installing to
- understand the permissions it requires and the actions it may perform.
- Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
- prompt. This can change how the agent interprets your requests and interacts with your environment.
- Review the skill definitions at the location(s) provided below to ensure they meet your security
- standards.
+ (Source: /mock/temp/dir/locked/SKILL.md) ⚠️ (Could not count items in directory)
+ The extension you are about to install may have been created by a third-party developer and sourced
+ from a public repository. Google does not vet, endorse, or guarantee the functionality or security
+ of extensions. Please carefully inspect any extension and its source code before installing to
+ understand the permissions it requires and the actions it may perform.
+ Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
+ prompt. This can change how the agent interprets your requests and interacts with your environment.
+ Review the skill definitions at the location(s) provided below to ensure they meet your security
+ standards.
\ No newline at end of file
diff --git a/packages/cli/src/config/extensions/__snapshots__/consent-consent-skillsConsentString-should-generate-a-consent-string-for-skills.snap.svg b/packages/cli/src/config/extensions/__snapshots__/consent-consent-skillsConsentString-should-generate-a-consent-string-for-skills.snap.svg
index c52724836e..362c247ad2 100644
--- a/packages/cli/src/config/extensions/__snapshots__/consent-consent-skillsConsentString-should-generate-a-consent-string-for-skills.snap.svg
+++ b/packages/cli/src/config/extensions/__snapshots__/consent-consent-skillsConsentString-should-generate-a-consent-string-for-skills.snap.svg
@@ -9,9 +9,9 @@
* skill1: desc1
(Source: /mock/temp/dir/skill1/SKILL.md) (1 items in directory)
Install Destination: /mock/target/dir
- Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
- prompt. This can change how the agent interprets your requests and interacts with your environment.
- Review the skill definitions at the location(s) provided below to ensure they meet your security
- standards.
+ Agent skills inject specialized instructions and domain-specific knowledge into the agent's system
+ prompt. This can change how the agent interprets your requests and interacts with your environment.
+ Review the skill definitions at the location(s) provided below to ensure they meet your security
+ standards.
\ No newline at end of file
diff --git a/packages/cli/src/config/extensions/consent.test.ts b/packages/cli/src/config/extensions/consent.test.ts
index 04e6cae69f..a8e059f651 100644
--- a/packages/cli/src/config/extensions/consent.test.ts
+++ b/packages/cli/src/config/extensions/consent.test.ts
@@ -7,6 +7,7 @@
import React from 'react';
import { Text } from 'ink';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+import '../../test-utils/customMatchers.js';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import * as os from 'node:os';
diff --git a/packages/cli/src/config/policy-engine.integration.test.ts b/packages/cli/src/config/policy-engine.integration.test.ts
index 02515815d0..edaf757743 100644
--- a/packages/cli/src/config/policy-engine.integration.test.ts
+++ b/packages/cli/src/config/policy-engine.integration.test.ts
@@ -400,8 +400,8 @@ describe('Policy Engine Integration Tests', () => {
'/home/user/.gemini/tmp/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2/session-1/plans/my-plan.md',
'/home/user/.gemini/tmp/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2/session-1/plans/feature_auth.md',
'/home/user/.gemini/tmp/new-temp_dir_123/session-1/plans/plan.md', // new style of temp directory
- 'C:\\Users\\user\\.gemini\\tmp\\project-id\\session-id\\plans\\plan.md',
- 'D:\\gemini-cli\\.gemini\\tmp\\project-id\\session-1\\plans\\plan.md', // no session ID
+ 'C:\\Users\\user\\.gemini\\tmp\\workspace-id\\session-id\\plans\\plan.md',
+ 'D:\\gemini-cli\\.gemini\\tmp\\workspace-id\\session-1\\plans\\plan.md', // no session ID
];
for (const file_path of validPaths) {
@@ -425,7 +425,7 @@ describe('Policy Engine Integration Tests', () => {
const engine = new PolicyEngine(config);
const invalidPaths = [
- '/project/src/file.ts', // Workspace
+ '/workspace/src/file.ts', // Workspace
'/home/user/.gemini/tmp/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2/plans/script.js', // Wrong extension
'/home/user/.gemini/tmp/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2/plans/../../../etc/passwd.md', // Path traversal (Unix)
'C:\\Users\\user\\.gemini\\tmp\\id\\session\\plans\\..\\..\\..\\Windows\\System32\\config\\SAM', // Path traversal (Windows)
diff --git a/packages/cli/src/config/settings.test.ts b/packages/cli/src/config/settings.test.ts
index 8fd0bd81b0..36bd22bb0e 100644
--- a/packages/cli/src/config/settings.test.ts
+++ b/packages/cli/src/config/settings.test.ts
@@ -1554,7 +1554,7 @@ describe('Settings Loading and Merging', () => {
process.env = originalEnv;
});
- it('should exclude DEBUG and DEBUG_MODE from project .env files by default', () => {
+ it('should exclude DEBUG and DEBUG_MODE from workspace .env files by default', () => {
// Create a workspace settings file with excludedProjectEnvVars
const workspaceSettingsContent = {
general: {},
@@ -1573,18 +1573,18 @@ describe('Settings Loading and Merging', () => {
},
);
- // Mock findEnvFile to return a project .env file
+ // Mock findEnvFile to return a workspace .env file
const originalFindEnvFile = (
loadSettings as unknown as { findEnvFile: () => string }
).findEnvFile;
(loadSettings as unknown as { findEnvFile: () => string }).findEnvFile =
- () => '/mock/project/.env';
+ () => '/mock/workspace/.env';
// Mock fs.readFileSync for .env file content
const originalReadFileSync = fs.readFileSync;
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
- if (p === '/mock/project/.env') {
+ if (p === '/mock/workspace/.env') {
return 'DEBUG=true\nDEBUG_MODE=1\nGEMINI_API_KEY=test-key';
}
if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
@@ -2940,7 +2940,9 @@ MALICIOUS_VAR=allowed-because-trusted
it('should sanitize value in sanitizeEnvVar helper', () => {
expect(sanitizeEnvVar('$(calc)')).toBe('calc');
expect(sanitizeEnvVar('`rm -rf /`')).toBe('rm-rf/');
- expect(sanitizeEnvVar('normal-project-123')).toBe('normal-project-123');
+ expect(sanitizeEnvVar('normal-workspace-123')).toBe(
+ 'normal-workspace-123',
+ );
expect(sanitizeEnvVar('us-central1')).toBe('us-central1');
});
});
@@ -2974,7 +2976,7 @@ MALICIOUS_VAR=allowed-because-trusted
});
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(
- 'GOOGLE_CLOUD_PROJECT=attacker-project;inject',
+ 'GOOGLE_CLOUD_PROJECT=attacker-workspace;inject',
);
loadEnvironment(
@@ -2983,7 +2985,7 @@ MALICIOUS_VAR=allowed-because-trusted
);
expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe(
- 'attacker-projectinject',
+ 'attacker-workspaceinject',
);
});
});
diff --git a/packages/cli/src/config/trustedFolders.test.ts b/packages/cli/src/config/trustedFolders.test.ts
index 714d703241..fcdc30f3db 100644
--- a/packages/cli/src/config/trustedFolders.test.ts
+++ b/packages/cli/src/config/trustedFolders.test.ts
@@ -272,30 +272,30 @@ describe('Trusted Folders', () => {
};
it('should return true for a directly trusted folder', () => {
- const config = { '/projectA': TrustLevel.TRUST_FOLDER };
+ const config = { '/workspaceA': TrustLevel.TRUST_FOLDER };
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
- expect(isWorkspaceTrusted(mockSettings, '/projectA')).toEqual({
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceA')).toEqual({
isTrusted: true,
source: 'file',
});
});
it('should return true for a child of a trusted folder', () => {
- const config = { '/projectA': TrustLevel.TRUST_FOLDER };
+ const config = { '/workspaceA': TrustLevel.TRUST_FOLDER };
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
- expect(isWorkspaceTrusted(mockSettings, '/projectA/src')).toEqual({
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceA/src')).toEqual({
isTrusted: true,
source: 'file',
});
});
it('should return true for a child of a trusted parent folder', () => {
- const config = { '/projectB/somefile.txt': TrustLevel.TRUST_PARENT };
+ const config = { '/workspaceB/somefile.txt': TrustLevel.TRUST_PARENT };
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
- expect(isWorkspaceTrusted(mockSettings, '/projectB')).toEqual({
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceB')).toEqual({
isTrusted: true,
source: 'file',
});
@@ -329,20 +329,22 @@ describe('Trusted Folders', () => {
it('should prioritize specific distrust over parent trust', () => {
const config = {
- '/projectA': TrustLevel.TRUST_FOLDER,
- '/projectA/untrusted': TrustLevel.DO_NOT_TRUST,
+ '/workspaceA': TrustLevel.TRUST_FOLDER,
+ '/workspaceA/untrusted': TrustLevel.DO_NOT_TRUST,
};
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
- expect(isWorkspaceTrusted(mockSettings, '/projectA/untrusted')).toEqual({
- isTrusted: false,
- source: 'file',
- });
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceA/untrusted')).toEqual(
+ {
+ isTrusted: false,
+ source: 'file',
+ },
+ );
});
it('should use workspaceDir instead of process.cwd() when provided', () => {
const config = {
- '/projectA': TrustLevel.TRUST_FOLDER,
+ '/workspaceA': TrustLevel.TRUST_FOLDER,
'/untrusted': TrustLevel.DO_NOT_TRUST,
};
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
@@ -350,18 +352,18 @@ describe('Trusted Folders', () => {
vi.spyOn(process, 'cwd').mockImplementation(() => '/untrusted');
// process.cwd() is untrusted, but workspaceDir is trusted
- expect(isWorkspaceTrusted(mockSettings, '/projectA')).toEqual({
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceA')).toEqual({
isTrusted: true,
source: 'file',
});
});
it('should handle path normalization', () => {
- const config = { '/home/user/projectA': TrustLevel.TRUST_FOLDER };
+ const config = { '/home/user/workspaceA': TrustLevel.TRUST_FOLDER };
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
expect(
- isWorkspaceTrusted(mockSettings, '/home/user/../user/projectA'),
+ isWorkspaceTrusted(mockSettings, '/home/user/../user/workspaceA'),
).toEqual({
isTrusted: true,
source: 'file',
@@ -369,13 +371,13 @@ describe('Trusted Folders', () => {
});
it('should prioritize IDE override over file config', () => {
- const config = { '/projectA': TrustLevel.DO_NOT_TRUST };
+ const config = { '/workspaceA': TrustLevel.DO_NOT_TRUST };
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
ideContextStore.set({ workspaceState: { isTrusted: true } });
try {
- expect(isWorkspaceTrusted(mockSettings, '/projectA')).toEqual({
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceA')).toEqual({
isTrusted: true,
source: 'ide',
});
@@ -385,13 +387,13 @@ describe('Trusted Folders', () => {
});
it('should return false when IDE override is false', () => {
- const config = { '/projectA': TrustLevel.TRUST_FOLDER };
+ const config = { '/workspaceA': TrustLevel.TRUST_FOLDER };
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
ideContextStore.set({ workspaceState: { isTrusted: false } });
try {
- expect(isWorkspaceTrusted(mockSettings, '/projectA')).toEqual({
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceA')).toEqual({
isTrusted: false,
source: 'ide',
});
@@ -442,10 +444,10 @@ describe('Trusted Folders', () => {
const geminiCore = await import('@google/gemini-cli-core');
vi.spyOn(geminiCore, 'isHeadlessMode').mockReturnValue(false);
- const config = { '/projectA': TrustLevel.DO_NOT_TRUST };
+ const config = { '/workspaceA': TrustLevel.DO_NOT_TRUST };
fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8');
- expect(isWorkspaceTrusted(mockSettings, '/projectA').isTrusted).toBe(
+ expect(isWorkspaceTrusted(mockSettings, '/workspaceA').isTrusted).toBe(
false,
);
});
diff --git a/packages/cli/src/services/CommandService.test.ts b/packages/cli/src/services/CommandService.test.ts
index ea906a3da6..bd9fa5ff79 100644
--- a/packages/cli/src/services/CommandService.test.ts
+++ b/packages/cli/src/services/CommandService.test.ts
@@ -237,17 +237,17 @@ describe('CommandService', () => {
expect(syncExtension?.extensionName).toBe('git-helper');
});
- it('should handle user/project command override correctly', async () => {
+ it('should handle user/workspace command override correctly', async () => {
const builtinCommand = createMockCommand('help', CommandKind.BUILT_IN);
const userCommand = createMockCommand('help', CommandKind.FILE);
- const projectCommand = createMockCommand('deploy', CommandKind.FILE);
+ const workspaceCommand = createMockCommand('deploy', CommandKind.FILE);
const userDeployCommand = createMockCommand('deploy', CommandKind.FILE);
const mockLoader1 = new MockCommandLoader([builtinCommand]);
const mockLoader2 = new MockCommandLoader([
userCommand,
userDeployCommand,
- projectCommand,
+ workspaceCommand,
]);
const service = await CommandService.create(
diff --git a/packages/cli/src/services/FileCommandLoader.test.ts b/packages/cli/src/services/FileCommandLoader.test.ts
index 077b8c45fe..450c6bf282 100644
--- a/packages/cli/src/services/FileCommandLoader.test.ts
+++ b/packages/cli/src/services/FileCommandLoader.test.ts
@@ -230,7 +230,7 @@ describe('FileCommandLoader', () => {
},
});
const mockConfig = {
- getProjectRoot: vi.fn(() => '/path/to/project'),
+ getWorkspaceRoot: vi.fn(() => '/path/to/workspace'),
getExtensions: vi.fn(() => []),
getFolderTrust: vi.fn(() => false),
isTrustedFolder: vi.fn(() => false),
@@ -260,22 +260,22 @@ describe('FileCommandLoader', () => {
expect(command.name).toBe('git:commit');
});
- it('returns both user and project commands in order', async () => {
+ it('returns both user and workspace commands in order', async () => {
const userCommandsDir = Storage.getUserCommandsDir();
- const projectCommandsDir = new Storage(
+ const workspaceCommandsDir = new Storage(
process.cwd(),
).getProjectCommandsDir();
mock({
[userCommandsDir]: {
'test.toml': 'prompt = "User prompt"',
},
- [projectCommandsDir]: {
+ [workspaceCommandsDir]: {
'test.toml': 'prompt = "Project prompt"',
},
});
const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
+ getWorkspaceRoot: vi.fn(() => process.cwd()),
getExtensions: vi.fn(() => []),
getFolderTrust: vi.fn(() => false),
isTrustedFolder: vi.fn(() => false),
@@ -299,7 +299,7 @@ describe('FileCommandLoader', () => {
} else {
assert.fail('Incorrect action type for user command');
}
- const projectResult = await commands[1].action?.(
+ const workspaceResult = await commands[1].action?.(
createMockCommandContext({
invocation: {
raw: '/test',
@@ -309,10 +309,10 @@ describe('FileCommandLoader', () => {
}),
'',
);
- if (projectResult?.type === 'submit_prompt') {
- expect(projectResult.content).toEqual([{ text: 'Project prompt' }]);
+ if (workspaceResult?.type === 'submit_prompt') {
+ expect(workspaceResult.content).toEqual([{ text: 'Project prompt' }]);
} else {
- assert.fail('Incorrect action type for project command');
+ assert.fail('Incorrect action type for workspace command');
}
});
@@ -532,7 +532,7 @@ describe('FileCommandLoader', () => {
describe('Extension Command Loading', () => {
it('loads commands from active extensions', async () => {
const userCommandsDir = Storage.getUserCommandsDir();
- const projectCommandsDir = new Storage(
+ const workspaceCommandsDir = new Storage(
process.cwd(),
).getProjectCommandsDir();
const extensionDir = path.join(
@@ -546,8 +546,8 @@ describe('FileCommandLoader', () => {
[userCommandsDir]: {
'user.toml': 'prompt = "User command"',
},
- [projectCommandsDir]: {
- 'project.toml': 'prompt = "Project command"',
+ [workspaceCommandsDir]: {
+ 'workspace.toml': 'prompt = "Project command"',
},
[extensionDir]: {
'gemini-extension.json': JSON.stringify({
@@ -561,7 +561,7 @@ describe('FileCommandLoader', () => {
});
const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
+ getWorkspaceRoot: vi.fn(() => process.cwd()),
getExtensions: vi.fn(() => [
{
name: 'test-ext',
@@ -578,7 +578,7 @@ describe('FileCommandLoader', () => {
expect(commands).toHaveLength(3);
const commandNames = commands.map((cmd) => cmd.name);
- expect(commandNames).toEqual(['user', 'project', 'ext']);
+ expect(commandNames).toEqual(['user', 'workspace', 'ext']);
const extCommand = commands.find((cmd) => cmd.name === 'ext');
expect(extCommand?.extensionName).toBe('test-ext');
@@ -587,7 +587,7 @@ describe('FileCommandLoader', () => {
it('extension commands have extensionName metadata for conflict resolution', async () => {
const userCommandsDir = Storage.getUserCommandsDir();
- const projectCommandsDir = new Storage(
+ const workspaceCommandsDir = new Storage(
process.cwd(),
).getProjectCommandsDir();
const extensionDir = path.join(
@@ -610,13 +610,13 @@ describe('FileCommandLoader', () => {
[userCommandsDir]: {
'deploy.toml': 'prompt = "User deploy command"',
},
- [projectCommandsDir]: {
+ [workspaceCommandsDir]: {
'deploy.toml': 'prompt = "Project deploy command"',
},
});
const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
+ getWorkspaceRoot: vi.fn(() => process.cwd()),
getExtensions: vi.fn(() => [
{
name: 'test-ext',
@@ -723,7 +723,7 @@ describe('FileCommandLoader', () => {
});
const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
+ getWorkspaceRoot: vi.fn(() => process.cwd()),
getExtensions: vi.fn(() => [
{
name: 'active-ext',
@@ -769,7 +769,7 @@ describe('FileCommandLoader', () => {
});
const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
+ getWorkspaceRoot: vi.fn(() => process.cwd()),
getExtensions: vi.fn(() => [
{
name: 'no-commands',
@@ -813,7 +813,7 @@ describe('FileCommandLoader', () => {
});
const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
+ getWorkspaceRoot: vi.fn(() => process.cwd()),
getExtensions: vi.fn(() => [
{ name: 'a', version: '1.0.0', isActive: true, path: extensionDir },
]),
@@ -874,7 +874,7 @@ describe('FileCommandLoader', () => {
});
const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
+ getWorkspaceRoot: vi.fn(() => process.cwd()),
getExtensions: vi.fn(() => [
{
name: 'my-test-ext',
@@ -1256,7 +1256,7 @@ describe('FileCommandLoader', () => {
describe('with folder trust enabled', () => {
it('loads multiple commands', async () => {
const mockConfig = {
- getProjectRoot: vi.fn(() => '/path/to/project'),
+ getWorkspaceRoot: vi.fn(() => '/path/to/workspace'),
getExtensions: vi.fn(() => []),
getFolderTrust: vi.fn(() => true),
isTrustedFolder: vi.fn(() => true),
@@ -1277,7 +1277,7 @@ describe('FileCommandLoader', () => {
it('does not load when folder is not trusted', async () => {
const mockConfig = {
- getProjectRoot: vi.fn(() => '/path/to/project'),
+ getWorkspaceRoot: vi.fn(() => '/path/to/workspace'),
getExtensions: vi.fn(() => []),
getFolderTrust: vi.fn(() => true),
isTrustedFolder: vi.fn(() => false),
@@ -1307,7 +1307,7 @@ describe('FileCommandLoader', () => {
.mockImplementation(() => {});
const mockConfig = {
- getProjectRoot: vi.fn(() => '/path/to/project'),
+ getWorkspaceRoot: vi.fn(() => '/path/to/workspace'),
getExtensions: vi.fn(() => []),
getFolderTrust: vi.fn(() => false),
isTrustedFolder: vi.fn(() => false),
diff --git a/packages/cli/src/test-utils/mockCommandContext.test.ts b/packages/cli/src/test-utils/mockCommandContext.test.ts
index 310bf74864..7c62fb256d 100644
--- a/packages/cli/src/test-utils/mockCommandContext.test.ts
+++ b/packages/cli/src/test-utils/mockCommandContext.test.ts
@@ -40,7 +40,7 @@ describe('createMockCommandContext', () => {
it('should apply deeply nested overrides correctly', () => {
// This is the most important test for factory's logic.
const mockConfig = {
- getProjectRoot: () => '/test/project',
+ getWorkspaceRoot: () => '/test/workspace',
getModel: () => 'gemini-pro',
};
@@ -54,7 +54,7 @@ describe('createMockCommandContext', () => {
expect(context.services.config).toBeDefined();
expect(context.services.config?.getModel()).toBe('gemini-pro');
- expect(context.services.config?.getProjectRoot()).toBe('/test/project');
+ expect(context.services.config?.getWorkspaceRoot()).toBe('/test/workspace');
// Verify a default property on the same nested object is still there
expect(context.services.logger).toBeDefined();
diff --git a/packages/cli/src/test-utils/mockConfig.ts b/packages/cli/src/test-utils/mockConfig.ts
index 8b7c7c520d..d05cfb7d08 100644
--- a/packages/cli/src/test-utils/mockConfig.ts
+++ b/packages/cli/src/test-utils/mockConfig.ts
@@ -21,7 +21,7 @@ export const createMockConfig = (overrides: Partial = {}): Config =>
isInitialized: vi.fn(() => true),
setTerminalBackground: vi.fn(),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/gemini-test'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/gemini-test'),
initialize: vi.fn().mockResolvedValue(undefined),
},
getDebugMode: vi.fn(() => false),
diff --git a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap
index 450da8362e..d95adcda95 100644
--- a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap
+++ b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap
@@ -2,14 +2,14 @@
exports[`App > Snapshots > renders default layout correctly 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -47,14 +47,14 @@ exports[`App > Snapshots > renders screen reader layout correctly 1`] = `
"Notifications
Footer
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -67,14 +67,14 @@ Composer
exports[`App > Snapshots > renders with dialogs visible 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
@@ -110,14 +110,14 @@ DialogManager
exports[`App > should render ToolConfirmationQueue along with Composer when tool is confirming and experiment is on 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
diff --git a/packages/cli/src/ui/commands/restoreCommand.test.ts b/packages/cli/src/ui/commands/restoreCommand.test.ts
index 2a5def5c42..5353ec3657 100644
--- a/packages/cli/src/ui/commands/restoreCommand.test.ts
+++ b/packages/cli/src/ui/commands/restoreCommand.test.ts
@@ -153,7 +153,7 @@ describe('restoreCommand', () => {
});
});
- it('should restore a tool call and project state', async () => {
+ it('should restore a tool call and workspace state', async () => {
const toolCallData = {
history: [{ type: 'user', text: 'do a thing', id: 123 }],
clientHistory: [{ role: 'user', parts: [{ text: 'do a thing' }] }],
@@ -181,7 +181,7 @@ describe('restoreCommand', () => {
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
{
type: 'info',
- text: 'Restored project to the state before the tool call.',
+ text: 'Restored workspace to the state before the tool call.',
},
expect.any(Number),
);
diff --git a/packages/cli/src/ui/components/FolderTrustDialog.test.tsx b/packages/cli/src/ui/components/FolderTrustDialog.test.tsx
index bbda51d8f0..f2a869c78a 100644
--- a/packages/cli/src/ui/components/FolderTrustDialog.test.tsx
+++ b/packages/cli/src/ui/components/FolderTrustDialog.test.tsx
@@ -297,7 +297,7 @@ describe('FolderTrustDialog', () => {
,
);
await waitUntilReady();
- expect(lastFrame()).toContain('Trust folder (project)');
+ expect(lastFrame()).toContain('Trust folder (workspace)');
unmount();
});
diff --git a/packages/cli/src/ui/components/SettingsDialog.test.tsx b/packages/cli/src/ui/components/SettingsDialog.test.tsx
index 3dd5374a18..80a822f9dd 100644
--- a/packages/cli/src/ui/components/SettingsDialog.test.tsx
+++ b/packages/cli/src/ui/components/SettingsDialog.test.tsx
@@ -23,7 +23,8 @@
import { render } from '../../test-utils/render.js';
import { waitFor } from '../../test-utils/async.js';
-import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import '../../../test-utils/customMatchers.js';
import { SettingsDialog } from './SettingsDialog.js';
import { LoadedSettings, SettingScope } from '../../config/settings.js';
import { createMockSettings } from '../../test-utils/settings.js';
diff --git a/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap
index 18e75b75e2..8fb49b8b71 100644
--- a/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap
@@ -2,14 +2,14 @@
exports[`AlternateBufferQuittingDisplay > renders with a tool awaiting confirmation > with_confirming_tool 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -25,14 +25,14 @@ Action Required (was prompted):
exports[`AlternateBufferQuittingDisplay > renders with active and pending tool messages > with_history_and_pending 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -52,14 +52,14 @@ Tips for getting started:
exports[`AlternateBufferQuittingDisplay > renders with empty history and no pending items > empty 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -71,14 +71,14 @@ Tips for getting started:
exports[`AlternateBufferQuittingDisplay > renders with history but no pending items > with_history_no_pending 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -98,14 +98,14 @@ Tips for getting started:
exports[`AlternateBufferQuittingDisplay > renders with pending items but no history > with_pending_no_history 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -117,14 +117,14 @@ Tips for getting started:
exports[`AlternateBufferQuittingDisplay > renders with user and gemini messages > with_user_gemini_messages 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -132,7 +132,7 @@ Tips for getting started:
3. Create GEMINI.md files to customize your interactions with Gemini.
4. /help for more information.
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > Hello Gemini
+ > Hello Gemini
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
✦ Hello User!
"
diff --git a/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap
index 324274fddd..59cf561759 100644
--- a/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap
@@ -2,14 +2,14 @@
exports[` > should not render the banner when no flags are set 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -21,14 +21,14 @@ Tips for getting started:
exports[` > should not render the default banner if shown count is 5 or more 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
@@ -40,14 +40,14 @@ Tips for getting started:
exports[` > should render the banner with default text 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ This is the default banner │
@@ -62,14 +62,14 @@ Tips for getting started:
exports[` > should render the banner with warning text 1`] = `
"
- ███ █████████
+ ███ █████████
░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
+ ░░░███ ███ ░░░
+ ░░░███░███
███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
+ ███░ ░░███ ░░███
+ ███░ ░░█████████
+░░░ ░░░░░░░░░
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ There are capacity issues │
diff --git a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
index 88a1b0486f..6a9bf5aeac 100644
--- a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
@@ -2,16 +2,16 @@
exports[`InputPrompt > History Navigation and Completion Suppression > should not render suggestions during history navigation 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > second message
+ > second message
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and collapses long suggestion via Right/Left arrows > command-search-render-collapsed-match 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- (r:) Type your message or @path/to/file
+ (r:) Type your message or @path/to/file
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
- lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll →
+ lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll →
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
...
"
@@ -19,9 +19,9 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and c
exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and collapses long suggestion via Right/Left arrows > command-search-render-expanded-match 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- (r:) Type your message or @path/to/file
+ (r:) Type your message or @path/to/file
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
- lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll ←
+ lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll ←
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
llllllllllllllllllllllllllllllllllllllllllllllllll
"
@@ -29,7 +29,7 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and c
exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match window and expanded view (snapshots) > command-search-render-collapsed-match 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- (r:) commit
+ (r:) commit
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
git commit -m "feat: add search" in src/app
"
@@ -37,7 +37,7 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match
exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match window and expanded view (snapshots) > command-search-render-expanded-match 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- (r:) commit
+ (r:) commit
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
git commit -m "feat: add search" in src/app
"
@@ -45,63 +45,63 @@ exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match
exports[`InputPrompt > image path transformation snapshots > should snapshot collapsed image path 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > [Image ...reenshot2x.png]
+ > [Image ...reenshot2x.png]
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > image path transformation snapshots > should snapshot expanded image path when cursor is on it 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > @/path/to/screenshots/screenshot2x.png
+ > @/path/to/screenshots/screenshot2x.png
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > [Pasted Text: 10 lines]
+ > [Pasted Text: 10 lines]
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 2`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > [Pasted Text: 10 lines]
+ > [Pasted Text: 10 lines]
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 3`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > [Pasted Text: 10 lines]
+ > [Pasted Text: 10 lines]
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > snapshots > should not show inverted cursor when shell is focused 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > Type your message or @path/to/file
+ > Type your message or @path/to/file
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > snapshots > should render correctly in shell mode 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- ! Type your message or @path/to/file
+ ! Type your message or @path/to/file
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > snapshots > should render correctly in yolo mode 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- * Type your message or @path/to/file
+ * Type your message or @path/to/file
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`InputPrompt > snapshots > should render correctly when accepting edits 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > Type your message or @path/to/file
+ > Type your message or @path/to/file
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
diff --git a/packages/cli/src/ui/components/__snapshots__/SettingsDialog.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/SettingsDialog.test.tsx.snap
deleted file mode 100644
index be2dd8d9a2..0000000000
--- a/packages/cli/src/ui/components/__snapshots__/SettingsDialog.test.tsx.snap
+++ /dev/null
@@ -1,415 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`SettingsDialog > Initial Rendering > should render settings list with visual indicators 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode false │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update true │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'accessibility settings enabled' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode true* │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update true │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'all boolean settings disabled' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode false* │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update true* │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false* │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'default state' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode false │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update true │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'file filtering settings configured' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode false │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update true │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'focused on scope selector' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ Vim Mode false │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update true │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ > Apply To │
-│ ● 1. User Settings │
-│ 2. Workspace Settings │
-│ 3. System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'mixed boolean and number settings' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode false* │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update false* │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'tools and security settings' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode false │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update true │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging false │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`SettingsDialog > Snapshot Tests > should render 'various boolean settings enabled' correctly 1`] = `
-"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ │
-│ > Settings │
-│ │
-│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │
-│ │ Search to filter │ │
-│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
-│ │
-│ ▲ │
-│ ● Vim Mode true* │
-│ Enable Vim keybindings │
-│ │
-│ Default Approval Mode Default │
-│ The default approval mode for tool execution. 'default' prompts for approval, 'au… │
-│ │
-│ Enable Auto Update false* │
-│ Enable automatic updates. │
-│ │
-│ Enable Notifications false │
-│ Enable run-event notifications for action-required prompts and session completion. … │
-│ │
-│ Plan Directory undefined │
-│ The directory where planning artifacts are stored. If not specified, defaults t… │
-│ │
-│ Plan Model Routing true │
-│ Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pr… │
-│ │
-│ Max Chat Model Attempts 10 │
-│ Maximum number of attempts for requests to the main chat model. Cannot exceed 10. │
-│ │
-│ Debug Keystroke Logging true* │
-│ Enable debug logging of keystrokes to the console. │
-│ │
-│ ▼ │
-│ │
-│ Apply To │
-│ ● User Settings │
-│ Workspace Settings │
-│ System Settings │
-│ │
-│ (Use Enter to select, Ctrl+L to reset, Tab to change focus, Esc to close) │
-│ │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
diff --git a/packages/cli/src/ui/components/messages/RedirectionConfirmation.test.tsx b/packages/cli/src/ui/components/messages/RedirectionConfirmation.test.tsx
index 15763bdae7..488c322e36 100644
--- a/packages/cli/src/ui/components/messages/RedirectionConfirmation.test.tsx
+++ b/packages/cli/src/ui/components/messages/RedirectionConfirmation.test.tsx
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { describe, it, expect, beforeAll } from 'vitest';
+import { describe, it, expect, beforeAll, vi } from 'vitest';
import { ToolConfirmationMessage } from './ToolConfirmationMessage.js';
import type {
SerializableConfirmationDetails,
diff --git a/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap b/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap
index 679a5885d1..9488a20ba3 100644
--- a/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap
+++ b/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap
@@ -2,29 +2,29 @@
exports[`UserMessage > renders multiline user message 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > Line 1
- Line 2
+ > Line 1
+ Line 2
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`UserMessage > renders normal user message with correct prefix 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > Hello Gemini
+ > Hello Gemini
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`UserMessage > renders slash command message 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > /help
+ > /help
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
exports[`UserMessage > transforms image paths in user message 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > Check out this image: [Image my-image.png]
+ > Check out this image: [Image my-image.png]
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"
`;
diff --git a/packages/cli/src/ui/components/shared/ExpandableText.test.tsx b/packages/cli/src/ui/components/shared/ExpandableText.test.tsx
index 00c82a009d..429e35acd3 100644
--- a/packages/cli/src/ui/components/shared/ExpandableText.test.tsx
+++ b/packages/cli/src/ui/components/shared/ExpandableText.test.tsx
@@ -5,6 +5,7 @@
*/
import { describe, it, expect } from 'vitest';
+import '../../../../test-utils/customMatchers.js';
import { render } from '../../../test-utils/render.js';
import { ExpandableText, MAX_WIDTH } from './ExpandableText.js';
diff --git a/packages/cli/src/ui/components/shared/__snapshots__/EnumSelector.test.tsx.snap b/packages/cli/src/ui/components/shared/__snapshots__/EnumSelector.test.tsx.snap
index 203ceb61d6..8fd19b3868 100644
--- a/packages/cli/src/ui/components/shared/__snapshots__/EnumSelector.test.tsx.snap
+++ b/packages/cli/src/ui/components/shared/__snapshots__/EnumSelector.test.tsx.snap
@@ -11,7 +11,7 @@ exports[` > renders with numeric options and matches snapshot 1`
`;
exports[` > renders with single option and matches snapshot 1`] = `
-" Only Option
+" Only Option
"
`;
diff --git a/packages/cli/src/ui/components/shared/__snapshots__/ExpandableText.test.tsx.snap b/packages/cli/src/ui/components/shared/__snapshots__/ExpandableText.test.tsx.snap
deleted file mode 100644
index 8716c962ea..0000000000
--- a/packages/cli/src/ui/components/shared/__snapshots__/ExpandableText.test.tsx.snap
+++ /dev/null
@@ -1,27 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`ExpandableText > creates centered window around match when collapsed 1`] = `
-"...ry/long/path/that/keeps/going/cd_/very/long/path/that/keeps/going/search-here/and/then/some/more/
-components//and/then/some/more/components//and/..."
-`;
-
-exports[`ExpandableText > highlights matched substring when expanded (text only visible) 1`] = `"run: git commit -m "feat: add search""`;
-
-exports[`ExpandableText > renders plain label when no match (short label) 1`] = `"simple command"`;
-
-exports[`ExpandableText > respects custom maxWidth 1`] = `"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz..."`;
-
-exports[`ExpandableText > shows full long label when expanded and no match 1`] = `
-"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
-`;
-
-exports[`ExpandableText > truncates long label when collapsed and no match 1`] = `
-"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..."
-`;
-
-exports[`ExpandableText > truncates match itself when match is very long 1`] = `
-"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..."
-`;
diff --git a/packages/cli/src/ui/components/shared/__snapshots__/HalfLinePaddedBox.test.tsx.snap b/packages/cli/src/ui/components/shared/__snapshots__/HalfLinePaddedBox.test.tsx.snap
index dbb9af2991..5dcbfda73d 100644
--- a/packages/cli/src/ui/components/shared/__snapshots__/HalfLinePaddedBox.test.tsx.snap
+++ b/packages/cli/src/ui/components/shared/__snapshots__/HalfLinePaddedBox.test.tsx.snap
@@ -2,7 +2,7 @@
exports[` > renders iTerm2-specific blocks when iTerm2 is detected 1`] = `
"▄▄▄▄▄▄▄▄▄▄
-Content
+Content
▀▀▀▀▀▀▀▀▀▀
"
`;
@@ -19,7 +19,7 @@ exports[` > renders nothing when useBackgroundColor is fals
exports[` > renders standard background and blocks when not iTerm2 1`] = `
"▀▀▀▀▀▀▀▀▀▀
-Content
+Content
▄▄▄▄▄▄▄▄▄▄
"
`;
diff --git a/packages/cli/src/ui/components/shared/__snapshots__/SearchableList.test.tsx.snap b/packages/cli/src/ui/components/shared/__snapshots__/SearchableList.test.tsx.snap
index 803ec8dd98..35f21daee3 100644
--- a/packages/cli/src/ui/components/shared/__snapshots__/SearchableList.test.tsx.snap
+++ b/packages/cli/src/ui/components/shared/__snapshots__/SearchableList.test.tsx.snap
@@ -7,7 +7,7 @@ exports[`SearchableList > should match snapshot 1`] = `
│ Search... │
╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- ● Item One
+ ● Item One
Description for item one
Item Two
@@ -28,7 +28,7 @@ exports[`SearchableList > should reset selection to top when items change if res
Item One
Description for item one
- ● Item Two
+ ● Item Two
Description for item two
Item Three
@@ -43,7 +43,7 @@ exports[`SearchableList > should reset selection to top when items change if res
│ One │
╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- ● Item One
+ ● Item One
Description for item one
"
`;
@@ -55,7 +55,7 @@ exports[`SearchableList > should reset selection to top when items change if res
│ Search... │
╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- ● Item One
+ ● Item One
Description for item one
Item Two
diff --git a/packages/cli/src/ui/components/shared/text-buffer.test.ts b/packages/cli/src/ui/components/shared/text-buffer.test.ts
index 51fa728c91..34db346a6d 100644
--- a/packages/cli/src/ui/components/shared/text-buffer.test.ts
+++ b/packages/cli/src/ui/components/shared/text-buffer.test.ts
@@ -5,6 +5,7 @@
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
+import '../../../test-utils/customMatchers.js';
import stripAnsi from 'strip-ansi';
import { act } from 'react';
import * as fs from 'node:fs';
diff --git a/packages/cli/src/ui/components/shared/vim-buffer-actions.test.ts b/packages/cli/src/ui/components/shared/vim-buffer-actions.test.ts
index 9cbfd9457b..c1a4b55d4b 100644
--- a/packages/cli/src/ui/components/shared/vim-buffer-actions.test.ts
+++ b/packages/cli/src/ui/components/shared/vim-buffer-actions.test.ts
@@ -5,6 +5,7 @@
*/
import { describe, it, expect } from 'vitest';
+import '../../../test-utils/customMatchers.js';
import { handleVimAction } from './vim-buffer-actions.js';
import type { TextBufferState, VisualLayout } from './text-buffer.js';
diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.test.ts b/packages/cli/src/ui/hooks/atCommandProcessor.test.ts
index eab3a82962..0d6fc2393a 100644
--- a/packages/cli/src/ui/hooks/atCommandProcessor.test.ts
+++ b/packages/cli/src/ui/hooks/atCommandProcessor.test.ts
@@ -108,7 +108,7 @@ describe('handleAtCommand', () => {
const workspaceDirs = this.getWorkspaceContext().getDirectories();
const projectTempDir = this.storage.getProjectTempDir();
- return `Path validation failed: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ return `Path validation failed: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the workspace temp directory: ${projectTempDir}`;
},
getMcpServers: () => ({}),
getMcpServerCommand: () => undefined,
diff --git a/packages/cli/src/ui/hooks/atCommandProcessor_agents.test.ts b/packages/cli/src/ui/hooks/atCommandProcessor_agents.test.ts
index 90267e64c0..f6f27594f4 100644
--- a/packages/cli/src/ui/hooks/atCommandProcessor_agents.test.ts
+++ b/packages/cli/src/ui/hooks/atCommandProcessor_agents.test.ts
@@ -108,7 +108,7 @@ describe('handleAtCommand with Agents', () => {
const workspaceDirs = this.getWorkspaceContext().getDirectories();
const projectTempDir = this.storage.getProjectTempDir();
- return `Path validation failed: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ return `Path validation failed: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the workspace temp directory: ${projectTempDir}`;
},
getMcpServers: () => ({}),
getMcpServerCommand: () => undefined,
diff --git a/packages/cli/src/ui/utils/TableRenderer.test.tsx b/packages/cli/src/ui/utils/TableRenderer.test.tsx
index 3960e8befe..e3f736d183 100644
--- a/packages/cli/src/ui/utils/TableRenderer.test.tsx
+++ b/packages/cli/src/ui/utils/TableRenderer.test.tsx
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
+import '../../../test-utils/customMatchers.js';
import { TableRenderer } from './TableRenderer.js';
import { renderWithProviders } from '../../test-utils/render.js';
diff --git a/packages/cli/src/ui/utils/__snapshots__/TableRenderer.test.tsx.snap b/packages/cli/src/ui/utils/__snapshots__/TableRenderer.test.tsx.snap
deleted file mode 100644
index 9b5c1e875a..0000000000
--- a/packages/cli/src/ui/utils/__snapshots__/TableRenderer.test.tsx.snap
+++ /dev/null
@@ -1,270 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`TableRenderer > 'calculates column widths based on ren…' 1`] = `
-"
-┌────────┬────────┬────────┐
-│ Col 1 │ Col 2 │ Col 3 │
-├────────┼────────┼────────┤
-│ 123456 │ Normal │ Short │
-│ Short │ 123456 │ Normal │
-│ Normal │ Short │ 123456 │
-└────────┴────────┴────────┘
-"
-`;
-
-exports[`TableRenderer > 'calculates width correctly for conten…' 1`] = `
-"
-┌───────────────────────────────────┬───────────────────────────────┬─────────────────────────────────┐
-│ Col 1 │ Col 2 │ Col 3 │
-├───────────────────────────────────┼───────────────────────────────┼─────────────────────────────────┤
-│ Visit Google (https://google.com) │ Plain Text │ More Info │
-│ Info Here │ Visit Bing (https://bing.com) │ Links │
-│ Check This │ Search │ Visit Yahoo (https://yahoo.com) │
-└───────────────────────────────────┴───────────────────────────────┴─────────────────────────────────┘
-"
-`;
-
-exports[`TableRenderer > 'does not parse markdown inside code s…' 1`] = `
-"
-┌─────────────────┬──────────────────────┬──────────────────┐
-│ Col 1 │ Col 2 │ Col 3 │
-├─────────────────┼──────────────────────┼──────────────────┤
-│ **not bold** │ _not italic_ │ ~~not strike~~ │
-│ [not link](url) │ not underline │ https://not.link │
-│ Normal Text │ More Code: *test* │ ***nested*** │
-└─────────────────┴──────────────────────┴──────────────────┘
-"
-`;
-
-exports[`TableRenderer > 'handles nested markdown styles recurs…' 1`] = `
-"
-┌─────────────────────────────┬─────────────────────────────┬─────────────────────────────┐
-│ Header 1 │ Header 2 │ Header 3 │
-├─────────────────────────────┼─────────────────────────────┼─────────────────────────────┤
-│ Bold with Italic and Strike │ Normal │ Short │
-│ Short │ Bold with Italic and Strike │ Normal │
-│ Normal │ Short │ Bold with Italic and Strike │
-└─────────────────────────────┴─────────────────────────────┴─────────────────────────────┘
-"
-`;
-
-exports[`TableRenderer > 'handles non-ASCII characters (emojis …' 1`] = `
-"
-┌──────────────┬────────────┬───────────────┐
-│ Emoji 😃 │ Asian 汉字 │ Mixed 🚀 Text │
-├──────────────┼────────────┼───────────────┤
-│ Start 🌟 End │ 你好世界 │ Rocket 🚀 Man │
-│ Thumbs 👍 Up │ こんにちは │ Fire 🔥 │
-└──────────────┴────────────┴───────────────┘
-"
-`;
-
-exports[`TableRenderer > 'renders a table with mixed emojis, As…' 1`] = `
-"
-┌───────────────┬───────────────────┬────────────────┐
-│ Mixed 😃 中文 │ Complex 🚀 日本語 │ Text 📝 한국어 │
-├───────────────┼───────────────────┼────────────────┤
-│ 你好 😃 │ こんにちは 🚀 │ 안녕하세요 📝 │
-│ World 🌍 │ Code 💻 │ Pizza 🍕 │
-└───────────────┴───────────────────┴────────────────┘
-"
-`;
-
-exports[`TableRenderer > 'renders a table with only Asian chara…' 1`] = `
-"
-┌──────────────┬─────────────────┬───────────────┐
-│ Chinese 中文 │ Japanese 日本語 │ Korean 한국어 │
-├──────────────┼─────────────────┼───────────────┤
-│ 你好 │ こんにちは │ 안녕하세요 │
-│ 世界 │ 世界 │ 세계 │
-└──────────────┴─────────────────┴───────────────┘
-"
-`;
-
-exports[`TableRenderer > 'renders a table with only emojis and …' 1`] = `
-"
-┌──────────┬───────────┬──────────┐
-│ Happy 😀 │ Rocket 🚀 │ Heart ❤️ │
-├──────────┼───────────┼──────────┤
-│ Smile 😃 │ Fire 🔥 │ Love 💖 │
-│ Cool 😎 │ Star ⭐ │ Blue 💙 │
-└──────────┴───────────┴──────────┘
-"
-`;
-
-exports[`TableRenderer > 'renders complex markdown in rows and …' 1`] = `
-"
-┌───────────────┬─────────────────────────────┐
-│ Feature │ Markdown │
-├───────────────┼─────────────────────────────┤
-│ Bold │ Bold Text │
-│ Italic │ Italic Text │
-│ Combined │ Bold and Italic │
-│ Link │ Google (https://google.com) │
-│ Code │ const x = 1 │
-│ Strikethrough │ Strike │
-│ Underline │ Underline │
-└───────────────┴─────────────────────────────┘
-"
-`;
-
-exports[`TableRenderer > 'renders correctly when headers are em…' 1`] = `
-"
-┌────────┬────────┐
-│ │ │
-├────────┼────────┤
-│ Data 1 │ Data 2 │
-└────────┴────────┘
-"
-`;
-
-exports[`TableRenderer > 'renders correctly when there are more…' 1`] = `
-"
-┌──────────┬──────────┬──────────┐
-│ Header 1 │ Header 2 │ Header 3 │
-├──────────┼──────────┼──────────┤
-│ Data 1 │ Data 2 │ │
-└──────────┴──────────┴──────────┘
-"
-`;
-
-exports[`TableRenderer > handles wrapped bold headers without showing markers 1`] = `
-"
-┌─────────────┬───────┬─────────┐
-│ Very Long │ Short │ Another │
-│ Bold Header │ │ Long │
-│ That Will │ │ Header │
-│ Wrap │ │ │
-├─────────────┼───────┼─────────┤
-│ Data 1 │ Data │ Data 3 │
-│ │ 2 │ │
-└─────────────┴───────┴─────────┘
-"
-`;
-
-exports[`TableRenderer > renders a 3x3 table correctly 1`] = `
-"
-┌──────────────┬──────────────┬──────────────┐
-│ Header 1 │ Header 2 │ Header 3 │
-├──────────────┼──────────────┼──────────────┤
-│ Row 1, Col 1 │ Row 1, Col 2 │ Row 1, Col 3 │
-│ Row 2, Col 1 │ Row 2, Col 2 │ Row 2, Col 3 │
-│ Row 3, Col 1 │ Row 3, Col 2 │ Row 3, Col 3 │
-└──────────────┴──────────────┴──────────────┘
-"
-`;
-
-exports[`TableRenderer > renders a complex table with mixed content lengths correctly 1`] = `
-"
-┌─────────────────────────────┬──────────────────────────────┬─────────────────────────────┬──────────────────────────────┬─────┬────────┬─────────┬───────┐
-│ Comprehensive Architectural │ Implementation Details for │ Longitudinal Performance │ Strategic Security Framework │ Key │ Status │ Version │ Owner │
-│ Specification for the │ the High-Throughput │ Analysis Across │ for Mitigating Sophisticated │ │ │ │ │
-│ Distributed Infrastructure │ Asynchronous Message │ Multi-Regional Cloud │ Cross-Site Scripting │ │ │ │ │
-│ Layer │ Processing Pipeline with │ Deployment Clusters │ Vulnerabilities │ │ │ │ │
-│ │ Extended Scalability │ │ │ │ │ │ │
-│ │ Features and Redundancy │ │ │ │ │ │ │
-│ │ Protocols │ │ │ │ │ │ │
-├─────────────────────────────┼──────────────────────────────┼─────────────────────────────┼──────────────────────────────┼─────┼────────┼─────────┼───────┤
-│ The primary architecture │ Each message is processed │ Historical data indicates a │ A multi-layered defense │ INF │ Active │ v2.4 │ J. │
-│ utilizes a decoupled │ through a series of │ significant reduction in │ strategy incorporates │ │ │ │ Doe │
-│ microservices approach, │ specialized workers that │ tail latency when utilizing │ content security policies, │ │ │ │ │
-│ leveraging container │ handle data transformation, │ edge computing nodes closer │ input sanitization │ │ │ │ │
-│ orchestration for │ validation, and persistent │ to the geographic location │ libraries, and regular │ │ │ │ │
-│ scalability and fault │ storage using a persistent │ of the end-user base. │ automated penetration │ │ │ │ │
-│ tolerance in high-load │ queue. │ │ testing routines. │ │ │ │ │
-│ scenarios. │ │ Monitoring tools have │ │ │ │ │ │
-│ │ The pipeline features │ captured a steady increase │ Developers are required to │ │ │ │ │
-│ This layer provides the │ built-in retry mechanisms │ in throughput efficiency │ undergo mandatory security │ │ │ │ │
-│ fundamental building blocks │ with exponential backoff to │ since the introduction of │ training focusing on the │ │ │ │ │
-│ for service discovery, load │ ensure message delivery │ the vectorized query engine │ OWASP Top Ten to ensure that │ │ │ │ │
-│ balancing, and │ integrity even during │ in the primary data │ security is integrated into │ │ │ │ │
-│ inter-service communication │ transient network or service │ warehouse. │ the initial design phase. │ │ │ │ │
-│ via highly efficient │ failures. │ │ │ │ │ │ │
-│ protocol buffers. │ │ Resource utilization │ The implementation of a │ │ │ │ │
-│ │ Horizontal autoscaling is │ metrics demonstrate that │ robust Identity and Access │ │ │ │ │
-│ Advanced telemetry and │ triggered automatically │ the transition to │ Management system ensures │ │ │ │ │
-│ logging integrations allow │ based on the depth of the │ serverless compute for │ that the principle of least │ │ │ │ │
-│ for real-time monitoring of │ processing queue, ensuring │ intermittent tasks has │ privilege is strictly │ │ │ │ │
-│ system health and rapid │ consistent performance │ resulted in a thirty │ enforced across all │ │ │ │ │
-│ identification of │ during unexpected traffic │ percent cost optimization. │ environments. │ │ │ │ │
-│ bottlenecks within the │ spikes. │ │ │ │ │ │ │
-│ service mesh. │ │ │ │ │ │ │ │
-└─────────────────────────────┴──────────────────────────────┴─────────────────────────────┴──────────────────────────────┴─────┴────────┴─────────┴───────┘
-"
-`;
-
-exports[`TableRenderer > renders a table with long headers and 4 columns correctly 1`] = `
-"
-┌───────────────┬───────────────┬──────────────────┬──────────────────┐
-│ Very Long │ Very Long │ Very Long Column │ Very Long Column │
-│ Column Header │ Column Header │ Header Three │ Header Four │
-│ One │ Two │ │ │
-├───────────────┼───────────────┼──────────────────┼──────────────────┤
-│ Data 1.1 │ Data 1.2 │ Data 1.3 │ Data 1.4 │
-│ Data 2.1 │ Data 2.2 │ Data 2.3 │ Data 2.4 │
-│ Data 3.1 │ Data 3.2 │ Data 3.3 │ Data 3.4 │
-└───────────────┴───────────────┴──────────────────┴──────────────────┘
-"
-`;
-
-exports[`TableRenderer > strips bold markers from headers and renders them correctly 1`] = `
-"
-┌─────────────┬───────────────┬──────────────┐
-│ Bold Header │ Normal Header │ Another Bold │
-├─────────────┼───────────────┼──────────────┤
-│ Data 1 │ Data 2 │ Data 3 │
-└─────────────┴───────────────┴──────────────┘
-"
-`;
-
-exports[`TableRenderer > wraps all long columns correctly 1`] = `
-"
-┌────────────────┬────────────────┬─────────────────┐
-│ Col 1 │ Col 2 │ Col 3 │
-├────────────────┼────────────────┼─────────────────┤
-│ This is a very │ This is also a │ And this is the │
-│ long text that │ very long text │ third long text │
-│ needs wrapping │ that needs │ that needs │
-│ in column 1 │ wrapping in │ wrapping in │
-│ │ column 2 │ column 3 │
-└────────────────┴────────────────┴─────────────────┘
-"
-`;
-
-exports[`TableRenderer > wraps columns with punctuation correctly 1`] = `
-"
-┌───────────────────┬───────────────┬─────────────────┐
-│ Punctuation 1 │ Punctuation 2 │ Punctuation 3 │
-├───────────────────┼───────────────┼─────────────────┤
-│ Start. Stop. │ Semi; colon: │ At@ Hash# │
-│ Comma, separated. │ Pipe| Slash/ │ Dollar$ │
-│ Exclamation! │ Backslash\\ │ Percent% Caret^ │
-│ Question? │ │ Ampersand& │
-│ hyphen-ated │ │ Asterisk* │
-└───────────────────┴───────────────┴─────────────────┘
-"
-`;
-
-exports[`TableRenderer > wraps long cell content correctly 1`] = `
-"
-┌───────┬─────────────────────────────┬───────┐
-│ Col 1 │ Col 2 │ Col 3 │
-├───────┼─────────────────────────────┼───────┤
-│ Short │ This is a very long cell │ Short │
-│ │ content that should wrap to │ │
-│ │ multiple lines │ │
-└───────┴─────────────────────────────┴───────┘
-"
-`;
-
-exports[`TableRenderer > wraps mixed long and short columns correctly 1`] = `
-"
-┌───────┬──────────────────────────┬────────┐
-│ Short │ Long │ Medium │
-├───────┼──────────────────────────┼────────┤
-│ Tiny │ This is a very long text │ Not so │
-│ │ that definitely needs to │ long │
-│ │ wrap to the next line │ │
-└───────┴──────────────────────────┴────────┘
-"
-`;
diff --git a/packages/cli/src/ui/utils/__snapshots__/borderStyles.test.tsx.snap b/packages/cli/src/ui/utils/__snapshots__/borderStyles.test.tsx.snap
deleted file mode 100644
index fbdc559480..0000000000
--- a/packages/cli/src/ui/utils/__snapshots__/borderStyles.test.tsx.snap
+++ /dev/null
@@ -1,55 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`MainContent tool group border SVG snapshots > should render SVG snapshot for a pending search dialog (google_web_search) 1`] = `
-"
- ███ █████████
-░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
- ███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
-
-╭──────────────────────────────────────────────────────────────────────────────────────────────╮
-│ ⊷ google_web_search │
-│ │
-│ Searching... │
-╰──────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`MainContent tool group border SVG snapshots > should render SVG snapshot for a shell tool 1`] = `
-"
- ███ █████████
-░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
- ███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
-
-╭──────────────────────────────────────────────────────────────────────────────────────────────╮
-│ ⊷ run_shell_command │
-│ │
-│ Running command... │
-╰──────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
-
-exports[`MainContent tool group border SVG snapshots > should render SVG snapshot for an empty slice following a search tool 1`] = `
-"
- ███ █████████
-░░░███ ███░░░░░███
- ░░░███ ███ ░░░
- ░░░███░███
- ███░ ░███ █████
- ███░ ░░███ ░░███
- ███░ ░░█████████
-░░░ ░░░░░░░░░
-
-╭──────────────────────────────────────────────────────────────────────────────────────────────╮
-│ ⊷ google_web_search │
-│ │
-│ Searching... │
-╰──────────────────────────────────────────────────────────────────────────────────────────────╯"
-`;
diff --git a/packages/cli/src/ui/utils/borderStyles.test.tsx b/packages/cli/src/ui/utils/borderStyles.test.tsx
index 91b2497f7f..b5e61144a9 100644
--- a/packages/cli/src/ui/utils/borderStyles.test.tsx
+++ b/packages/cli/src/ui/utils/borderStyles.test.tsx
@@ -5,6 +5,7 @@
*/
import { describe, expect, it } from 'vitest';
+import '../../../test-utils/customMatchers.js';
import { getToolGroupBorderAppearance } from './borderStyles.js';
import { CoreToolCallStatus } from '@google/gemini-cli-core';
import { theme } from '../semantic-colors.js';
diff --git a/packages/cli/src/ui/utils/clipboardUtils.test.ts b/packages/cli/src/ui/utils/clipboardUtils.test.ts
index cfd9f115ba..0ea4668e8c 100644
--- a/packages/cli/src/ui/utils/clipboardUtils.test.ts
+++ b/packages/cli/src/ui/utils/clipboardUtils.test.ts
@@ -52,7 +52,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
warn: vi.fn(),
},
Storage: class {
- getProjectTempDir = vi.fn(() => '/tmp/global');
+ getWorkspaceTempDir = vi.fn(() => '/tmp/global');
initialize = vi.fn(() => Promise.resolve(undefined));
},
};
diff --git a/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts b/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts
index 6fce8197fd..1d8526a42d 100644
--- a/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts
+++ b/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts
@@ -17,7 +17,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
...actual,
spawnAsync: vi.fn(),
Storage: class {
- getProjectTempDir = vi.fn(() => "C:\\User's Files");
+ getWorkspaceTempDir = vi.fn(() => "C:\\User's Files");
initialize = vi.fn(() => Promise.resolve(undefined));
},
};
diff --git a/packages/cli/src/utils/agentUtils.test.ts b/packages/cli/src/utils/agentUtils.test.ts
index e62fb7f1f2..c05368eb96 100644
--- a/packages/cli/src/utils/agentUtils.test.ts
+++ b/packages/cli/src/utils/agentUtils.test.ts
@@ -108,7 +108,7 @@ describe('agentUtils', () => {
],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
- 'Agent "my-agent" enabled by setting it to enabled in [user:/path/to/user/settings] and [project:/path/to/workspace/settings] settings.',
+ 'Agent "my-agent" enabled by setting it to enabled in [user:/path/to/user/settings] and [workspace:/path/to/workspace/settings] settings.',
);
});
@@ -143,7 +143,7 @@ describe('agentUtils', () => {
],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
- 'Agent "my-agent" is now disabled in both [user:/path/to/user/settings] and [project:/path/to/workspace/settings] settings.',
+ 'Agent "my-agent" is now disabled in both [user:/path/to/user/settings] and [workspace:/path/to/workspace/settings] settings.',
);
});
});
diff --git a/packages/cli/src/utils/agentUtils.ts b/packages/cli/src/utils/agentUtils.ts
index 4bcee796d1..87a0f205be 100644
--- a/packages/cli/src/utils/agentUtils.ts
+++ b/packages/cli/src/utils/agentUtils.ts
@@ -40,7 +40,7 @@ export function renderAgentActionFeedback(
const formatScopeItem = (s: { scope: SettingScope; path: string }) => {
const label =
- s.scope === SettingScope.Workspace ? 'project' : s.scope.toLowerCase();
+ s.scope === SettingScope.Workspace ? 'workspace' : s.scope.toLowerCase();
return formatScope(label, s.path);
};
diff --git a/packages/cli/src/utils/cleanup.test.ts b/packages/cli/src/utils/cleanup.test.ts
index e9a2b0ea76..b94738764a 100644
--- a/packages/cli/src/utils/cleanup.test.ts
+++ b/packages/cli/src/utils/cleanup.test.ts
@@ -10,7 +10,7 @@ import * as path from 'node:path';
vi.mock('@google/gemini-cli-core', () => ({
Storage: vi.fn().mockImplementation(() => ({
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/workspace'),
initialize: vi.fn().mockResolvedValue(undefined),
})),
shutdownTelemetry: vi.fn(),
@@ -112,7 +112,7 @@ describe('cleanup', () => {
it('should remove checkpoints directory', async () => {
await cleanupCheckpoints();
expect(fs.rm).toHaveBeenCalledWith(
- path.join('/tmp/project', 'checkpoints'),
+ path.join('/tmp/workspace', 'checkpoints'),
{
recursive: true,
force: true,
diff --git a/packages/cli/src/utils/cleanup.ts b/packages/cli/src/utils/cleanup.ts
index 6185b34fe5..2f7a93eb5f 100644
--- a/packages/cli/src/utils/cleanup.ts
+++ b/packages/cli/src/utils/cleanup.ts
@@ -165,7 +165,7 @@ export function setupTtyCheck(): () => void {
export async function cleanupCheckpoints() {
const storage = new Storage(process.cwd());
await storage.initialize();
- const tempDir = storage.getProjectTempDir();
+ const tempDir = storage.getWorkspaceTempDir();
const checkpointsDir = join(tempDir, 'checkpoints');
try {
await fs.rm(checkpointsDir, { recursive: true, force: true });
diff --git a/packages/cli/src/utils/handleAutoUpdate.ts b/packages/cli/src/utils/handleAutoUpdate.ts
index a6d0cdc574..56527ec286 100644
--- a/packages/cli/src/utils/handleAutoUpdate.ts
+++ b/packages/cli/src/utils/handleAutoUpdate.ts
@@ -16,7 +16,7 @@ import type { spawn } from 'node:child_process';
export function handleAutoUpdate(
info: UpdateObject | null,
settings: LoadedSettings,
- projectRoot: string,
+ workspaceRoot: string,
spawnFn: typeof spawn = spawnWrapper,
) {
if (!info) {
@@ -35,7 +35,7 @@ export function handleAutoUpdate(
}
const installationInfo = getInstallationInfo(
- projectRoot,
+ workspaceRoot,
settings.merged.general.enableAutoUpdate,
);
diff --git a/packages/cli/src/utils/installationInfo.test.ts b/packages/cli/src/utils/installationInfo.test.ts
index ca1120c0e3..bbae7b2ba4 100644
--- a/packages/cli/src/utils/installationInfo.test.ts
+++ b/packages/cli/src/utils/installationInfo.test.ts
@@ -43,14 +43,14 @@ const mockedExistsSync = vi.mocked(fs.existsSync);
const mockedExecSync = vi.mocked(childProcess.execSync);
describe('getInstallationInfo', () => {
- const projectRoot = '/path/to/project';
+ const workspaceRoot = '/path/to/workspace';
let originalArgv: string[];
beforeEach(() => {
vi.resetAllMocks();
originalArgv = [...process.argv];
// Mock process.cwd() for isGitRepository
- vi.spyOn(process, 'cwd').mockReturnValue(projectRoot);
+ vi.spyOn(process, 'cwd').mockReturnValue(workspaceRoot);
vi.spyOn(debugLogger, 'log').mockImplementation(() => {});
});
@@ -60,7 +60,7 @@ describe('getInstallationInfo', () => {
it('should return UNKNOWN when cliPath is not available', () => {
process.argv[1] = '';
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.UNKNOWN);
});
@@ -71,20 +71,20 @@ describe('getInstallationInfo', () => {
throw error;
});
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.UNKNOWN);
expect(debugLogger.log).toHaveBeenCalledWith(error);
});
it('should detect running from a local git clone', () => {
- process.argv[1] = `${projectRoot}/packages/cli/dist/index.js`;
+ process.argv[1] = `${workspaceRoot}/packages/cli/dist/index.js`;
mockedRealPathSync.mockReturnValue(
- `${projectRoot}/packages/cli/dist/index.js`,
+ `${workspaceRoot}/packages/cli/dist/index.js`,
);
mockedIsGitRepository.mockReturnValue(true);
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.UNKNOWN);
expect(info.isGlobal).toBe(false);
@@ -98,7 +98,7 @@ describe('getInstallationInfo', () => {
process.argv[1] = npxPath;
mockedRealPathSync.mockReturnValue(npxPath);
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.NPX);
expect(info.isGlobal).toBe(false);
@@ -110,7 +110,7 @@ describe('getInstallationInfo', () => {
process.argv[1] = pnpxPath;
mockedRealPathSync.mockReturnValue(pnpxPath);
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.PNPX);
expect(info.isGlobal).toBe(false);
@@ -125,7 +125,7 @@ describe('getInstallationInfo', () => {
throw new Error('Command failed');
});
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.BUNX);
expect(info.isGlobal).toBe(false);
@@ -155,7 +155,7 @@ describe('getInstallationInfo', () => {
return String(p);
});
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(mockedExecSync).toHaveBeenCalledWith(
expect.stringContaining('brew --prefix gemini-cli'),
@@ -179,7 +179,7 @@ describe('getInstallationInfo', () => {
throw new Error('Command failed');
});
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(mockedExecSync).toHaveBeenCalledWith(
expect.stringContaining('brew --prefix gemini-cli'),
@@ -199,14 +199,14 @@ describe('getInstallationInfo', () => {
});
// isAutoUpdateEnabled = true -> "Attempting to automatically update"
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.PNPM);
expect(info.isGlobal).toBe(true);
expect(info.updateCommand).toBe('pnpm add -g @google/gemini-cli@latest');
expect(info.updateMessage).toContain('Attempting to automatically update');
// isAutoUpdateEnabled = false -> "Please run..."
- const infoDisabled = getInstallationInfo(projectRoot, false);
+ const infoDisabled = getInstallationInfo(workspaceRoot, false);
expect(infoDisabled.updateMessage).toContain('Please run pnpm add');
});
@@ -219,7 +219,7 @@ describe('getInstallationInfo', () => {
});
// isAutoUpdateEnabled = true -> "Attempting to automatically update"
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.YARN);
expect(info.isGlobal).toBe(true);
expect(info.updateCommand).toBe(
@@ -228,7 +228,7 @@ describe('getInstallationInfo', () => {
expect(info.updateMessage).toContain('Attempting to automatically update');
// isAutoUpdateEnabled = false -> "Please run..."
- const infoDisabled = getInstallationInfo(projectRoot, false);
+ const infoDisabled = getInstallationInfo(workspaceRoot, false);
expect(infoDisabled.updateMessage).toContain('Please run yarn global add');
});
@@ -241,29 +241,29 @@ describe('getInstallationInfo', () => {
});
// isAutoUpdateEnabled = true -> "Attempting to automatically update"
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.BUN);
expect(info.isGlobal).toBe(true);
expect(info.updateCommand).toBe('bun add -g @google/gemini-cli@latest');
expect(info.updateMessage).toContain('Attempting to automatically update');
// isAutoUpdateEnabled = false -> "Please run..."
- const infoDisabled = getInstallationInfo(projectRoot, false);
+ const infoDisabled = getInstallationInfo(workspaceRoot, false);
expect(infoDisabled.updateMessage).toContain('Please run bun add');
});
it('should detect local installation and identify yarn from lockfile', () => {
- const localPath = `${projectRoot}/node_modules/.bin/gemini`;
+ const localPath = `${workspaceRoot}/node_modules/.bin/gemini`;
process.argv[1] = localPath;
mockedRealPathSync.mockReturnValue(localPath);
mockedExecSync.mockImplementation(() => {
throw new Error('Command failed');
});
mockedExistsSync.mockImplementation(
- (p) => p === path.join(projectRoot, 'yarn.lock'),
+ (p) => p === path.join(workspaceRoot, 'yarn.lock'),
);
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.YARN);
expect(info.isGlobal).toBe(false);
@@ -271,41 +271,41 @@ describe('getInstallationInfo', () => {
});
it('should detect local installation and identify pnpm from lockfile', () => {
- const localPath = `${projectRoot}/node_modules/.bin/gemini`;
+ const localPath = `${workspaceRoot}/node_modules/.bin/gemini`;
process.argv[1] = localPath;
mockedRealPathSync.mockReturnValue(localPath);
mockedExecSync.mockImplementation(() => {
throw new Error('Command failed');
});
mockedExistsSync.mockImplementation(
- (p) => p === path.join(projectRoot, 'pnpm-lock.yaml'),
+ (p) => p === path.join(workspaceRoot, 'pnpm-lock.yaml'),
);
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.PNPM);
expect(info.isGlobal).toBe(false);
});
it('should detect local installation and identify bun from lockfile', () => {
- const localPath = `${projectRoot}/node_modules/.bin/gemini`;
+ const localPath = `${workspaceRoot}/node_modules/.bin/gemini`;
process.argv[1] = localPath;
mockedRealPathSync.mockReturnValue(localPath);
mockedExecSync.mockImplementation(() => {
throw new Error('Command failed');
});
mockedExistsSync.mockImplementation(
- (p) => p === path.join(projectRoot, 'bun.lockb'),
+ (p) => p === path.join(workspaceRoot, 'bun.lockb'),
);
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.BUN);
expect(info.isGlobal).toBe(false);
});
it('should default to local npm installation if no lockfile is found', () => {
- const localPath = `${projectRoot}/node_modules/.bin/gemini`;
+ const localPath = `${workspaceRoot}/node_modules/.bin/gemini`;
process.argv[1] = localPath;
mockedRealPathSync.mockReturnValue(localPath);
mockedExecSync.mockImplementation(() => {
@@ -313,7 +313,7 @@ describe('getInstallationInfo', () => {
});
mockedExistsSync.mockReturnValue(false); // No lockfiles
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.NPM);
expect(info.isGlobal).toBe(false);
@@ -328,14 +328,14 @@ describe('getInstallationInfo', () => {
});
// isAutoUpdateEnabled = true -> "Attempting to automatically update"
- const info = getInstallationInfo(projectRoot, true);
+ const info = getInstallationInfo(workspaceRoot, true);
expect(info.packageManager).toBe(PackageManager.NPM);
expect(info.isGlobal).toBe(true);
expect(info.updateCommand).toBe('npm install -g @google/gemini-cli@latest');
expect(info.updateMessage).toContain('Attempting to automatically update');
// isAutoUpdateEnabled = false -> "Please run..."
- const infoDisabled = getInstallationInfo(projectRoot, false);
+ const infoDisabled = getInstallationInfo(workspaceRoot, false);
expect(infoDisabled.updateMessage).toContain('Please run npm install');
});
@@ -368,7 +368,7 @@ describe('getInstallationInfo', () => {
return String(p);
});
- const info = getInstallationInfo(projectRoot, false);
+ const info = getInstallationInfo(workspaceRoot, false);
expect(info.packageManager).not.toBe(PackageManager.HOMEBREW);
expect(info.packageManager).toBe(PackageManager.NPM);
diff --git a/packages/cli/src/utils/installationInfo.ts b/packages/cli/src/utils/installationInfo.ts
index a682cc75e1..c727e458d9 100644
--- a/packages/cli/src/utils/installationInfo.ts
+++ b/packages/cli/src/utils/installationInfo.ts
@@ -32,7 +32,7 @@ export interface InstallationInfo {
}
export function getInstallationInfo(
- projectRoot: string,
+ workspaceRoot: string,
isAutoUpdateEnabled: boolean,
): InstallationInfo {
const cliPath = process.argv[1];
@@ -43,7 +43,7 @@ export function getInstallationInfo(
try {
// Normalize path separators to forward slashes for consistent matching.
const realPath = fs.realpathSync(cliPath).replace(/\\/g, '/');
- const normalizedProjectRoot = projectRoot?.replace(/\\/g, '/');
+ const normalizedProjectRoot = workspaceRoot?.replace(/\\/g, '/');
const isGit = isGitRepository(process.cwd());
// Check for local git clone first
@@ -160,11 +160,11 @@ export function getInstallationInfo(
realPath.startsWith(`${normalizedProjectRoot}/node_modules`)
) {
let pm = PackageManager.NPM;
- if (fs.existsSync(path.join(projectRoot, 'yarn.lock'))) {
+ if (fs.existsSync(path.join(workspaceRoot, 'yarn.lock'))) {
pm = PackageManager.YARN;
- } else if (fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))) {
+ } else if (fs.existsSync(path.join(workspaceRoot, 'pnpm-lock.yaml'))) {
pm = PackageManager.PNPM;
- } else if (fs.existsSync(path.join(projectRoot, 'bun.lockb'))) {
+ } else if (fs.existsSync(path.join(workspaceRoot, 'bun.lockb'))) {
pm = PackageManager.BUN;
}
return {
diff --git a/packages/cli/src/utils/sandboxUtils.test.ts b/packages/cli/src/utils/sandboxUtils.test.ts
index b999f415e4..a35fe24751 100644
--- a/packages/cli/src/utils/sandboxUtils.test.ts
+++ b/packages/cli/src/utils/sandboxUtils.test.ts
@@ -69,7 +69,7 @@ describe('sandboxUtils', () => {
});
it('should handle registry path', () => {
- expect(parseImageName('gcr.io/my-project/my-image:v1')).toBe(
+ expect(parseImageName('gcr.io/my-workspace/my-image:v1')).toBe(
'my-image-v1',
);
});
diff --git a/packages/cli/src/utils/sessionCleanup.integration.test.ts b/packages/cli/src/utils/sessionCleanup.integration.test.ts
index eec9a12592..9efd202618 100644
--- a/packages/cli/src/utils/sessionCleanup.integration.test.ts
+++ b/packages/cli/src/utils/sessionCleanup.integration.test.ts
@@ -17,7 +17,7 @@ import {
function createTestConfig(): Config {
return {
storage: {
- getProjectTempDir: () => '/tmp/nonexistent-test-dir',
+ getWorkspaceTempDir: () => '/tmp/nonexistent-test-dir',
},
getSessionId: () => 'test-session-id',
getDebugMode: () => false,
@@ -93,7 +93,7 @@ describe('Session Cleanup Integration', () => {
);
const config = createTestConfig();
- config.storage.getProjectTempDir = vi.fn().mockReturnValue(tempDir);
+ config.storage.getWorkspaceTempDir = vi.fn().mockReturnValue(tempDir);
const settings: Settings = {};
@@ -209,7 +209,7 @@ describe('Session Cleanup Integration', () => {
// Configure test with real temp directory
const config: Config = {
storage: {
- getProjectTempDir: () => tempDir,
+ getWorkspaceTempDir: () => tempDir,
},
getSessionId: () => 'current123',
getDebugMode: () => false,
diff --git a/packages/cli/src/utils/sessionCleanup.test.ts b/packages/cli/src/utils/sessionCleanup.test.ts
index cc775d01c9..ec5a0838c1 100644
--- a/packages/cli/src/utils/sessionCleanup.test.ts
+++ b/packages/cli/src/utils/sessionCleanup.test.ts
@@ -28,8 +28,8 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
return {
...actual,
Storage: class MockStorage {
- getProjectTempDir() {
- return '/tmp/test-project';
+ getWorkspaceTempDir() {
+ return '/tmp/test-workspace';
}
},
};
@@ -42,7 +42,7 @@ const mockGetAllSessionFiles = vi.mocked(getAllSessionFiles);
function createMockConfig(overrides: Partial = {}): Config {
return {
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/test-project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/test-workspace'),
},
getSessionId: vi.fn().mockReturnValue('current123'),
getDebugMode: vi.fn().mockReturnValue(false),
@@ -245,7 +245,7 @@ describe('Session Cleanup', () => {
// Verify that unlink was never called with the current session file
const unlinkCalls = mockFs.unlink.mock.calls;
const currentSessionPath = path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}2025-01-20T10-30-00-current12.json`,
);
@@ -525,21 +525,21 @@ describe('Session Cleanup', () => {
const unlinkCalls = mockFs.unlink.mock.calls.map((call) => call[0]);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}8d.json`,
),
);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}15d.json`,
),
);
expect(unlinkCalls).not.toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}5d.json`,
),
@@ -724,21 +724,21 @@ describe('Session Cleanup', () => {
const unlinkCalls = mockFs.unlink.mock.calls.map((call) => call[0]);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}3d.json`,
),
);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}4d.json`,
),
);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}5d.json`,
),
@@ -747,21 +747,21 @@ describe('Session Cleanup', () => {
// Verify which files were NOT deleted
expect(unlinkCalls).not.toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}current.json`,
),
);
expect(unlinkCalls).not.toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}1d.json`,
),
);
expect(unlinkCalls).not.toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}2d.json`,
),
@@ -883,21 +883,21 @@ describe('Session Cleanup', () => {
const unlinkCalls = mockFs.unlink.mock.calls.map((call) => call[0]);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}5d.json`,
),
);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}7d.json`,
),
);
expect(unlinkCalls).toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}12d.json`,
),
@@ -906,14 +906,14 @@ describe('Session Cleanup', () => {
// Verify which files were NOT deleted
expect(unlinkCalls).not.toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}current.json`,
),
);
expect(unlinkCalls).not.toContain(
path.join(
- '/tmp/test-project',
+ '/tmp/test-workspace',
'chats',
`${SESSION_FILE_PREFIX}3d.json`,
),
diff --git a/packages/cli/src/utils/sessionCleanup.ts b/packages/cli/src/utils/sessionCleanup.ts
index 64e3b4c565..0bbeab5051 100644
--- a/packages/cli/src/utils/sessionCleanup.ts
+++ b/packages/cli/src/utils/sessionCleanup.ts
@@ -59,7 +59,7 @@ export async function cleanupExpiredSessions(
}
const retentionConfig = settings.general.sessionRetention;
- const chatsDir = path.join(config.storage.getProjectTempDir(), 'chats');
+ const chatsDir = path.join(config.storage.getWorkspaceTempDir(), 'chats');
// Validate retention configuration
const validationErrorMessage = validateRetentionConfig(
@@ -95,7 +95,10 @@ export async function cleanupExpiredSessions(
// ALSO cleanup Activity logs in the project logs directory
const sessionId = sessionToDelete.sessionInfo?.id;
if (sessionId) {
- const logsDir = path.join(config.storage.getProjectTempDir(), 'logs');
+ const logsDir = path.join(
+ config.storage.getWorkspaceTempDir(),
+ 'logs',
+ );
const logPath = path.join(logsDir, `session-${sessionId}.jsonl`);
try {
await fs.unlink(logPath);
@@ -106,7 +109,7 @@ export async function cleanupExpiredSessions(
// ALSO cleanup tool outputs for this session
const safeSessionId = sanitizeFilenamePart(sessionId);
const toolOutputDir = path.join(
- config.storage.getProjectTempDir(),
+ config.storage.getWorkspaceTempDir(),
TOOL_OUTPUTS_DIR,
`session-${safeSessionId}`,
);
@@ -371,7 +374,7 @@ export async function cleanupToolOutputFiles(
if (!tempDir) {
const storage = new Storage(process.cwd());
await storage.initialize();
- tempDir = storage.getProjectTempDir();
+ tempDir = storage.getWorkspaceTempDir();
}
const toolOutputDir = path.join(tempDir, TOOL_OUTPUTS_DIR);
diff --git a/packages/cli/src/utils/sessionUtils.test.ts b/packages/cli/src/utils/sessionUtils.test.ts
index 8491f748bd..aa8fe6e457 100644
--- a/packages/cli/src/utils/sessionUtils.test.ts
+++ b/packages/cli/src/utils/sessionUtils.test.ts
@@ -30,7 +30,7 @@ describe('SessionSelector', () => {
// Mock config
config = {
storage: {
- getProjectTempDir: () => tmpDir,
+ getWorkspaceTempDir: () => tmpDir,
},
getSessionId: () => 'current-session-id',
} as Partial as Config;
@@ -55,7 +55,7 @@ describe('SessionSelector', () => {
const session1 = {
sessionId: sessionId1,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -70,7 +70,7 @@ describe('SessionSelector', () => {
const session2 = {
sessionId: sessionId2,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T11:00:00.000Z',
lastUpdated: '2024-01-01T11:30:00.000Z',
messages: [
@@ -121,7 +121,7 @@ describe('SessionSelector', () => {
const session1 = {
sessionId: sessionId1,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -136,7 +136,7 @@ describe('SessionSelector', () => {
const session2 = {
sessionId: sessionId2,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T11:00:00.000Z',
lastUpdated: '2024-01-01T11:30:00.000Z',
messages: [
@@ -185,7 +185,7 @@ describe('SessionSelector', () => {
const session1 = {
sessionId: sessionId1,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -200,7 +200,7 @@ describe('SessionSelector', () => {
const session2 = {
sessionId: sessionId2,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T11:00:00.000Z',
lastUpdated: '2024-01-01T11:30:00.000Z',
messages: [
@@ -245,7 +245,7 @@ describe('SessionSelector', () => {
const sessionOriginal = {
sessionId,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -260,7 +260,7 @@ describe('SessionSelector', () => {
const sessionDuplicate = {
sessionId,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T11:00:00.000Z', // Newer
messages: [
@@ -309,7 +309,7 @@ describe('SessionSelector', () => {
const session1 = {
sessionId: sessionId1,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -352,7 +352,7 @@ describe('SessionSelector', () => {
// Session with user message - should be listed
const sessionWithUser = {
sessionId: sessionIdWithUser,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -368,7 +368,7 @@ describe('SessionSelector', () => {
// Session with only system messages - should NOT be listed
const sessionSystemOnly = {
sessionId: sessionIdSystemOnly,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T11:00:00.000Z',
lastUpdated: '2024-01-01T11:30:00.000Z',
messages: [
@@ -421,7 +421,7 @@ describe('SessionSelector', () => {
// Session with only gemini message - should be listed
const sessionGeminiOnly = {
sessionId: sessionIdGeminiOnly,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -461,7 +461,7 @@ describe('SessionSelector', () => {
// Main session - should be listed
const mainSession = {
sessionId: mainSessionId,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T10:00:00.000Z',
lastUpdated: '2024-01-01T10:30:00.000Z',
messages: [
@@ -478,7 +478,7 @@ describe('SessionSelector', () => {
// Subagent session - should NOT be listed
const subagentSession = {
sessionId: subagentSessionId,
- projectHash: 'test-hash',
+ workspaceHash: 'test-hash',
startTime: '2024-01-01T11:00:00.000Z',
lastUpdated: '2024-01-01T11:30:00.000Z',
messages: [
diff --git a/packages/cli/src/utils/sessionUtils.ts b/packages/cli/src/utils/sessionUtils.ts
index aaecb31510..bbc8a41f70 100644
--- a/packages/cli/src/utils/sessionUtils.ts
+++ b/packages/cli/src/utils/sessionUtils.ts
@@ -402,7 +402,7 @@ export class SessionSelector {
*/
async listSessions(): Promise {
const chatsDir = path.join(
- this.config.storage.getProjectTempDir(),
+ this.config.storage.getWorkspaceTempDir(),
'chats',
);
return getSessionFiles(chatsDir, this.config.getSessionId());
@@ -498,7 +498,7 @@ export class SessionSelector {
sessionInfo: SessionInfo,
): Promise {
const chatsDir = path.join(
- this.config.storage.getProjectTempDir(),
+ this.config.storage.getWorkspaceTempDir(),
'chats',
);
const sessionPath = path.join(chatsDir, sessionInfo.fileName);
diff --git a/packages/cli/src/utils/sessions.test.ts b/packages/cli/src/utils/sessions.test.ts
index e927efffe3..e616975fd1 100644
--- a/packages/cli/src/utils/sessions.test.ts
+++ b/packages/cli/src/utils/sessions.test.ts
@@ -40,7 +40,7 @@ describe('listSessions', () => {
// Create mock config
mockConfig = {
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/test-project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/test-workspace'),
},
getSessionId: vi.fn().mockReturnValue('current-session-id'),
} as unknown as Config;
@@ -340,7 +340,7 @@ describe('deleteSession', () => {
// Create mock config
mockConfig = {
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/test-project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/test-workspace'),
},
getSessionId: vi.fn().mockReturnValue('current-session-id'),
} as unknown as Config;
diff --git a/packages/cli/src/utils/skillUtils.test.ts b/packages/cli/src/utils/skillUtils.test.ts
index c769f22401..cf64314c58 100644
--- a/packages/cli/src/utils/skillUtils.test.ts
+++ b/packages/cli/src/utils/skillUtils.test.ts
@@ -12,7 +12,7 @@ import { installSkill, linkSkill } from './skillUtils.js';
describe('skillUtils', () => {
let tempDir: string;
- const projectRoot = path.resolve(__dirname, '../../../../../');
+ const workspaceRoot = path.resolve(__dirname, '../../../../../');
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'skill-utils-test-'));
@@ -130,7 +130,7 @@ describe('skillUtils', () => {
});
it('should successfully install from a .skill file', async () => {
- const skillPath = path.join(projectRoot, 'weather-skill.skill');
+ const skillPath = path.join(workspaceRoot, 'weather-skill.skill');
// Ensure the file exists
const exists = await fs.stat(skillPath).catch(() => null);
diff --git a/packages/cli/src/utils/userStartupWarnings.test.ts b/packages/cli/src/utils/userStartupWarnings.test.ts
index 41ed061166..4cace870b0 100644
--- a/packages/cli/src/utils/userStartupWarnings.test.ts
+++ b/packages/cli/src/utils/userStartupWarnings.test.ts
@@ -82,10 +82,10 @@ describe('getUserStartupWarnings', () => {
);
});
- it('should not return a warning when running in a project directory', async () => {
- const projectDir = path.join(testRootDir, 'project');
- await fs.mkdir(projectDir);
- const warnings = await getUserStartupWarnings({}, projectDir);
+ it('should not return a warning when running in a workspace directory', async () => {
+ const workspaceDir = path.join(testRootDir, 'workspace');
+ await fs.mkdir(workspaceDir);
+ const warnings = await getUserStartupWarnings({}, workspaceDir);
expect(warnings.find((w) => w.id === 'home-directory')).toBeUndefined();
});
@@ -123,9 +123,9 @@ describe('getUserStartupWarnings', () => {
});
it('should not return a warning when running in a non-root directory', async () => {
- const projectDir = path.join(testRootDir, 'project');
- await fs.mkdir(projectDir);
- const warnings = await getUserStartupWarnings({}, projectDir);
+ const workspaceDir = path.join(testRootDir, 'workspace');
+ await fs.mkdir(workspaceDir);
+ const warnings = await getUserStartupWarnings({}, workspaceDir);
expect(warnings.find((w) => w.id === 'root-directory')).toBeUndefined();
});
});
@@ -151,10 +151,10 @@ describe('getUserStartupWarnings', () => {
priority: WarningPriority.High,
};
vi.mocked(getCompatibilityWarnings).mockReturnValue([compWarning]);
- const projectDir = path.join(testRootDir, 'project');
- await fs.mkdir(projectDir);
+ const workspaceDir = path.join(testRootDir, 'workspace');
+ await fs.mkdir(workspaceDir);
- const warnings = await getUserStartupWarnings({}, projectDir);
+ const warnings = await getUserStartupWarnings({}, workspaceDir);
expect(warnings).toContainEqual(compWarning);
});
@@ -165,12 +165,12 @@ describe('getUserStartupWarnings', () => {
priority: WarningPriority.High,
};
vi.mocked(getCompatibilityWarnings).mockReturnValue([compWarning]);
- const projectDir = path.join(testRootDir, 'project');
- await fs.mkdir(projectDir);
+ const workspaceDir = path.join(testRootDir, 'workspace');
+ await fs.mkdir(workspaceDir);
const warnings = await getUserStartupWarnings(
{ ui: { showCompatibilityWarnings: false } },
- projectDir,
+ workspaceDir,
);
expect(warnings).not.toContainEqual(compWarning);
});
diff --git a/packages/cli/src/utils/windowTitle.test.ts b/packages/cli/src/utils/windowTitle.test.ts
index 853ede8db0..3f7dd3cdf1 100644
--- a/packages/cli/src/utils/windowTitle.test.ts
+++ b/packages/cli/src/utils/windowTitle.test.ts
@@ -23,11 +23,11 @@ describe('computeTerminalTitle', () => {
streamingState: StreamingState.Idle,
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: false,
useDynamicTitle: true,
} as TerminalTitleOptions,
- expected: '◇ Ready (my-project)',
+ expected: '◇ Ready (my-workspace)',
},
{
description: 'legacy title when useDynamicTitle is false',
@@ -35,11 +35,11 @@ describe('computeTerminalTitle', () => {
streamingState: StreamingState.Responding,
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: true,
useDynamicTitle: false,
} as TerminalTitleOptions,
- expected: 'Gemini CLI (my-project)'.padEnd(80, ' '),
+ expected: 'Gemini CLI (my-workspace)'.padEnd(80, ' '),
exact: true,
},
{
@@ -50,11 +50,11 @@ describe('computeTerminalTitle', () => {
thoughtSubject: 'Reading files',
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: false,
useDynamicTitle: true,
} as TerminalTitleOptions,
- expected: '✦ Working… (my-project)',
+ expected: '✦ Working… (my-workspace)',
},
{
description:
@@ -64,11 +64,11 @@ describe('computeTerminalTitle', () => {
thoughtSubject: 'Short thought',
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: true,
useDynamicTitle: true,
} as TerminalTitleOptions,
- expected: '✦ Short thought (my-project)',
+ expected: '✦ Short thought (my-workspace)',
},
{
description:
@@ -78,11 +78,11 @@ describe('computeTerminalTitle', () => {
thoughtSubject: undefined,
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: true,
useDynamicTitle: true,
} as TerminalTitleOptions,
- expected: '✦ Working… (my-project)'.padEnd(80, ' '),
+ expected: '✦ Working… (my-workspace)'.padEnd(80, ' '),
exact: true,
},
{
@@ -91,11 +91,11 @@ describe('computeTerminalTitle', () => {
streamingState: StreamingState.Idle,
isConfirming: true,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: false,
useDynamicTitle: true,
} as TerminalTitleOptions,
- expected: '✋ Action Required (my-project)',
+ expected: '✋ Action Required (my-workspace)',
},
{
description: 'silent working state',
@@ -103,11 +103,11 @@ describe('computeTerminalTitle', () => {
streamingState: StreamingState.Responding,
isConfirming: false,
isSilentWorking: true,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: false,
useDynamicTitle: true,
} as TerminalTitleOptions,
- expected: '⏲ Working… (my-project)',
+ expected: '⏲ Working… (my-workspace)',
},
])('should return $description', ({ args, expected, exact }) => {
const title = computeTerminalTitle(args);
@@ -126,12 +126,12 @@ describe('computeTerminalTitle', () => {
thoughtSubject: longThought,
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: true,
useDynamicTitle: true,
});
- expect(title).not.toContain('(my-project)');
+ expect(title).not.toContain('(my-workspace)');
expect(title).toContain('✦ AAAAAAAAAAAAAAAA');
expect(title.length).toBe(80);
});
@@ -143,7 +143,7 @@ describe('computeTerminalTitle', () => {
thoughtSubject: longThought,
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: true,
useDynamicTitle: true,
});
@@ -159,7 +159,7 @@ describe('computeTerminalTitle', () => {
thoughtSubject: 'BadTitle\x00 With\x07Control\x1BChars',
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: true,
useDynamicTitle: true,
});
@@ -178,13 +178,13 @@ describe('computeTerminalTitle', () => {
streamingState: StreamingState.Idle,
isConfirming: false,
isSilentWorking: false,
- folderName: 'my-project',
+ folderName: 'my-workspace',
showThoughts: false,
useDynamicTitle: true,
});
expect(title).toContain('◇ Ready (EnvOverride)');
- expect(title).not.toContain('my-project');
+ expect(title).not.toContain('my-workspace');
expect(title.length).toBe(80);
});
@@ -196,7 +196,7 @@ describe('computeTerminalTitle', () => {
},
{
name: 'CLI_TITLE',
- folderName: 'my-project',
+ folderName: 'my-workspace',
envTitle: 'B'.repeat(100),
expected: '◇ Ready (BBBBB',
},
diff --git a/packages/cli/src/zed-integration/acpResume.test.ts b/packages/cli/src/zed-integration/acpResume.test.ts
index 54c04a0ff3..8ad043fa56 100644
--- a/packages/cli/src/zed-integration/acpResume.test.ts
+++ b/packages/cli/src/zed-integration/acpResume.test.ts
@@ -89,7 +89,7 @@ describe('GeminiAgent Session Resume', () => {
getChat: vi.fn().mockReturnValue({}),
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/workspace'),
},
getApprovalMode: vi.fn().mockReturnValue('default'),
isPlanEnabled: vi.fn().mockReturnValue(false),
diff --git a/packages/core/src/agents/registry_acknowledgement.test.ts b/packages/core/src/agents/registry_acknowledgement.test.ts
index 5ac563091d..8cad92a80b 100644
--- a/packages/core/src/agents/registry_acknowledgement.test.ts
+++ b/packages/core/src/agents/registry_acknowledgement.test.ts
@@ -62,14 +62,16 @@ describe('AgentRegistry Acknowledgement', () => {
// Ensure we are in trusted folder mode for project agents to load
vi.spyOn(config, 'isTrustedFolder').mockReturnValue(true);
vi.spyOn(config, 'getFolderTrust').mockReturnValue(true);
- vi.spyOn(config, 'getProjectRoot').mockReturnValue('/project');
+ vi.spyOn(config, 'getWorkspaceRoot').mockReturnValue('/project');
+ vi.spyOn(config, 'getWorkspaceContext').mockReturnValue({
+ targetDir: '/project',
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } as any);
vi.spyOn(config, 'getAcknowledgedAgentsService').mockReturnValue(
ackService,
);
- // We cannot easily spy on storage.getProjectAgentsDir if it's a property/getter unless we cast to any or it's a method
- // Assuming it's a method on Storage class
- vi.spyOn(config.storage, 'getProjectAgentsDir').mockReturnValue(
+ vi.spyOn(config.storage, 'getWorkspaceAgentsDir').mockReturnValue(
'/project/.gemini/agents',
);
vi.spyOn(config, 'isAgentsEnabled').mockReturnValue(true);
diff --git a/packages/core/src/commands/memory.test.ts b/packages/core/src/commands/memory.test.ts
index 18c2b07f49..dda4494207 100644
--- a/packages/core/src/commands/memory.test.ts
+++ b/packages/core/src/commands/memory.test.ts
@@ -136,7 +136,7 @@ describe('memory commands', () => {
if (result.type === 'message') {
expect(result.messageType).toBe('info');
expect(result.content).toBe(
- 'Memory refreshed successfully. Loaded 33 characters from 2 file(s).',
+ 'Memory refreshed successfully. Loaded 35 characters from 2 file(s).',
);
}
});
diff --git a/packages/core/src/commands/restore.test.ts b/packages/core/src/commands/restore.test.ts
index 4dcba5dd87..2158f0f720 100644
--- a/packages/core/src/commands/restore.test.ts
+++ b/packages/core/src/commands/restore.test.ts
@@ -63,7 +63,7 @@ describe('performRestore', () => {
expect(result.value).toEqual({
type: 'message',
messageType: 'info',
- content: 'Restored project to the state before the tool call.',
+ content: 'Restored workspace to the state before the tool call.',
});
expect(result.done).toBe(false);
@@ -138,7 +138,7 @@ describe('performRestore', () => {
expect(messageResult.value).toEqual({
type: 'message',
messageType: 'info',
- content: 'Restored project to the state before the tool call.',
+ content: 'Restored workspace to the state before the tool call.',
});
expect(messageResult.done).toBe(false);
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
index ad8af8656c..90742ccc55 100644
--- a/packages/core/src/config/config.test.ts
+++ b/packages/core/src/config/config.test.ts
@@ -47,6 +47,24 @@ import { ToolRegistry } from '../tools/tool-registry.js';
import { ACTIVATE_SKILL_TOOL_NAME } from '../tools/tool-names.js';
import type { SkillDefinition } from '../skills/skillLoader.js';
import type { McpClientManager } from '../tools/mcp-client-manager.js';
+vi.mock('./storage.js', async (importOriginal) => {
+ const actual = await importOriginal();
+ actual.Storage.prototype.initialize = vi.fn().mockResolvedValue(undefined);
+ actual.Storage.prototype.getWorkspaceTempDir = vi
+ .fn()
+ .mockReturnValue('/tmp/workspace');
+ actual.Storage.prototype.getWorkspaceTempPlansDir = vi
+ .fn()
+ .mockReturnValue('/tmp/workspace/plans');
+ actual.Storage.prototype.getPlansDir = vi
+ .fn()
+ .mockReturnValue('/tmp/workspace/plans');
+ actual.Storage.prototype.getProjectIdentifier = vi
+ .fn()
+ .mockReturnValue('test-project');
+ return actual;
+});
+
import { DEFAULT_MODEL_CONFIGS } from './defaultModelConfigs.js';
import {
DEFAULT_GEMINI_MODEL,
@@ -2763,7 +2781,7 @@ describe('Config JIT Initialization', () => {
expect(config.getUserMemory()).toEqual({
global: 'Global Memory',
extension: 'Extension Memory',
- project: 'Environment Memory\n\nMCP Instructions',
+ workspace: 'Environment Memory\n\nMCP Instructions',
});
// Verify state update (delegated to ContextManager)
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index eb0ec93e7a..84428d504f 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -2392,7 +2392,7 @@ export class Config implements McpContext {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
+ const projectTempDir = this.storage.getWorkspaceTempDir();
const resolvedTempDir = realpath(projectTempDir);
return isSubpath(resolvedTempDir, resolvedPath);
diff --git a/packages/core/src/config/memory.test.ts b/packages/core/src/config/memory.test.ts
index dfc4307f4f..bf11528e5d 100644
--- a/packages/core/src/config/memory.test.ts
+++ b/packages/core/src/config/memory.test.ts
@@ -38,7 +38,7 @@ extension content`,
it('should return content with headers even if only project memory is present', () => {
expect(flattenMemory({ project: 'project content' })).toBe(
- `--- Project ---
+ `--- Workspace ---
project content`,
);
});
@@ -50,7 +50,7 @@ project content`,
});
expect(result).toContain('--- Global ---');
expect(result).toContain('global content');
- expect(result).toContain('--- Project ---');
+ expect(result).toContain('--- Workspace ---');
expect(result).toContain('project content');
expect(result).not.toContain('--- Extension ---');
});
@@ -63,7 +63,7 @@ project content`,
});
expect(result).toContain('--- Global ---');
expect(result).toContain('--- Extension ---');
- expect(result).toContain('--- Project ---');
+ expect(result).toContain('--- Workspace ---');
expect(result).toBe(
`--- Global ---
global content
@@ -71,7 +71,7 @@ global content
--- Extension ---
extension content
---- Project ---
+--- Workspace ---
project content`,
);
});
@@ -86,7 +86,7 @@ project content`,
`--- Global ---
trimmed global
---- Project ---
+--- Workspace ---
project`,
);
});
diff --git a/packages/core/src/config/storage.test.ts b/packages/core/src/config/storage.test.ts
index 15b49d12f1..933d9156ee 100644
--- a/packages/core/src/config/storage.test.ts
+++ b/packages/core/src/config/storage.test.ts
@@ -34,7 +34,7 @@ vi.mock('./projectRegistry.js');
vi.mock('./storageMigration.js');
describe('Storage – initialize', () => {
- const projectRoot = '/tmp/project';
+ const workspaceRoot = '/tmp/project';
let storage: Storage;
beforeEach(() => {
@@ -42,16 +42,16 @@ describe('Storage – initialize', () => {
ProjectRegistry.prototype.getShortId = vi
.fn()
.mockReturnValue(PROJECT_SLUG);
- storage = new Storage(projectRoot);
+ storage = new Storage(workspaceRoot);
vi.clearAllMocks();
// Mock StorageMigration.migrateDirectory
vi.mocked(StorageMigration.migrateDirectory).mockResolvedValue(undefined);
});
- it('sets up the registry and performs migration if `getProjectTempDir` is called', async () => {
+ it('sets up the registry and performs migration if `getWorkspaceTempDir` is called', async () => {
await storage.initialize();
- expect(storage.getProjectTempDir()).toBe(
+ expect(storage.getWorkspaceTempDir()).toBe(
path.join(os.homedir(), GEMINI_DIR, 'tmp', PROJECT_SLUG),
);
@@ -60,14 +60,14 @@ describe('Storage – initialize', () => {
expect(vi.mocked(ProjectRegistry).prototype.initialize).toHaveBeenCalled();
expect(
vi.mocked(ProjectRegistry).prototype.getShortId,
- ).toHaveBeenCalledWith(projectRoot);
+ ).toHaveBeenCalledWith(workspaceRoot);
// Verify migration calls
// We can't easily get the hash here without repeating logic, but we can verify it's called twice
expect(StorageMigration.migrateDirectory).toHaveBeenCalledTimes(2);
// Verify identifier is set by checking a path
- expect(storage.getProjectTempDir()).toContain(PROJECT_SLUG);
+ expect(storage.getWorkspaceTempDir()).toContain(PROJECT_SLUG);
});
});
@@ -103,8 +103,8 @@ describe('Storage - Security', () => {
});
describe('Storage – additional helpers', () => {
- const projectRoot = '/tmp/project';
- const storage = new Storage(projectRoot);
+ const workspaceRoot = '/tmp/project';
+ const storage = new Storage(workspaceRoot);
beforeEach(() => {
ProjectRegistry.prototype.getShortId = vi
@@ -113,7 +113,7 @@ describe('Storage – additional helpers', () => {
});
it('getWorkspaceSettingsPath returns project/.gemini/settings.json', () => {
- const expected = path.join(projectRoot, GEMINI_DIR, 'settings.json');
+ const expected = path.join(workspaceRoot, GEMINI_DIR, 'settings.json');
expect(storage.getWorkspaceSettingsPath()).toBe(expected);
});
@@ -123,7 +123,7 @@ describe('Storage – additional helpers', () => {
});
it('getProjectCommandsDir returns project/.gemini/commands', () => {
- const expected = path.join(projectRoot, GEMINI_DIR, 'commands');
+ const expected = path.join(workspaceRoot, GEMINI_DIR, 'commands');
expect(storage.getProjectCommandsDir()).toBe(expected);
});
@@ -133,7 +133,7 @@ describe('Storage – additional helpers', () => {
});
it('getProjectSkillsDir returns project/.gemini/skills', () => {
- const expected = path.join(projectRoot, GEMINI_DIR, 'skills');
+ const expected = path.join(workspaceRoot, GEMINI_DIR, 'skills');
expect(storage.getProjectSkillsDir()).toBe(expected);
});
@@ -143,7 +143,7 @@ describe('Storage – additional helpers', () => {
});
it('getProjectAgentsDir returns project/.gemini/agents', () => {
- const expected = path.join(projectRoot, GEMINI_DIR, 'agents');
+ const expected = path.join(workspaceRoot, GEMINI_DIR, 'agents');
expect(storage.getProjectAgentsDir()).toBe(expected);
});
@@ -163,19 +163,19 @@ describe('Storage – additional helpers', () => {
it('getProjectTempPlansDir returns ~/.gemini/tmp//plans when no sessionId is provided', async () => {
await storage.initialize();
- const tempDir = storage.getProjectTempDir();
+ const tempDir = storage.getWorkspaceTempDir();
const expected = path.join(tempDir, 'plans');
expect(storage.getProjectTempPlansDir()).toBe(expected);
});
it('getProjectTempPlansDir returns ~/.gemini/tmp///plans when sessionId is provided', async () => {
const sessionId = 'test-session-id';
- const storageWithSession = new Storage(projectRoot, sessionId);
+ const storageWithSession = new Storage(workspaceRoot, sessionId);
ProjectRegistry.prototype.getShortId = vi
.fn()
.mockReturnValue(PROJECT_SLUG);
await storageWithSession.initialize();
- const tempDir = storageWithSession.getProjectTempDir();
+ const tempDir = storageWithSession.getWorkspaceTempDir();
const expected = path.join(tempDir, sessionId, 'plans');
expect(storageWithSession.getProjectTempPlansDir()).toBe(expected);
});
@@ -273,29 +273,29 @@ describe('Storage – additional helpers', () => {
{
name: 'custom relative path',
customDir: '.my-plans',
- expected: path.resolve(projectRoot, '.my-plans'),
+ expected: path.resolve(workspaceRoot, '.my-plans'),
},
{
name: 'custom absolute path outside throws',
customDir: '/absolute/path/to/plans',
expected: '',
expectedError:
- "Custom plans directory '/absolute/path/to/plans' resolves to '/absolute/path/to/plans', which is outside the project root '/tmp/project'.",
+ "Custom plans directory '/absolute/path/to/plans' resolves to '/absolute/path/to/plans', which is outside the workspace root '/tmp/project'.",
},
{
name: 'absolute path that happens to be inside project root',
- customDir: path.join(projectRoot, 'internal-plans'),
- expected: path.join(projectRoot, 'internal-plans'),
+ customDir: path.join(workspaceRoot, 'internal-plans'),
+ expected: path.join(workspaceRoot, 'internal-plans'),
},
{
name: 'relative path that stays within project root',
customDir: 'subdir/../plans',
- expected: path.resolve(projectRoot, 'plans'),
+ expected: path.resolve(workspaceRoot, 'plans'),
},
{
name: 'dot path',
customDir: '.',
- expected: projectRoot,
+ expected: workspaceRoot,
},
{
name: 'default behavior when customDir is undefined',
@@ -307,12 +307,12 @@ describe('Storage – additional helpers', () => {
customDir: '../escaped-plans',
expected: '',
expectedError:
- "Custom plans directory '../escaped-plans' resolves to '/tmp/escaped-plans', which is outside the project root '/tmp/project'.",
+ "Custom plans directory '../escaped-plans' resolves to '/tmp/escaped-plans', which is outside the workspace root '/tmp/project'.",
},
{
name: 'hidden directory starting with ..',
customDir: '..plans',
- expected: path.resolve(projectRoot, '..plans'),
+ expected: path.resolve(workspaceRoot, '..plans'),
},
{
name: 'security escape via symbolic link throws',
@@ -328,7 +328,7 @@ describe('Storage – additional helpers', () => {
},
expected: '',
expectedError:
- "Custom plans directory 'symlink-to-outside' resolves to '/outside/project/root', which is outside the project root '/tmp/project'.",
+ "Custom plans directory 'symlink-to-outside' resolves to '/outside/project/root', which is outside the workspace root '/tmp/project'.",
},
];
diff --git a/packages/core/src/core/__snapshots__/prompts.test.ts.snap b/packages/core/src/core/__snapshots__/prompts.test.ts.snap
index 6fb439ff4c..f995a0f54f 100644
--- a/packages/core/src/core/__snapshots__/prompts.test.ts.snap
+++ b/packages/core/src/core/__snapshots__/prompts.test.ts.snap
@@ -63,7 +63,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -231,7 +231,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -518,7 +518,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -686,7 +686,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -1572,7 +1572,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -1735,7 +1735,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -1824,7 +1824,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
# macOS Seatbelt
- You are running under macos seatbelt with limited access to files outside the project directory or system temp directory, and with limited access to host system resources such as ports. If you encounter failures that could be due to macOS Seatbelt (e.g. if a command fails with 'Operation not permitted' or similar error), as you report the error to the user, also explain why you think it could be due to macOS Seatbelt, and how the user may need to adjust their Seatbelt profile."
+ You are running under macos seatbelt with limited access to files outside the workspace directory or system temp directory, and with limited access to host system resources such as ports. If you encounter failures that could be due to macOS Seatbelt (e.g. if a command fails with 'Operation not permitted' or similar error), as you report the error to the user, also explain why you think it could be due to macOS Seatbelt, and how the user may need to adjust their Seatbelt profile."
`;
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=true 1`] = `
@@ -1890,7 +1890,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -1979,7 +1979,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
# Sandbox
- You are running in a sandbox container with limited access to files outside the project directory or system temp directory, and with limited access to host system resources such as ports. If you encounter failures that could be due to sandboxing (e.g. if a command fails with 'Operation not permitted' or similar error), when you report the error to the user, also explain why you think it could be due to sandboxing, and how the user may need to adjust their sandbox configuration."
+ You are running in a sandbox container with limited access to files outside the workspace directory or system temp directory, and with limited access to host system resources such as ports. If you encounter failures that could be due to sandboxing (e.g. if a command fails with 'Operation not permitted' or similar error), when you report the error to the user, also explain why you think it could be due to sandboxing, and how the user may need to adjust their sandbox configuration."
`;
exports[`Core System Prompt (prompts.ts) > should include correct sandbox instructions for SANDBOX=undefined 1`] = `
@@ -2045,7 +2045,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -2196,7 +2196,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -2347,7 +2347,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -2490,7 +2490,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -2640,7 +2640,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -3032,7 +3032,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -3183,7 +3183,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -3446,7 +3446,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
@@ -3597,7 +3597,7 @@ Operate as a **strategic orchestrator**. Your own context window is your most pr
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
-- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
+- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the workspace").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts
index c910556ca8..c84436bb61 100644
--- a/packages/core/src/core/client.test.ts
+++ b/packages/core/src/core/client.test.ts
@@ -231,6 +231,7 @@ describe('Gemini Client (client.ts)', () => {
getDebugMode: vi.fn().mockReturnValue(false),
getWorkspaceContext: vi.fn().mockReturnValue({
getDirectories: vi.fn().mockReturnValue(['/test/dir']),
+ targetDir: '/test/project/root',
}),
getGeminiClient: vi.fn(),
getModelRouterService: vi
@@ -243,10 +244,10 @@ describe('Gemini Client (client.ts)', () => {
getSkipNextSpeakerCheck: vi.fn().mockReturnValue(false),
getShowModelInfoInChat: vi.fn().mockReturnValue(false),
getContinueOnFailedApiCall: vi.fn(),
- getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceRoot: vi.fn().mockReturnValue('/test/project/root'),
getIncludeDirectoryTree: vi.fn().mockReturnValue(true),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/test/temp'),
},
getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
getBaseLlmClient: vi.fn().mockReturnValue({
@@ -371,7 +372,7 @@ describe('Gemini Client (client.ts)', () => {
expect(history[0].role).toBe('user');
expect(history[0].parts?.[0]?.text).toContain('This is the Gemini CLI');
expect(history[0].parts?.[0]?.text).toContain(
- "The project's temporary directory is:",
+ "The workspace's temporary directory is:",
);
// The subsequent messages should be the extra history
diff --git a/packages/core/src/core/coreToolScheduler.test.ts b/packages/core/src/core/coreToolScheduler.test.ts
index 6bdad0dddb..6905e7bac6 100644
--- a/packages/core/src/core/coreToolScheduler.test.ts
+++ b/packages/core/src/core/coreToolScheduler.test.ts
@@ -277,7 +277,7 @@ function createMockConfig(overrides: Partial = {}): Config {
},
}),
storage: {
- getProjectTempDir: () => '/tmp',
+ getWorkspaceTempDir: () => '/tmp',
},
getTruncateToolOutputThreshold: () =>
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
diff --git a/packages/core/src/core/geminiChat.test.ts b/packages/core/src/core/geminiChat.test.ts
index 770a594bda..620ef1a72d 100644
--- a/packages/core/src/core/geminiChat.test.ts
+++ b/packages/core/src/core/geminiChat.test.ts
@@ -144,9 +144,12 @@ describe('GeminiChat', () => {
getQuotaErrorOccurred: vi.fn().mockReturnValue(false),
setQuotaErrorOccurred: vi.fn(),
flashFallbackHandler: undefined,
- getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceContext: vi.fn().mockReturnValue({
+ targetDir: '/test/project/root',
+ }),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/test/temp'),
},
getToolRegistry: vi.fn().mockReturnValue({
getTool: vi.fn(),
diff --git a/packages/core/src/core/geminiChat_network_retry.test.ts b/packages/core/src/core/geminiChat_network_retry.test.ts
index 161cadaf52..9ecd15e00b 100644
--- a/packages/core/src/core/geminiChat_network_retry.test.ts
+++ b/packages/core/src/core/geminiChat_network_retry.test.ts
@@ -87,9 +87,12 @@ describe('GeminiChat Network Retries', () => {
getActiveModel: vi.fn().mockReturnValue('gemini-pro'),
setActiveModel: vi.fn(),
getQuotaErrorOccurred: vi.fn().mockReturnValue(false),
- getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceContext: vi.fn().mockReturnValue({
+ targetDir: '/test/project/root',
+ }),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/test/temp'),
},
getToolRegistry: vi.fn().mockReturnValue({ getTool: vi.fn() }),
getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
diff --git a/packages/core/src/core/logger.test.ts b/packages/core/src/core/logger.test.ts
index 498aa85ca1..968e91f1aa 100644
--- a/packages/core/src/core/logger.test.ts
+++ b/packages/core/src/core/logger.test.ts
@@ -28,6 +28,9 @@ import type { Content } from '@google/genai';
import os from 'node:os';
import { GEMINI_DIR } from '../utils/paths.js';
import { debugLogger } from '../utils/debugLogger.js';
+import { ProjectRegistry } from '../config/projectRegistry.js';
+
+vi.mock('../config/projectRegistry.js');
const PROJECT_SLUG = 'project-slug';
const TMP_DIR_NAME = 'tmp';
@@ -83,6 +86,12 @@ describe('Logger', () => {
await cleanupLogAndCheckpointFiles();
// Ensure the directory exists for the test
await fs.mkdir(TEST_GEMINI_DIR, { recursive: true });
+
+ ProjectRegistry.prototype.initialize = vi.fn().mockResolvedValue(undefined);
+ ProjectRegistry.prototype.getShortId = vi
+ .fn()
+ .mockReturnValue(PROJECT_SLUG);
+
logger = new Logger(testSessionId, new Storage(process.cwd()));
await logger.initialize();
});
diff --git a/packages/core/src/core/logger.ts b/packages/core/src/core/logger.ts
index 362601f895..05dd99df90 100644
--- a/packages/core/src/core/logger.ts
+++ b/packages/core/src/core/logger.ts
@@ -145,7 +145,7 @@ export class Logger {
}
await this.storage.initialize();
- this.geminiDir = this.storage.getProjectTempDir();
+ this.geminiDir = this.storage.getWorkspaceTempDir();
this.logFilePath = path.join(this.geminiDir, LOG_FILE_NAME);
try {
diff --git a/packages/core/src/core/prompts-substitution.test.ts b/packages/core/src/core/prompts-substitution.test.ts
index 388229d948..ebff6d3082 100644
--- a/packages/core/src/core/prompts-substitution.test.ts
+++ b/packages/core/src/core/prompts-substitution.test.ts
@@ -32,7 +32,7 @@ describe('Core System Prompt Substitution', () => {
}),
getEnableShellOutputEfficiency: vi.fn().mockReturnValue(true),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
},
isInteractive: vi.fn().mockReturnValue(true),
isInteractiveShellEnabled: vi.fn().mockReturnValue(true),
diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts
index 3720490ce0..d8045f0f17 100644
--- a/packages/core/src/core/prompts.test.ts
+++ b/packages/core/src/core/prompts.test.ts
@@ -89,7 +89,7 @@ describe('Core System Prompt (prompts.ts)', () => {
}),
getEnableShellOutputEfficiency: vi.fn().mockReturnValue(true),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
getPlansDir: vi.fn().mockReturnValue('/tmp/project-temp/plans'),
},
isInteractive: vi.fn().mockReturnValue(true),
@@ -384,7 +384,7 @@ describe('Core System Prompt (prompts.ts)', () => {
}),
getEnableShellOutputEfficiency: vi.fn().mockReturnValue(true),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
},
isInteractive: vi.fn().mockReturnValue(false),
isInteractiveShellEnabled: vi.fn().mockReturnValue(false),
diff --git a/packages/core/src/hooks/hookRegistry.test.ts b/packages/core/src/hooks/hookRegistry.test.ts
index d8157f4ef5..e80952d6bc 100644
--- a/packages/core/src/hooks/hookRegistry.test.ts
+++ b/packages/core/src/hooks/hookRegistry.test.ts
@@ -74,7 +74,10 @@ describe('HookRegistry', () => {
getProjectHooks: vi.fn().mockReturnValue({}),
getDisabledHooks: vi.fn().mockReturnValue([]),
isTrustedFolder: vi.fn().mockReturnValue(true),
- getProjectRoot: vi.fn().mockReturnValue('/project'),
+ getWorkspaceRoot: vi.fn().mockReturnValue('/project'),
+ getWorkspaceContext: vi.fn().mockReturnValue({
+ targetDir: '/project',
+ }),
} as unknown as Config;
hookRegistry = new HookRegistry(mockConfig);
@@ -121,7 +124,7 @@ describe('HookRegistry', () => {
expect(hookRegistry.getAllHooks()).toHaveLength(0);
expect(mockDebugLogger.warn).toHaveBeenCalledWith(
- 'Project hooks disabled because the folder is not trusted.',
+ 'Workspace hooks disabled because the folder is not trusted.',
);
});
@@ -740,7 +743,7 @@ describe('HookRegistry', () => {
expect(mockCoreEvents.emitFeedback).toHaveBeenCalledWith(
'warning',
expect.stringContaining(
- 'WARNING: The following project-level hooks have been detected',
+ 'WARNING: The following workspace-level hooks have been detected',
),
);
expect(mockTrustedHooksManager.trustHooks).toHaveBeenCalledWith(
diff --git a/packages/core/src/prompts/promptProvider.test.ts b/packages/core/src/prompts/promptProvider.test.ts
index b74f159e4f..6af4c2514f 100644
--- a/packages/core/src/prompts/promptProvider.test.ts
+++ b/packages/core/src/prompts/promptProvider.test.ts
@@ -42,7 +42,7 @@ describe('PromptProvider', () => {
}),
getEnableShellOutputEfficiency: vi.fn().mockReturnValue(true),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
getPlansDir: vi.fn().mockReturnValue('/tmp/project-temp/plans'),
},
isInteractive: vi.fn().mockReturnValue(true),
diff --git a/packages/core/src/scheduler/tool-executor.test.ts b/packages/core/src/scheduler/tool-executor.test.ts
index 0d77204f4e..5aff183221 100644
--- a/packages/core/src/scheduler/tool-executor.test.ts
+++ b/packages/core/src/scheduler/tool-executor.test.ts
@@ -254,7 +254,7 @@ describe('ToolExecutor', () => {
it('should truncate large shell output', async () => {
// 1. Setup Config for Truncation
vi.spyOn(config, 'getTruncateToolOutputThreshold').mockReturnValue(10);
- vi.spyOn(config.storage, 'getProjectTempDir').mockReturnValue('/tmp');
+ vi.spyOn(config.storage, 'getWorkspaceTempDir').mockReturnValue('/tmp');
const mockTool = new MockTool({ name: SHELL_TOOL_NAME });
const invocation = mockTool.build({});
diff --git a/packages/core/src/scheduler/tool-executor.ts b/packages/core/src/scheduler/tool-executor.ts
index 7903266fe1..282f31e620 100644
--- a/packages/core/src/scheduler/tool-executor.ts
+++ b/packages/core/src/scheduler/tool-executor.ts
@@ -237,7 +237,7 @@ export class ToolExecutor {
content,
toolName,
callId,
- this.config.storage.getProjectTempDir(),
+ this.config.storage.getWorkspaceTempDir(),
this.config.getSessionId(),
);
outputFile = savedPath;
diff --git a/packages/core/src/services/FolderTrustDiscoveryService.test.ts b/packages/core/src/services/FolderTrustDiscoveryService.test.ts
index b6d7d7734a..31ce76752e 100644
--- a/packages/core/src/services/FolderTrustDiscoveryService.test.ts
+++ b/packages/core/src/services/FolderTrustDiscoveryService.test.ts
@@ -96,16 +96,16 @@ describe('FolderTrustDiscoveryService', () => {
const results = await FolderTrustDiscoveryService.discover(tempDir);
expect(results.securityWarnings).toContain(
- 'This project auto-approves certain tools (tools.allowed).',
+ 'This workspace auto-approves certain tools (tools.allowed).',
);
expect(results.securityWarnings).toContain(
- 'This project enables autonomous agents (enableAgents).',
+ 'This workspace enables autonomous agents (enableAgents).',
);
expect(results.securityWarnings).toContain(
- 'This project attempts to disable folder trust (security.folderTrust.enabled).',
+ 'This workspace attempts to disable folder trust (security.folderTrust.enabled).',
);
expect(results.securityWarnings).toContain(
- 'This project disables the security sandbox (tools.sandbox).',
+ 'This workspace disables the security sandbox (tools.sandbox).',
);
});
diff --git a/packages/core/src/services/chatCompressionService.test.ts b/packages/core/src/services/chatCompressionService.test.ts
index 4ddd38e25c..19951424a9 100644
--- a/packages/core/src/services/chatCompressionService.test.ts
+++ b/packages/core/src/services/chatCompressionService.test.ts
@@ -186,7 +186,7 @@ describe('ChatCompressionService', () => {
getNextCompressionTruncationId: vi.fn().mockReturnValue(1),
getTruncateToolOutputThreshold: vi.fn().mockReturnValue(40000),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue(testTempDir),
+ getWorkspaceTempDir: vi.fn().mockReturnValue(testTempDir),
},
} as unknown as Config;
diff --git a/packages/core/src/services/chatCompressionService.ts b/packages/core/src/services/chatCompressionService.ts
index 5303a1a82a..1faf1e0dc2 100644
--- a/packages/core/src/services/chatCompressionService.ts
+++ b/packages/core/src/services/chatCompressionService.ts
@@ -183,7 +183,7 @@ async function truncateHistoryToBudget(
contentStr,
part.functionResponse.name ?? 'unknown_tool',
config.getNextCompressionTruncationId(),
- config.storage.getProjectTempDir(),
+ config.storage.getWorkspaceTempDir(),
);
const truncatedMessage = formatTruncatedToolOutput(
diff --git a/packages/core/src/services/chatRecordingService.test.ts b/packages/core/src/services/chatRecordingService.test.ts
index 086a7b6ff5..bc1199f72b 100644
--- a/packages/core/src/services/chatRecordingService.test.ts
+++ b/packages/core/src/services/chatRecordingService.test.ts
@@ -17,7 +17,7 @@ import { CoreToolCallStatus } from '../scheduler/types.js';
import type { Content, Part } from '@google/genai';
import { ChatRecordingService } from './chatRecordingService.js';
import type { Config } from '../config/config.js';
-import { getProjectHash } from '../utils/paths.js';
+import { getWorkspaceHash } from '../utils/paths.js';
vi.mock('../utils/paths.js');
vi.mock('node:crypto', () => {
@@ -44,9 +44,12 @@ describe('ChatRecordingService', () => {
mockConfig = {
getSessionId: vi.fn().mockReturnValue('test-session-id'),
- getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceContext: vi.fn().mockReturnValue({
+ targetDir: '/test/project/root',
+ }),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue(testTempDir),
+ getWorkspaceTempDir: vi.fn().mockReturnValue(testTempDir),
},
getModel: vi.fn().mockReturnValue('gemini-pro'),
getDebugMode: vi.fn().mockReturnValue(false),
@@ -59,7 +62,7 @@ describe('ChatRecordingService', () => {
}),
} as unknown as Config;
- vi.mocked(getProjectHash).mockReturnValue('test-project-hash');
+ vi.mocked(getWorkspaceHash).mockReturnValue('test-workspace-hash');
chatRecordingService = new ChatRecordingService(mockConfig);
});
diff --git a/packages/core/src/services/fileDiscoveryService.test.ts b/packages/core/src/services/fileDiscoveryService.test.ts
index 7fbdcdead8..9f9536c16d 100644
--- a/packages/core/src/services/fileDiscoveryService.test.ts
+++ b/packages/core/src/services/fileDiscoveryService.test.ts
@@ -13,10 +13,10 @@ import { GEMINI_IGNORE_FILE_NAME } from '../config/constants.js';
describe('FileDiscoveryService', () => {
let testRootDir: string;
- let projectRoot: string;
+ let workspaceRoot: string;
async function createTestFile(filePath: string, content = '') {
- const fullPath = path.join(projectRoot, filePath);
+ const fullPath = path.join(workspaceRoot, filePath);
await fs.mkdir(path.dirname(fullPath), { recursive: true });
await fs.writeFile(fullPath, content);
return fullPath;
@@ -26,8 +26,8 @@ describe('FileDiscoveryService', () => {
testRootDir = await fs.mkdtemp(
path.join(os.tmpdir(), 'file-discovery-test-'),
);
- projectRoot = path.join(testRootDir, 'project');
- await fs.mkdir(projectRoot, { recursive: true });
+ workspaceRoot = path.join(testRootDir, 'project');
+ await fs.mkdir(workspaceRoot, { recursive: true });
});
afterEach(async () => {
@@ -36,10 +36,10 @@ describe('FileDiscoveryService', () => {
describe('initialization', () => {
it('should initialize git ignore parser by default in a git repo', async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', 'node_modules/');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
// Let's check the effect of the parser instead of mocking it.
expect(service.shouldIgnoreFile('node_modules/foo.js')).toBe(true);
expect(service.shouldIgnoreFile('src/foo.js')).toBe(false);
@@ -48,7 +48,7 @@ describe('FileDiscoveryService', () => {
it('should not load git repo patterns when not in a git repo', async () => {
// No .git directory
await createTestFile('.gitignore', 'node_modules/');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
// .gitignore is not loaded in non-git repos
expect(service.shouldIgnoreFile('node_modules/foo.js')).toBe(false);
@@ -56,7 +56,7 @@ describe('FileDiscoveryService', () => {
it('should load .geminiignore patterns even when not in a git repo', async () => {
await createTestFile(GEMINI_IGNORE_FILE_NAME, 'secrets.txt');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
expect(service.shouldIgnoreFile('secrets.txt')).toBe(true);
expect(service.shouldIgnoreFile('src/index.js')).toBe(false);
@@ -69,7 +69,7 @@ describe('FileDiscoveryService', () => {
'applyFilterFilesOptions',
);
const options = { respectGitIgnore: false };
- new FileDiscoveryService(projectRoot, options);
+ new FileDiscoveryService(workspaceRoot, options);
expect(resolveSpy).toHaveBeenCalledWith(options);
});
@@ -79,7 +79,7 @@ describe('FileDiscoveryService', () => {
respectGeminiIgnore: false,
customIgnoreFilePaths: ['custom/.ignore'],
};
- const service = new FileDiscoveryService(projectRoot, options);
+ const service = new FileDiscoveryService(workspaceRoot, options);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const defaults = (service as any).defaultFilterFileOptions;
@@ -89,7 +89,7 @@ describe('FileDiscoveryService', () => {
});
it('should use defaults when options are not provided', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const defaults = (service as any).defaultFilterFileOptions;
@@ -99,7 +99,7 @@ describe('FileDiscoveryService', () => {
});
it('should partially override defaults', () => {
- const service = new FileDiscoveryService(projectRoot, {
+ const service = new FileDiscoveryService(workspaceRoot, {
respectGitIgnore: false,
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -112,7 +112,7 @@ describe('FileDiscoveryService', () => {
describe('filterFiles', () => {
beforeEach(async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', 'node_modules/\n.git/\ndist');
await createTestFile(GEMINI_IGNORE_FILE_NAME, 'logs/');
});
@@ -125,12 +125,12 @@ describe('FileDiscoveryService', () => {
'.git/config',
'dist/bundle.js',
'logs/latest.log',
- ].map((f) => path.join(projectRoot, f));
+ ].map((f) => path.join(workspaceRoot, f));
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
expect(service.filterFiles(files)).toEqual(
- ['src/index.ts', 'README.md'].map((f) => path.join(projectRoot, f)),
+ ['src/index.ts', 'README.md'].map((f) => path.join(workspaceRoot, f)),
);
});
@@ -140,9 +140,9 @@ describe('FileDiscoveryService', () => {
'node_modules/package/index.js',
'.git/config',
'logs/latest.log',
- ].map((f) => path.join(projectRoot, f));
+ ].map((f) => path.join(workspaceRoot, f));
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const filtered = service.filterFiles(files, {
respectGitIgnore: false,
@@ -151,7 +151,7 @@ describe('FileDiscoveryService', () => {
expect(filtered).toEqual(
['src/index.ts', 'node_modules/package/index.js', '.git/config'].map(
- (f) => path.join(projectRoot, f),
+ (f) => path.join(workspaceRoot, f),
),
);
});
@@ -161,9 +161,9 @@ describe('FileDiscoveryService', () => {
'src/index.ts',
'node_modules/package/index.js',
'logs/latest.log',
- ].map((f) => path.join(projectRoot, f));
+ ].map((f) => path.join(workspaceRoot, f));
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const filtered = service.filterFiles(files, {
respectGitIgnore: true,
@@ -172,13 +172,13 @@ describe('FileDiscoveryService', () => {
expect(filtered).toEqual(
['src/index.ts', 'logs/latest.log'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
),
);
});
it('should handle empty file list', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
expect(service.filterFiles([])).toEqual([]);
});
@@ -186,7 +186,7 @@ describe('FileDiscoveryService', () => {
describe('filterFilesWithReport', () => {
beforeEach(async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', 'node_modules/');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '*.log');
});
@@ -197,23 +197,23 @@ describe('FileDiscoveryService', () => {
'node_modules/package/index.js',
'debug.log',
'README.md',
- ].map((f) => path.join(projectRoot, f));
+ ].map((f) => path.join(workspaceRoot, f));
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const report = service.filterFilesWithReport(files);
expect(report.filteredPaths).toEqual(
- ['src/index.ts', 'README.md'].map((f) => path.join(projectRoot, f)),
+ ['src/index.ts', 'README.md'].map((f) => path.join(workspaceRoot, f)),
);
expect(report.ignoredCount).toBe(2);
});
it('should handle no ignored files', () => {
const files = ['src/index.ts', 'README.md'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const report = service.filterFilesWithReport(files);
expect(report.filteredPaths).toEqual(files);
@@ -223,101 +223,101 @@ describe('FileDiscoveryService', () => {
describe('shouldGitIgnoreFile & shouldGeminiIgnoreFile', () => {
beforeEach(async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', 'node_modules/');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '*.log');
});
it('should return true for git-ignored files', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
expect(
service.shouldIgnoreFile(
- path.join(projectRoot, 'node_modules/package/index.js'),
+ path.join(workspaceRoot, 'node_modules/package/index.js'),
),
).toBe(true);
});
it('should return false for non-git-ignored files', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
expect(
- service.shouldIgnoreFile(path.join(projectRoot, 'src/index.ts')),
+ service.shouldIgnoreFile(path.join(workspaceRoot, 'src/index.ts')),
).toBe(false);
});
it('should return true for gemini-ignored files', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
expect(
- service.shouldIgnoreFile(path.join(projectRoot, 'debug.log')),
+ service.shouldIgnoreFile(path.join(workspaceRoot, 'debug.log')),
).toBe(true);
});
it('should return false for non-gemini-ignored files', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
expect(
- service.shouldIgnoreFile(path.join(projectRoot, 'src/index.ts')),
+ service.shouldIgnoreFile(path.join(workspaceRoot, 'src/index.ts')),
).toBe(false);
});
});
describe('edge cases', () => {
it('should handle relative project root paths', async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', 'ignored.txt');
const service = new FileDiscoveryService(
- path.relative(process.cwd(), projectRoot),
+ path.relative(process.cwd(), workspaceRoot),
);
expect(
- service.shouldIgnoreFile(path.join(projectRoot, 'ignored.txt')),
+ service.shouldIgnoreFile(path.join(workspaceRoot, 'ignored.txt')),
).toBe(true);
expect(
- service.shouldIgnoreFile(path.join(projectRoot, 'not-ignored.txt')),
+ service.shouldIgnoreFile(path.join(workspaceRoot, 'not-ignored.txt')),
).toBe(false);
});
it('should handle filterFiles with undefined options', async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', 'ignored.txt');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const files = ['src/index.ts', 'ignored.txt'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
expect(service.filterFiles(files, undefined)).toEqual([
- path.join(projectRoot, 'src/index.ts'),
+ path.join(workspaceRoot, 'src/index.ts'),
]);
});
});
describe('precedence (.geminiignore over .gitignore)', () => {
beforeEach(async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
});
it('should un-ignore a file in .geminiignore that is ignored in .gitignore', async () => {
await createTestFile('.gitignore', '*.txt');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '!important.txt');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const files = ['file.txt', 'important.txt'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
const filtered = service.filterFiles(files);
- expect(filtered).toEqual([path.join(projectRoot, 'important.txt')]);
+ expect(filtered).toEqual([path.join(workspaceRoot, 'important.txt')]);
});
it('should un-ignore a directory in .geminiignore that is ignored in .gitignore', async () => {
await createTestFile('.gitignore', 'logs/');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '!logs/');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const files = ['logs/app.log', 'other/app.log'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
const filtered = service.filterFiles(files);
@@ -328,9 +328,9 @@ describe('FileDiscoveryService', () => {
await createTestFile('.gitignore', '*.log');
await createTestFile(GEMINI_IGNORE_FILE_NAME, 'temp/');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const files = ['app.log', 'temp/file.txt'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
const filtered = service.filterFiles(files);
@@ -341,9 +341,9 @@ describe('FileDiscoveryService', () => {
await createTestFile('.gitignore', '*.txt');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '!important.txt');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const files = ['file.txt', 'important.txt'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
const filtered = service.filterFiles(files, {
@@ -358,9 +358,9 @@ describe('FileDiscoveryService', () => {
await createTestFile('.gitignore', '*.txt');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '!important.txt\ntemp/');
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const files = ['file.txt', 'important.txt', 'temp/file.js'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
const filtered = service.filterFiles(files, {
@@ -372,7 +372,7 @@ describe('FileDiscoveryService', () => {
// .geminiignore un-ignores important.txt (which wasn't ignored anyway)
// and ignores temp/
expect(filtered).toEqual(
- ['file.txt', 'important.txt'].map((f) => path.join(projectRoot, f)),
+ ['file.txt', 'important.txt'].map((f) => path.join(workspaceRoot, f)),
);
});
});
@@ -382,20 +382,20 @@ describe('FileDiscoveryService', () => {
const customIgnoreName = '.customignore';
await createTestFile(customIgnoreName, '*.secret');
- const service = new FileDiscoveryService(projectRoot, {
+ const service = new FileDiscoveryService(workspaceRoot, {
customIgnoreFilePaths: [customIgnoreName],
});
const files = ['file.txt', 'file.secret'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
const filtered = service.filterFiles(files);
- expect(filtered).toEqual([path.join(projectRoot, 'file.txt')]);
+ expect(filtered).toEqual([path.join(workspaceRoot, 'file.txt')]);
});
it('should prioritize custom ignore patterns over .geminiignore patterns in git repo', async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', 'node_modules/');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '*.log');
@@ -403,16 +403,16 @@ describe('FileDiscoveryService', () => {
// .geminiignore ignores *.log, custom un-ignores debug.log
await createTestFile(customIgnoreName, '!debug.log');
- const service = new FileDiscoveryService(projectRoot, {
+ const service = new FileDiscoveryService(workspaceRoot, {
customIgnoreFilePaths: [customIgnoreName],
});
const files = ['debug.log', 'error.log'].map((f) =>
- path.join(projectRoot, f),
+ path.join(workspaceRoot, f),
);
const filtered = service.filterFiles(files);
- expect(filtered).toEqual([path.join(projectRoot, 'debug.log')]);
+ expect(filtered).toEqual([path.join(workspaceRoot, 'debug.log')]);
});
it('should prioritize custom ignore patterns over .geminiignore patterns in non-git repo', async () => {
@@ -423,33 +423,35 @@ describe('FileDiscoveryService', () => {
// .geminiignore ignores secret.txt, custom un-ignores it
await createTestFile(customIgnoreName, '!secret.txt');
- const service = new FileDiscoveryService(projectRoot, {
+ const service = new FileDiscoveryService(workspaceRoot, {
customIgnoreFilePaths: [customIgnoreName],
});
- const files = ['secret.txt'].map((f) => path.join(projectRoot, f));
+ const files = ['secret.txt'].map((f) => path.join(workspaceRoot, f));
const filtered = service.filterFiles(files);
- expect(filtered).toEqual([path.join(projectRoot, 'secret.txt')]);
+ expect(filtered).toEqual([path.join(workspaceRoot, 'secret.txt')]);
});
});
describe('getIgnoreFilePaths & getAllIgnoreFilePaths', () => {
beforeEach(async () => {
- await fs.mkdir(path.join(projectRoot, '.git'));
+ await fs.mkdir(path.join(workspaceRoot, '.git'));
await createTestFile('.gitignore', '*.log');
await createTestFile(GEMINI_IGNORE_FILE_NAME, '*.tmp');
await createTestFile('.customignore', '*.secret');
});
it('should return .geminiignore path by default', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const paths = service.getIgnoreFilePaths();
- expect(paths).toEqual([path.join(projectRoot, GEMINI_IGNORE_FILE_NAME)]);
+ expect(paths).toEqual([
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
+ ]);
});
it('should not return .geminiignore path if respectGeminiIgnore is false', () => {
- const service = new FileDiscoveryService(projectRoot, {
+ const service = new FileDiscoveryService(workspaceRoot, {
respectGeminiIgnore: false,
});
const paths = service.getIgnoreFilePaths();
@@ -457,42 +459,50 @@ describe('FileDiscoveryService', () => {
});
it('should return custom ignore file paths', () => {
- const service = new FileDiscoveryService(projectRoot, {
+ const service = new FileDiscoveryService(workspaceRoot, {
customIgnoreFilePaths: ['.customignore'],
});
const paths = service.getIgnoreFilePaths();
- expect(paths).toContain(path.join(projectRoot, GEMINI_IGNORE_FILE_NAME));
- expect(paths).toContain(path.join(projectRoot, '.customignore'));
+ expect(paths).toContain(
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
+ );
+ expect(paths).toContain(path.join(workspaceRoot, '.customignore'));
});
it('should return all ignore paths including .gitignore', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const paths = service.getAllIgnoreFilePaths();
- expect(paths).toContain(path.join(projectRoot, GEMINI_IGNORE_FILE_NAME));
- expect(paths).toContain(path.join(projectRoot, '.gitignore'));
+ expect(paths).toContain(
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
+ );
+ expect(paths).toContain(path.join(workspaceRoot, '.gitignore'));
});
it('should not return .gitignore if respectGitIgnore is false', () => {
- const service = new FileDiscoveryService(projectRoot, {
+ const service = new FileDiscoveryService(workspaceRoot, {
respectGitIgnore: false,
});
const paths = service.getAllIgnoreFilePaths();
- expect(paths).toContain(path.join(projectRoot, GEMINI_IGNORE_FILE_NAME));
- expect(paths).not.toContain(path.join(projectRoot, '.gitignore'));
+ expect(paths).toContain(
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
+ );
+ expect(paths).not.toContain(path.join(workspaceRoot, '.gitignore'));
});
it('should not return .gitignore if it does not exist', async () => {
- await fs.rm(path.join(projectRoot, '.gitignore'));
- const service = new FileDiscoveryService(projectRoot);
+ await fs.rm(path.join(workspaceRoot, '.gitignore'));
+ const service = new FileDiscoveryService(workspaceRoot);
const paths = service.getAllIgnoreFilePaths();
- expect(paths).not.toContain(path.join(projectRoot, '.gitignore'));
- expect(paths).toContain(path.join(projectRoot, GEMINI_IGNORE_FILE_NAME));
+ expect(paths).not.toContain(path.join(workspaceRoot, '.gitignore'));
+ expect(paths).toContain(
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
+ );
});
it('should ensure .gitignore is the first file in the list', () => {
- const service = new FileDiscoveryService(projectRoot);
+ const service = new FileDiscoveryService(workspaceRoot);
const paths = service.getAllIgnoreFilePaths();
- expect(paths[0]).toBe(path.join(projectRoot, '.gitignore'));
+ expect(paths[0]).toBe(path.join(workspaceRoot, '.gitignore'));
});
});
});
diff --git a/packages/core/src/services/gitService.test.ts b/packages/core/src/services/gitService.test.ts
index 095b8bc56f..6a263a41a8 100644
--- a/packages/core/src/services/gitService.test.ts
+++ b/packages/core/src/services/gitService.test.ts
@@ -15,6 +15,7 @@ import {
} from 'vitest';
import { GitService } from './gitService.js';
import { Storage } from '../config/storage.js';
+import { ProjectRegistry } from '../config/projectRegistry.js';
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import * as os from 'node:os';
@@ -70,6 +71,8 @@ vi.mock('../utils/paths.js', async (importOriginal) => {
};
});
+vi.mock('../config/projectRegistry.js');
+
const hoistedMockDebugLogger = vi.hoisted(() => ({
debug: vi.fn(),
warn: vi.fn(),
@@ -81,15 +84,15 @@ vi.mock('../utils/debugLogger.js', () => ({
describe('GitService', () => {
let testRootDir: string;
- let projectRoot: string;
+ let workspaceRoot: string;
let homedir: string;
let storage: Storage;
beforeEach(async () => {
testRootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'git-service-test-'));
- projectRoot = path.join(testRootDir, 'project');
+ workspaceRoot = path.join(testRootDir, 'project');
homedir = path.join(testRootDir, 'home');
- await fs.mkdir(projectRoot, { recursive: true });
+ await fs.mkdir(workspaceRoot, { recursive: true });
await fs.mkdir(homedir, { recursive: true });
vi.clearAllMocks();
@@ -126,7 +129,12 @@ describe('GitService', () => {
hoistedMockCommit.mockResolvedValue({
commit: 'initial',
});
- storage = new Storage(projectRoot);
+ ProjectRegistry.prototype.initialize = vi.fn().mockResolvedValue(undefined);
+ ProjectRegistry.prototype.getShortId = vi
+ .fn()
+ .mockReturnValue('project-slug');
+ storage = new Storage(workspaceRoot);
+ await storage.initialize();
});
afterEach(async () => {
@@ -136,7 +144,7 @@ describe('GitService', () => {
describe('constructor', () => {
it('should successfully create an instance', () => {
- expect(() => new GitService(projectRoot, storage)).not.toThrow();
+ expect(() => new GitService(workspaceRoot, storage)).not.toThrow();
});
});
@@ -155,14 +163,14 @@ describe('GitService', () => {
describe('initialize', () => {
it('should throw an error if Git is not available', async () => {
(spawnAsync as Mock).mockRejectedValue(new Error('git not found'));
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await expect(service.initialize()).rejects.toThrow(
'Checkpointing is enabled, but Git is not installed. Please install Git or disable checkpointing to continue.',
);
});
it('should call setupShadowGitRepository if Git is available', async () => {
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
const setupSpy = vi
.spyOn(service, 'setupShadowGitRepository')
.mockResolvedValue(undefined);
@@ -182,14 +190,14 @@ describe('GitService', () => {
});
it('should create history and repository directories', async () => {
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
const stats = await fs.stat(repoDir);
expect(stats.isDirectory()).toBe(true);
});
it('should create a .gitconfig file with the correct content', async () => {
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
const expectedConfigContent =
@@ -200,7 +208,7 @@ describe('GitService', () => {
it('should initialize git repo in historyDir if not already initialized', async () => {
hoistedMockCheckIsRepo.mockResolvedValue(false);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
expect(hoistedMockSimpleGit).toHaveBeenCalledWith(repoDir);
expect(hoistedMockInit).toHaveBeenCalled();
@@ -208,17 +216,17 @@ describe('GitService', () => {
it('should not initialize git repo if already initialized', async () => {
hoistedMockCheckIsRepo.mockResolvedValue(true);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
expect(hoistedMockInit).not.toHaveBeenCalled();
});
- it('should copy .gitignore from projectRoot if it exists', async () => {
+ it('should copy .gitignore from workspaceRoot if it exists', async () => {
const gitignoreContent = 'node_modules/\n.env';
- const visibleGitIgnorePath = path.join(projectRoot, '.gitignore');
+ const visibleGitIgnorePath = path.join(workspaceRoot, '.gitignore');
await fs.writeFile(visibleGitIgnorePath, gitignoreContent);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
const hiddenGitIgnorePath = path.join(repoDir, '.gitignore');
@@ -227,7 +235,7 @@ describe('GitService', () => {
});
it('should not create a .gitignore in shadow repo if project .gitignore does not exist', async () => {
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
const hiddenGitIgnorePath = path.join(repoDir, '.gitignore');
@@ -236,12 +244,12 @@ describe('GitService', () => {
expect(content).toBe('');
});
- it('should throw an error if reading projectRoot .gitignore fails with other errors', async () => {
- const visibleGitIgnorePath = path.join(projectRoot, '.gitignore');
+ it('should throw an error if reading workspaceRoot .gitignore fails with other errors', async () => {
+ const visibleGitIgnorePath = path.join(workspaceRoot, '.gitignore');
// Create a directory instead of a file to cause a read error
await fs.mkdir(visibleGitIgnorePath);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
// EISDIR is the expected error code on Unix-like systems
await expect(service.setupShadowGitRepository()).rejects.toThrow(
/EISDIR: illegal operation on a directory, read|EBUSY: resource busy or locked, read/,
@@ -250,7 +258,7 @@ describe('GitService', () => {
it('should make an initial commit if no commits exist in history repo', async () => {
hoistedMockCheckIsRepo.mockResolvedValue(false);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
expect(hoistedMockCommit).toHaveBeenCalledWith('Initial commit', {
'--allow-empty': null,
@@ -259,7 +267,7 @@ describe('GitService', () => {
it('should not make an initial commit if commits already exist', async () => {
hoistedMockCheckIsRepo.mockResolvedValue(true);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
expect(hoistedMockCommit).not.toHaveBeenCalled();
});
@@ -269,7 +277,7 @@ describe('GitService', () => {
hoistedMockCheckIsRepo.mockRejectedValue(
new Error('git rev-parse --is-inside-work-tree failed'),
);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
// Should proceed to initialize the repo since checkIsRepo failed
expect(hoistedMockInit).toHaveBeenCalled();
@@ -281,7 +289,7 @@ describe('GitService', () => {
it('should configure git environment to use local gitconfig', async () => {
hoistedMockCheckIsRepo.mockResolvedValue(false);
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.setupShadowGitRepository();
expect(hoistedMockEnv).toHaveBeenCalledWith(
@@ -302,7 +310,7 @@ describe('GitService', () => {
describe('createFileSnapshot', () => {
it('should commit with --no-verify flag', async () => {
hoistedMockStatus.mockResolvedValue({ isClean: () => false });
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
await service.initialize();
await service.createFileSnapshot('test commit');
expect(hoistedMockCommit).toHaveBeenCalledWith('test commit', {
@@ -313,7 +321,7 @@ describe('GitService', () => {
it('should create a new commit if there are staged changes', async () => {
hoistedMockStatus.mockResolvedValue({ isClean: () => false });
hoistedMockCommit.mockResolvedValue({ commit: 'new-commit-hash' });
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
const commitHash = await service.createFileSnapshot('test message');
expect(hoistedMockAdd).toHaveBeenCalledWith('.');
expect(hoistedMockStatus).toHaveBeenCalled();
@@ -326,7 +334,7 @@ describe('GitService', () => {
it('should return the current HEAD commit hash if there are no staged changes', async () => {
hoistedMockStatus.mockResolvedValue({ isClean: () => true });
hoistedMockRaw.mockResolvedValue('current-head-hash');
- const service = new GitService(projectRoot, storage);
+ const service = new GitService(workspaceRoot, storage);
const commitHash = await service.createFileSnapshot('test message');
expect(hoistedMockAdd).toHaveBeenCalledWith('.');
expect(hoistedMockStatus).toHaveBeenCalled();
diff --git a/packages/core/src/services/sessionSummaryUtils.test.ts b/packages/core/src/services/sessionSummaryUtils.test.ts
index 2314b7ca06..36edfd6db7 100644
--- a/packages/core/src/services/sessionSummaryUtils.test.ts
+++ b/packages/core/src/services/sessionSummaryUtils.test.ts
@@ -58,7 +58,7 @@ describe('sessionSummaryUtils', () => {
mockConfig = {
getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/workspace'),
},
} as unknown as Config;
@@ -133,7 +133,7 @@ describe('sessionSummaryUtils', () => {
expect(result).toBe(
path.join(
- '/tmp/project',
+ '/tmp/workspace',
'chats',
'session-2024-01-01T10-00-abc12345.json',
),
@@ -154,7 +154,7 @@ describe('sessionSummaryUtils', () => {
expect(result).toBe(
path.join(
- '/tmp/project',
+ '/tmp/workspace',
'chats',
'session-2024-01-02T10-00-newer000.json',
),
@@ -181,7 +181,7 @@ describe('sessionSummaryUtils', () => {
it('should generate and save summary for session needing one', async () => {
const sessionPath = path.join(
- '/tmp/project',
+ '/tmp/workspace',
'chats',
'session-2024-01-01T10-00-abc12345.json',
);
diff --git a/packages/core/src/services/sessionSummaryUtils.ts b/packages/core/src/services/sessionSummaryUtils.ts
index c64f19870d..9bf8ec5fab 100644
--- a/packages/core/src/services/sessionSummaryUtils.ts
+++ b/packages/core/src/services/sessionSummaryUtils.ts
@@ -98,7 +98,7 @@ export async function getPreviousSession(
config: Config,
): Promise {
try {
- const chatsDir = path.join(config.storage.getProjectTempDir(), 'chats');
+ const chatsDir = path.join(config.storage.getWorkspaceTempDir(), 'chats');
// Check if chats directory exists
try {
diff --git a/packages/core/src/services/toolOutputMaskingService.test.ts b/packages/core/src/services/toolOutputMaskingService.test.ts
index 1187a28ae1..1142a773dc 100644
--- a/packages/core/src/services/toolOutputMaskingService.test.ts
+++ b/packages/core/src/services/toolOutputMaskingService.test.ts
@@ -41,7 +41,7 @@ describe('ToolOutputMaskingService', () => {
mockConfig = {
storage: {
getHistoryDir: () => path.join(testTempDir, 'history'),
- getProjectTempDir: () => testTempDir,
+ getWorkspaceTempDir: () => testTempDir,
},
getSessionId: () => 'mock-session',
getUsageStatisticsEnabled: () => false,
diff --git a/packages/core/src/services/toolOutputMaskingService.ts b/packages/core/src/services/toolOutputMaskingService.ts
index 8a7ae0090d..e8c0fd606a 100644
--- a/packages/core/src/services/toolOutputMaskingService.ts
+++ b/packages/core/src/services/toolOutputMaskingService.ts
@@ -160,7 +160,7 @@ export class ToolOutputMaskingService {
const newHistory = [...history]; // Shallow copy of history
let actualTokensSaved = 0;
let toolOutputsDir = path.join(
- config.storage.getProjectTempDir(),
+ config.storage.getWorkspaceTempDir(),
TOOL_OUTPUTS_DIR,
);
const sessionId = config.getSessionId();
diff --git a/packages/core/src/tools/confirmation-policy.test.ts b/packages/core/src/tools/confirmation-policy.test.ts
index a20bb611e3..a55d1c8339 100644
--- a/packages/core/src/tools/confirmation-policy.test.ts
+++ b/packages/core/src/tools/confirmation-policy.test.ts
@@ -74,7 +74,7 @@ describe('Tool Confirmation Policy Updates', () => {
}),
getDirectWebFetch: () => false,
storage: {
- getProjectTempDir: () => path.join(os.tmpdir(), 'gemini-cli-temp'),
+ getWorkspaceTempDir: () => path.join(os.tmpdir(), 'gemini-cli-temp'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -82,8 +82,8 @@ describe('Tool Confirmation Policy Updates', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -91,8 +91,8 @@ describe('Tool Confirmation Policy Updates', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
};
});
diff --git a/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap b/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap
index 70cf828d86..59e474000e 100644
--- a/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap
+++ b/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap
@@ -592,7 +592,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-2.5-pro > snaps
"type": "string",
},
"dir_path": {
- "description": "(OPTIONAL) The path of the directory to run the command in. If not provided, the project root directory is used. Must be a directory within the workspace and must already exist.",
+ "description": "(OPTIONAL) The path of the directory to run the command in. If not provided, the workspace root directory is used. Must be a directory within the workspace and must already exist.",
"type": "string",
},
"is_background": {
@@ -715,7 +715,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps
User request: Create a website with a React for creating fancy logos using gemini-2.5-flash-image
ToDo list created by the agent:
-1. Initialize a new React project environment (e.g., using Vite).
+1. Initialize a new React workspace environment (e.g., using Vite).
2. Design and build the core UI components: a text input (prompt field) for the logo description, selection controls for style parameters (if the API supports them), and an image preview area.
3. Implement state management (e.g., React Context or Zustand) to manage the user's input prompt, the API loading status (pending, success, error), and the resulting image data.
4. Create an API service module within the React app (using "fetch" or "axios") to securely format and send the prompt data via an HTTP POST request to the specified "gemini-2.5-flash-image" (Gemini model) endpoint.
@@ -1355,7 +1355,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview >
"type": "string",
},
"dir_path": {
- "description": "(OPTIONAL) The path of the directory to run the command in. If not provided, the project root directory is used. Must be a directory within the workspace and must already exist.",
+ "description": "(OPTIONAL) The path of the directory to run the command in. If not provided, the workspace root directory is used. Must be a directory within the workspace and must already exist.",
"type": "string",
},
"is_background": {
@@ -1373,13 +1373,13 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview >
exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview > snapshot for tool: save_memory 1`] = `
{
- "description": "Persists global preferences or facts across ALL future sessions. Use this for recurring instructions like coding styles or tool aliases. Unlike 'write_file', which is for project-specific files, this appends to a global memory file loaded in every workspace. If you are unsure whether a fact should be remembered globally, ask the user first. CRITICAL: Do not use for session-specific context or temporary data.",
+ "description": "Persists global preferences or facts across ALL future sessions. Use this for recurring instructions like coding styles or tool aliases. Unlike 'write_file', which is for workspace-specific files, this appends to a global memory file loaded in every workspace. If you are unsure whether a fact should be remembered globally, ask the user first. CRITICAL: Do not use for session-specific context or temporary data.",
"name": "save_memory",
"parametersJsonSchema": {
"additionalProperties": false,
"properties": {
"fact": {
- "description": "A concise, global fact or preference (e.g., 'I prefer using tabs'). Do not include local paths or project-specific names.",
+ "description": "A concise, global fact or preference (e.g., 'I prefer using tabs'). Do not include local paths or workspace-specific names.",
"type": "string",
},
},
@@ -1469,7 +1469,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps
User request: Create a website with a React for creating fancy logos using gemini-2.5-flash-image
ToDo list created by the agent:
-1. Initialize a new React project environment (e.g., using Vite).
+1. Initialize a new React workspace environment (e.g., using Vite).
2. Design and build the core UI components: a text input (prompt field) for the logo description, selection controls for style parameters (if the API supports them), and an image preview area.
3. Implement state management (e.g., React Context or Zustand) to manage the user's input prompt, the API loading status (pending, success, error), and the resulting image data.
4. Create an API service module within the React app (using "fetch" or "axios") to securely format and send the prompt data via an HTTP POST request to the specified "gemini-2.5-flash-image" (Gemini model) endpoint.
diff --git a/packages/core/src/tools/edit.test.ts b/packages/core/src/tools/edit.test.ts
index 0cae5a070c..cb41077f11 100644
--- a/packages/core/src/tools/edit.test.ts
+++ b/packages/core/src/tools/edit.test.ts
@@ -124,7 +124,7 @@ describe('EditTool', () => {
getDisableLLMCorrection: vi.fn(() => true),
getExperiments: () => {},
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -132,8 +132,8 @@ describe('EditTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -141,8 +141,8 @@ describe('EditTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
diff --git a/packages/core/src/tools/glob.test.ts b/packages/core/src/tools/glob.test.ts
index 2aa4d52c7e..aea4216119 100644
--- a/packages/core/src/tools/glob.test.ts
+++ b/packages/core/src/tools/glob.test.ts
@@ -41,7 +41,7 @@ describe('GlobTool', () => {
const fileDiscovery = new FileDiscoveryService(rootDir);
const mockStorage = {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
};
mockConfig = {
@@ -57,8 +57,8 @@ describe('GlobTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -66,8 +66,8 @@ describe('GlobTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
diff --git a/packages/core/src/tools/grep.test.ts b/packages/core/src/tools/grep.test.ts
index 6f98b0f2fc..7fbd188c9c 100644
--- a/packages/core/src/tools/grep.test.ts
+++ b/packages/core/src/tools/grep.test.ts
@@ -55,7 +55,7 @@ describe('GrepTool', () => {
getGlobExcludes: () => [],
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -63,8 +63,8 @@ describe('GrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -72,8 +72,8 @@ describe('GrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
@@ -339,7 +339,7 @@ describe('GrepTool', () => {
getGlobExcludes: () => [],
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -347,8 +347,8 @@ describe('GrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -356,8 +356,8 @@ describe('GrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
@@ -416,7 +416,7 @@ describe('GrepTool', () => {
getGlobExcludes: () => [],
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -424,8 +424,8 @@ describe('GrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -433,8 +433,8 @@ describe('GrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
diff --git a/packages/core/src/tools/ls.test.ts b/packages/core/src/tools/ls.test.ts
index 63d7693123..668e1b84d4 100644
--- a/packages/core/src/tools/ls.test.ts
+++ b/packages/core/src/tools/ls.test.ts
@@ -32,7 +32,7 @@ describe('LSTool', () => {
);
const mockStorage = {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
};
mockConfig = {
@@ -51,8 +51,8 @@ describe('LSTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -60,8 +60,8 @@ describe('LSTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
diff --git a/packages/core/src/tools/read-file.test.ts b/packages/core/src/tools/read-file.test.ts
index 5457b8337b..6cc3aeab8c 100644
--- a/packages/core/src/tools/read-file.test.ts
+++ b/packages/core/src/tools/read-file.test.ts
@@ -45,7 +45,7 @@ describe('ReadFileTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: () => path.join(tempRootDir, '.temp'),
+ getWorkspaceTempDir: () => path.join(tempRootDir, '.temp'),
},
isInteractive: () => false,
isPathAllowed(this: Config, absolutePath: string): boolean {
@@ -54,8 +54,8 @@ describe('ReadFileTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -63,8 +63,8 @@ describe('ReadFileTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
tool = new ReadFileTool(mockConfigInstance, createMockMessageBus());
@@ -459,7 +459,7 @@ describe('ReadFileTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: () => path.join(tempRootDir, '.temp'),
+ getWorkspaceTempDir: () => path.join(tempRootDir, '.temp'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -467,8 +467,8 @@ describe('ReadFileTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(
this: Config,
@@ -479,8 +479,8 @@ describe('ReadFileTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
tool = new ReadFileTool(mockConfigInstance, createMockMessageBus());
@@ -532,7 +532,7 @@ describe('ReadFileTool', () => {
respectGeminiIgnore: false,
}),
storage: {
- getProjectTempDir: () => path.join(tempRootDir, '.temp'),
+ getWorkspaceTempDir: () => path.join(tempRootDir, '.temp'),
},
isInteractive: () => false,
isPathAllowed(this: Config, absolutePath: string): boolean {
@@ -541,8 +541,8 @@ describe('ReadFileTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(
this: Config,
@@ -553,8 +553,8 @@ describe('ReadFileTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
diff --git a/packages/core/src/tools/read-many-files.test.ts b/packages/core/src/tools/read-many-files.test.ts
index f340424a35..68b2e0504c 100644
--- a/packages/core/src/tools/read-many-files.test.ts
+++ b/packages/core/src/tools/read-many-files.test.ts
@@ -94,7 +94,7 @@ describe('ReadManyFilesTool', () => {
}),
isInteractive: () => false,
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -102,8 +102,8 @@ describe('ReadManyFilesTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -111,8 +111,8 @@ describe('ReadManyFilesTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
tool = new ReadManyFilesTool(mockConfig, createMockMessageBus());
@@ -531,7 +531,7 @@ describe('ReadManyFilesTool', () => {
}),
isInteractive: () => false,
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -539,8 +539,8 @@ describe('ReadManyFilesTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -548,8 +548,8 @@ describe('ReadManyFilesTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
tool = new ReadManyFilesTool(mockConfig, createMockMessageBus());
diff --git a/packages/core/src/tools/ripGrep.test.ts b/packages/core/src/tools/ripGrep.test.ts
index 0eaf5c0b68..3d777e9c79 100644
--- a/packages/core/src/tools/ripGrep.test.ts
+++ b/packages/core/src/tools/ripGrep.test.ts
@@ -286,7 +286,7 @@ describe('RipGrepTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -294,8 +294,8 @@ describe('RipGrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -303,8 +303,8 @@ describe('RipGrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
@@ -857,7 +857,7 @@ describe('RipGrepTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -865,8 +865,8 @@ describe('RipGrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -874,8 +874,8 @@ describe('RipGrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
@@ -970,7 +970,7 @@ describe('RipGrepTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -978,8 +978,8 @@ describe('RipGrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -987,8 +987,8 @@ describe('RipGrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
@@ -1510,7 +1510,7 @@ describe('RipGrepTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -1518,8 +1518,8 @@ describe('RipGrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -1527,8 +1527,8 @@ describe('RipGrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
const gitIgnoreDisabledTool = new RipGrepTool(
@@ -1576,7 +1576,7 @@ describe('RipGrepTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -1584,8 +1584,8 @@ describe('RipGrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -1593,8 +1593,8 @@ describe('RipGrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
const geminiIgnoreTool = new RipGrepTool(
@@ -1642,7 +1642,7 @@ describe('RipGrepTool', () => {
respectGeminiIgnore: false,
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -1650,8 +1650,8 @@ describe('RipGrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -1659,8 +1659,8 @@ describe('RipGrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
const geminiIgnoreTool = new RipGrepTool(
@@ -1816,7 +1816,7 @@ describe('RipGrepTool', () => {
respectGeminiIgnore: true,
}),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -1824,8 +1824,8 @@ describe('RipGrepTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -1833,8 +1833,8 @@ describe('RipGrepTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
diff --git a/packages/core/src/tools/shell.test.ts b/packages/core/src/tools/shell.test.ts
index 907d117439..bc76974a2e 100644
--- a/packages/core/src/tools/shell.test.ts
+++ b/packages/core/src/tools/shell.test.ts
@@ -106,7 +106,7 @@ describe('ShellTool', () => {
.fn()
.mockReturnValue(new WorkspaceContext(tempRootDir)),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
isPathAllowed(this: Config, absolutePath: string): boolean {
const workspaceContext = this.getWorkspaceContext();
@@ -114,8 +114,8 @@ describe('ShellTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -123,8 +123,8 @@ describe('ShellTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
getGeminiClient: vi.fn().mockReturnValue({}),
getShellToolInactivityTimeout: vi.fn().mockReturnValue(1000),
diff --git a/packages/core/src/tools/tool-registry.test.ts b/packages/core/src/tools/tool-registry.test.ts
index 57c992f674..b8f97945b8 100644
--- a/packages/core/src/tools/tool-registry.test.ts
+++ b/packages/core/src/tools/tool-registry.test.ts
@@ -11,6 +11,24 @@ import type { ConfigParameters } from '../config/config.js';
import { Config } from '../config/config.js';
import { ApprovalMode } from '../policy/types.js';
+vi.mock('../config/storage.js', async (importOriginal) => {
+ const actual = await importOriginal();
+ actual.Storage.prototype.initialize = vi.fn().mockResolvedValue(undefined);
+ actual.Storage.prototype.getWorkspaceTempDir = vi
+ .fn()
+ .mockReturnValue('/tmp/workspace');
+ actual.Storage.prototype.getWorkspaceTempPlansDir = vi
+ .fn()
+ .mockReturnValue('/tmp/workspace/plans');
+ actual.Storage.prototype.getPlansDir = vi
+ .fn()
+ .mockReturnValue('/tmp/workspace/plans');
+ actual.Storage.prototype.getProjectIdentifier = vi
+ .fn()
+ .mockReturnValue('test-project');
+ return actual;
+});
+
import { ToolRegistry, DiscoveredTool } from './tool-registry.js';
import { DISCOVERED_TOOL_PREFIX } from './tool-names.js';
import { DiscoveredMCPTool, MCP_QUALIFIED_NAME_SEPARATOR } from './mcp-tool.js';
diff --git a/packages/core/src/tools/write-file.test.ts b/packages/core/src/tools/write-file.test.ts
index 0b978f14f9..88cb72abb4 100644
--- a/packages/core/src/tools/write-file.test.ts
+++ b/packages/core/src/tools/write-file.test.ts
@@ -105,7 +105,7 @@ const mockConfigInternal = {
getDisableLLMCorrection: vi.fn(() => true),
getActiveModel: () => 'test-model',
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
},
};
@@ -135,7 +135,7 @@ describe('WriteFileTool', () => {
const workspaceContext = new WorkspaceContext(rootDir, [plansDir]);
const mockStorage = {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project'),
};
mockConfig = {
@@ -148,8 +148,8 @@ describe('WriteFileTool', () => {
return true;
}
- const projectTempDir = this.storage.getProjectTempDir();
- return isSubpath(path.resolve(projectTempDir), absolutePath);
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return isSubpath(path.resolve(workspaceTempDir), absolutePath);
},
validatePathAccess(this: Config, absolutePath: string): string | null {
if (this.isPathAllowed(absolutePath)) {
@@ -157,8 +157,8 @@ describe('WriteFileTool', () => {
}
const workspaceDirs = this.getWorkspaceContext().getDirectories();
- const projectTempDir = this.storage.getProjectTempDir();
- return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${projectTempDir}`;
+ const workspaceTempDir = this.storage.getWorkspaceTempDir();
+ return `Path not in workspace: Attempted path "${absolutePath}" resolves outside the allowed workspace directories: ${workspaceDirs.join(', ')} or the project temp directory: ${workspaceTempDir}`;
},
} as unknown as Config;
diff --git a/packages/core/src/utils/bfsFileSearch.test.ts b/packages/core/src/utils/bfsFileSearch.test.ts
index 22e4ed6795..69e975d87c 100644
--- a/packages/core/src/utils/bfsFileSearch.test.ts
+++ b/packages/core/src/utils/bfsFileSearch.test.ts
@@ -10,7 +10,7 @@ import * as path from 'node:path';
import * as os from 'node:os';
import { bfsFileSearch, bfsFileSearchSync } from './bfsFileSearch.js';
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
-import { GEMINI_IGNORE_FILE_NAME } from 'src/config/constants.js';
+import { GEMINI_IGNORE_FILE_NAME } from '../config/constants.js';
describe('bfsFileSearch', () => {
let testRootDir: string;
@@ -108,10 +108,10 @@ describe('bfsFileSearch', () => {
});
describe('with FileDiscoveryService', () => {
- let projectRoot: string;
+ let workspaceRoot: string;
beforeEach(async () => {
- projectRoot = await createEmptyDir('project');
+ workspaceRoot = await createEmptyDir('project');
});
it('should ignore gitignored files', async () => {
@@ -125,8 +125,8 @@ describe('bfsFileSearch', () => {
'target.txt',
);
- const fileService = new FileDiscoveryService(projectRoot);
- const result = await bfsFileSearch(projectRoot, {
+ const fileService = new FileDiscoveryService(workspaceRoot);
+ const result = await bfsFileSearch(workspaceRoot, {
fileName: 'target.txt',
fileService,
fileFilteringOptions: {
@@ -149,8 +149,8 @@ describe('bfsFileSearch', () => {
'target.txt',
);
- const fileService = new FileDiscoveryService(projectRoot);
- const result = await bfsFileSearch(projectRoot, {
+ const fileService = new FileDiscoveryService(workspaceRoot);
+ const result = await bfsFileSearch(workspaceRoot, {
fileName: 'target.txt',
fileService,
fileFilteringOptions: {
@@ -179,8 +179,8 @@ describe('bfsFileSearch', () => {
'target.txt',
);
- const fileService = new FileDiscoveryService(projectRoot);
- const result = await bfsFileSearch(projectRoot, {
+ const fileService = new FileDiscoveryService(workspaceRoot);
+ const result = await bfsFileSearch(workspaceRoot, {
fileName: 'target.txt',
fileService,
fileFilteringOptions: {
@@ -302,7 +302,7 @@ describe('bfsFileSearchSync', () => {
});
it('should work with FileDiscoveryService synchronously', async () => {
- const projectRoot = await createEmptyDir('project');
+ const workspaceRoot = await createEmptyDir('project');
await createEmptyDir('project', '.git');
await createTestFile('node_modules/', 'project', '.gitignore');
await createTestFile('content', 'project', 'node_modules', 'target.txt');
@@ -313,8 +313,8 @@ describe('bfsFileSearchSync', () => {
'target.txt',
);
- const fileService = new FileDiscoveryService(projectRoot);
- const result = bfsFileSearchSync(projectRoot, {
+ const fileService = new FileDiscoveryService(workspaceRoot);
+ const result = bfsFileSearchSync(workspaceRoot, {
fileName: 'target.txt',
fileService,
fileFilteringOptions: {
diff --git a/packages/core/src/utils/environmentContext.test.ts b/packages/core/src/utils/environmentContext.test.ts
index a43bb5fd56..7281ea61ff 100644
--- a/packages/core/src/utils/environmentContext.test.ts
+++ b/packages/core/src/utils/environmentContext.test.ts
@@ -37,7 +37,7 @@ describe('getDirectoryContextString', () => {
}),
getFileService: vi.fn(),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
} as unknown as Storage,
};
vi.mocked(getFolderStructure).mockResolvedValue('Mock Folder Structure');
@@ -93,7 +93,7 @@ describe('getEnvironmentContext', () => {
getToolRegistry: vi.fn().mockReturnValue(mockToolRegistry),
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/tmp/project-temp'),
} as unknown as Storage,
};
diff --git a/packages/core/src/utils/environmentContext.ts b/packages/core/src/utils/environmentContext.ts
index c28f16d837..01a185a215 100644
--- a/packages/core/src/utils/environmentContext.ts
+++ b/packages/core/src/utils/environmentContext.ts
@@ -56,7 +56,7 @@ export async function getEnvironmentContext(config: Config): Promise {
const directoryContext = config.getIncludeDirectoryTree()
? await getDirectoryContextString(config)
: '';
- const tempDir = config.storage.getProjectTempDir();
+ const tempDir = config.storage.getWorkspaceTempDir();
const environmentMemory = config.getEnvironmentMemory();
const context = `
diff --git a/packages/core/src/utils/fastAckHelper.test.ts b/packages/core/src/utils/fastAckHelper.test.ts
index 3947c43f23..b71375b9a6 100644
--- a/packages/core/src/utils/fastAckHelper.test.ts
+++ b/packages/core/src/utils/fastAckHelper.test.ts
@@ -12,7 +12,7 @@ import {
truncateFastAckInput,
generateSteeringAckMessage,
} from './fastAckHelper.js';
-import { LlmRole } from 'src/telemetry/llmRole.js';
+import { LlmRole } from '../telemetry/llmRole.js';
describe('truncateFastAckInput', () => {
it('returns input as-is when below limit', () => {
diff --git a/packages/core/src/utils/getFolderStructure.test.ts b/packages/core/src/utils/getFolderStructure.test.ts
index 5a9a077e91..881de5b3a4 100644
--- a/packages/core/src/utils/getFolderStructure.test.ts
+++ b/packages/core/src/utils/getFolderStructure.test.ts
@@ -11,7 +11,7 @@ import { getFolderStructure } from './getFolderStructure.js';
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
import * as path from 'node:path';
import { GEMINI_DIR } from './paths.js';
-import { GEMINI_IGNORE_FILE_NAME } from 'src/config/constants.js';
+import { GEMINI_IGNORE_FILE_NAME } from '../config/constants.js';
describe('getFolderStructure', () => {
let testRootDir: string;
diff --git a/packages/core/src/utils/gitIgnoreParser.test.ts b/packages/core/src/utils/gitIgnoreParser.test.ts
index 2afeb823d2..7bdb41cd60 100644
--- a/packages/core/src/utils/gitIgnoreParser.test.ts
+++ b/packages/core/src/utils/gitIgnoreParser.test.ts
@@ -12,25 +12,25 @@ import * as os from 'node:os';
describe('GitIgnoreParser', () => {
let parser: GitIgnoreParser;
- let projectRoot: string;
+ let workspaceRoot: string;
async function createTestFile(filePath: string, content = '') {
- const fullPath = path.join(projectRoot, filePath);
+ const fullPath = path.join(workspaceRoot, filePath);
await fs.mkdir(path.dirname(fullPath), { recursive: true });
await fs.writeFile(fullPath, content);
}
async function setupGitRepo() {
- await fs.mkdir(path.join(projectRoot, '.git'), { recursive: true });
+ await fs.mkdir(path.join(workspaceRoot, '.git'), { recursive: true });
}
beforeEach(async () => {
- projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitignore-test-'));
- parser = new GitIgnoreParser(projectRoot);
+ workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitignore-test-'));
+ parser = new GitIgnoreParser(workspaceRoot);
});
afterEach(async () => {
- await fs.rm(projectRoot, { recursive: true, force: true });
+ await fs.rm(workspaceRoot, { recursive: true, force: true });
});
describe('Basic ignore behaviors', () => {
@@ -90,7 +90,7 @@ src/*.tmp
it('should always ignore .git directory', () => {
expect(parser.isIgnored('.git')).toBe(true);
expect(parser.isIgnored(path.join('.git', 'config'))).toBe(true);
- expect(parser.isIgnored(path.join(projectRoot, '.git', 'HEAD'))).toBe(
+ expect(parser.isIgnored(path.join(workspaceRoot, '.git', 'HEAD'))).toBe(
true,
);
});
@@ -121,12 +121,17 @@ src/*.tmp
});
it('should handle absolute paths correctly', () => {
- const absolutePath = path.join(projectRoot, 'node_modules', 'lib');
+ const absolutePath = path.join(workspaceRoot, 'node_modules', 'lib');
expect(parser.isIgnored(absolutePath)).toBe(true);
});
it('should handle paths outside project root by not ignoring them', () => {
- const outsidePath = path.resolve(projectRoot, '..', 'other', 'file.txt');
+ const outsidePath = path.resolve(
+ workspaceRoot,
+ '..',
+ 'other',
+ 'file.txt',
+ );
expect(parser.isIgnored(outsidePath)).toBe(false);
});
@@ -280,7 +285,7 @@ src/*.tmp
await createTestFile('.gitignore', '*.txt');
const extraPatterns = ['!important.txt', 'temp/'];
- parser = new GitIgnoreParser(projectRoot, extraPatterns);
+ parser = new GitIgnoreParser(workspaceRoot, extraPatterns);
expect(parser.isIgnored('file.txt')).toBe(true);
expect(parser.isIgnored('important.txt')).toBe(false); // Un-ignored by extraPatterns
@@ -291,7 +296,7 @@ src/*.tmp
await createTestFile('.gitignore', '/foo/\n/a/*/c/');
const extraPatterns = ['!foo/', '!a/*/c/'];
- parser = new GitIgnoreParser(projectRoot, extraPatterns);
+ parser = new GitIgnoreParser(workspaceRoot, extraPatterns);
expect(parser.isIgnored('foo/bar/file.txt')).toBe(false);
expect(parser.isIgnored('a/b/c/file.txt')).toBe(false);
@@ -302,7 +307,7 @@ src/*.tmp
await createTestFile('foo/bar/.gitignore', 'file.txt');
const extraPatterns = ['!foo/'];
- parser = new GitIgnoreParser(projectRoot, extraPatterns);
+ parser = new GitIgnoreParser(workspaceRoot, extraPatterns);
expect(parser.isIgnored('foo/bar/file.txt')).toBe(true);
expect(parser.isIgnored('foo/bar/file2.txt')).toBe(false);
diff --git a/packages/core/src/utils/ignoreFileParser.test.ts b/packages/core/src/utils/ignoreFileParser.test.ts
index 528ad1e8ef..9e7526879f 100644
--- a/packages/core/src/utils/ignoreFileParser.test.ts
+++ b/packages/core/src/utils/ignoreFileParser.test.ts
@@ -12,22 +12,22 @@ import * as os from 'node:os';
import { GEMINI_IGNORE_FILE_NAME } from '../config/constants.js';
describe('GeminiIgnoreParser', () => {
- let projectRoot: string;
+ let workspaceRoot: string;
async function createTestFile(filePath: string, content = '') {
- const fullPath = path.join(projectRoot, filePath);
+ const fullPath = path.join(workspaceRoot, filePath);
await fs.mkdir(path.dirname(fullPath), { recursive: true });
await fs.writeFile(fullPath, content);
}
beforeEach(async () => {
- projectRoot = await fs.mkdtemp(
+ workspaceRoot = await fs.mkdtemp(
path.join(os.tmpdir(), 'geminiignore-test-'),
);
});
afterEach(async () => {
- await fs.rm(projectRoot, { recursive: true, force: true });
+ await fs.rm(workspaceRoot, { recursive: true, force: true });
vi.restoreAllMocks();
});
@@ -50,7 +50,10 @@ describe('GeminiIgnoreParser', () => {
});
it('should ignore files specified in .geminiignore', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.getPatterns()).toEqual(['ignored.txt', '/ignored_dir/']);
expect(parser.isIgnored('ignored.txt')).toBe(true);
expect(parser.isIgnored('not_ignored.txt')).toBe(false);
@@ -61,20 +64,29 @@ describe('GeminiIgnoreParser', () => {
});
it('should return ignore file path when patterns exist', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.getIgnoreFilePaths()).toEqual([
- path.join(projectRoot, GEMINI_IGNORE_FILE_NAME),
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
]);
});
it('should return true for hasPatterns when patterns exist', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.hasPatterns()).toBe(true);
});
it('should maintain patterns in memory when .geminiignore is deleted', async () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
- await fs.rm(path.join(projectRoot, GEMINI_IGNORE_FILE_NAME));
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
+ await fs.rm(path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME));
expect(parser.hasPatterns()).toBe(true);
expect(parser.getIgnoreFilePaths()).toEqual([]);
});
@@ -82,18 +94,27 @@ describe('GeminiIgnoreParser', () => {
describe('when .geminiignore does not exist', () => {
it('should not load any patterns and not ignore any files', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.getPatterns()).toEqual([]);
expect(parser.isIgnored('any_file.txt')).toBe(false);
});
it('should return empty array for getIgnoreFilePaths when no patterns exist', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.getIgnoreFilePaths()).toEqual([]);
});
it('should return false for hasPatterns when no patterns exist', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.hasPatterns()).toBe(false);
});
});
@@ -104,14 +125,20 @@ describe('GeminiIgnoreParser', () => {
});
it('should return file path for getIgnoreFilePaths', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.getIgnoreFilePaths()).toEqual([
- path.join(projectRoot, GEMINI_IGNORE_FILE_NAME),
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
]);
});
it('should return false for hasPatterns', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.hasPatterns()).toBe(false);
});
});
@@ -125,14 +152,20 @@ describe('GeminiIgnoreParser', () => {
});
it('should return file path for getIgnoreFilePaths', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.getIgnoreFilePaths()).toEqual([
- path.join(projectRoot, GEMINI_IGNORE_FILE_NAME),
+ path.join(workspaceRoot, GEMINI_IGNORE_FILE_NAME),
]);
});
it('should return false for hasPatterns', () => {
- const parser = new IgnoreFileParser(projectRoot, GEMINI_IGNORE_FILE_NAME);
+ const parser = new IgnoreFileParser(
+ workspaceRoot,
+ GEMINI_IGNORE_FILE_NAME,
+ );
expect(parser.hasPatterns()).toBe(false);
});
});
@@ -149,7 +182,7 @@ describe('GeminiIgnoreParser', () => {
});
it('should combine patterns from all files', () => {
- const parser = new IgnoreFileParser(projectRoot, [
+ const parser = new IgnoreFileParser(workspaceRoot, [
primaryFile,
secondaryFile,
]);
@@ -157,7 +190,7 @@ describe('GeminiIgnoreParser', () => {
});
it('should respect priority (first file overrides second)', () => {
- const parser = new IgnoreFileParser(projectRoot, [
+ const parser = new IgnoreFileParser(workspaceRoot, [
primaryFile,
secondaryFile,
]);
@@ -165,28 +198,28 @@ describe('GeminiIgnoreParser', () => {
});
it('should return all existing file paths in reverse order', () => {
- const parser = new IgnoreFileParser(projectRoot, [
+ const parser = new IgnoreFileParser(workspaceRoot, [
'nonexistent.ignore',
primaryFile,
secondaryFile,
]);
expect(parser.getIgnoreFilePaths()).toEqual([
- path.join(projectRoot, secondaryFile),
- path.join(projectRoot, primaryFile),
+ path.join(workspaceRoot, secondaryFile),
+ path.join(workspaceRoot, primaryFile),
]);
});
});
describe('when patterns are passed directly', () => {
it('should ignore files matching the passed patterns', () => {
- const parser = new IgnoreFileParser(projectRoot, ['*.log'], true);
+ const parser = new IgnoreFileParser(workspaceRoot, ['*.log'], true);
expect(parser.isIgnored('debug.log')).toBe(true);
expect(parser.isIgnored('src/index.ts')).toBe(false);
});
it('should handle multiple patterns', () => {
const parser = new IgnoreFileParser(
- projectRoot,
+ workspaceRoot,
['*.log', 'temp/'],
true,
);
@@ -197,7 +230,7 @@ describe('GeminiIgnoreParser', () => {
it('should respect precedence (later patterns override earlier ones)', () => {
const parser = new IgnoreFileParser(
- projectRoot,
+ workspaceRoot,
['*.txt', '!important.txt'],
true,
);
@@ -206,13 +239,13 @@ describe('GeminiIgnoreParser', () => {
});
it('should return empty array for getIgnoreFilePaths', () => {
- const parser = new IgnoreFileParser(projectRoot, ['*.log'], true);
+ const parser = new IgnoreFileParser(workspaceRoot, ['*.log'], true);
expect(parser.getIgnoreFilePaths()).toEqual([]);
});
it('should return patterns via getPatterns', () => {
const patterns = ['*.log', '!debug.log'];
- const parser = new IgnoreFileParser(projectRoot, patterns, true);
+ const parser = new IgnoreFileParser(workspaceRoot, patterns, true);
expect(parser.getPatterns()).toEqual(patterns);
});
});
diff --git a/packages/core/src/utils/memoryDiscovery.test.ts b/packages/core/src/utils/memoryDiscovery.test.ts
index 3df110d678..b5e23c91da 100644
--- a/packages/core/src/utils/memoryDiscovery.test.ts
+++ b/packages/core/src/utils/memoryDiscovery.test.ts
@@ -66,7 +66,7 @@ describe('memoryDiscovery', () => {
const DEFAULT_FOLDER_TRUST = true;
let testRootDir: string;
let cwd: string;
- let projectRoot: string;
+ let workspaceRoot: string;
let homedir: string;
async function createEmptyDir(fullPath: string) {
@@ -92,8 +92,8 @@ describe('memoryDiscovery', () => {
vi.stubEnv('NODE_ENV', 'test');
vi.stubEnv('VITEST', 'true');
- projectRoot = await createEmptyDir(path.join(testRootDir, 'project'));
- cwd = await createEmptyDir(path.join(projectRoot, 'src'));
+ workspaceRoot = await createEmptyDir(path.join(testRootDir, 'project'));
+ cwd = await createEmptyDir(path.join(workspaceRoot, 'src'));
homedir = await createEmptyDir(path.join(testRootDir, 'userhome'));
vi.mocked(os.homedir).mockReturnValue(homedir);
vi.mocked(pathsHomedir).mockReturnValue(homedir);
@@ -119,8 +119,8 @@ describe('memoryDiscovery', () => {
describe('when untrusted', () => {
it('does not load context files from untrusted workspaces', async () => {
await createTestFile(
- path.join(projectRoot, DEFAULT_CONTEXT_FILENAME),
- 'Project root memory',
+ path.join(workspaceRoot, DEFAULT_CONTEXT_FILENAME),
+ 'Workspace root memory',
);
await createTestFile(
path.join(cwd, DEFAULT_CONTEXT_FILENAME),
@@ -131,7 +131,7 @@ describe('memoryDiscovery', () => {
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
false, // untrusted
),
@@ -146,8 +146,8 @@ describe('memoryDiscovery', () => {
it('loads context from outside the untrusted workspace', async () => {
await createTestFile(
- path.join(projectRoot, DEFAULT_CONTEXT_FILENAME),
- 'Project root memory', // Untrusted
+ path.join(workspaceRoot, DEFAULT_CONTEXT_FILENAME),
+ 'Workspace root memory', // Untrusted
);
await createTestFile(
path.join(cwd, DEFAULT_CONTEXT_FILENAME),
@@ -168,7 +168,7 @@ describe('memoryDiscovery', () => {
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
false, // untrusted
),
@@ -186,7 +186,7 @@ describe('memoryDiscovery', () => {
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
@@ -210,7 +210,7 @@ describe('memoryDiscovery', () => {
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
@@ -243,7 +243,7 @@ default context content
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
@@ -264,8 +264,8 @@ custom context content
setGeminiMdFilename(customFilename);
const projectContextFile = await createTestFile(
- path.join(projectRoot, customFilename),
- 'project context content',
+ path.join(workspaceRoot, customFilename),
+ 'workspace context content',
);
const cwdContextFile = await createTestFile(
path.join(cwd, customFilename),
@@ -277,16 +277,16 @@ custom context content
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
);
expect(result).toEqual({
- memoryContent: `--- Project ---
+ memoryContent: `--- Workspace ---
--- Context from: ${normMarker(path.relative(cwd, projectContextFile))} ---
-project context content
+workspace context content
--- End of Context from: ${normMarker(path.relative(cwd, projectContextFile))} ---
--- Context from: ${normMarker(path.relative(cwd, cwdContextFile))} ---
@@ -315,14 +315,14 @@ cwd context content
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
);
expect(result).toEqual({
- memoryContent: `--- Project ---
+ memoryContent: `--- Workspace ---
--- Context from: ${normMarker(customFilename)} ---
CWD custom memory
--- End of Context from: ${normMarker(customFilename)} ---
@@ -336,9 +336,9 @@ Subdir custom memory
});
it('should load ORIGINAL_GEMINI_MD_FILENAME files by upward traversal from CWD to project root', async () => {
- const projectRootGeminiFile = await createTestFile(
- path.join(projectRoot, DEFAULT_CONTEXT_FILENAME),
- 'Project root memory',
+ const workspaceRootGeminiFile = await createTestFile(
+ path.join(workspaceRoot, DEFAULT_CONTEXT_FILENAME),
+ 'Workspace root memory',
);
const srcGeminiFile = await createTestFile(
path.join(cwd, DEFAULT_CONTEXT_FILENAME),
@@ -350,23 +350,23 @@ Subdir custom memory
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
);
expect(result).toEqual({
- memoryContent: `--- Project ---
---- Context from: ${normMarker(path.relative(cwd, projectRootGeminiFile))} ---
-Project root memory
---- End of Context from: ${normMarker(path.relative(cwd, projectRootGeminiFile))} ---
+ memoryContent: `--- Workspace ---
+--- Context from: ${normMarker(path.relative(cwd, workspaceRootGeminiFile))} ---
+Workspace root memory
+--- End of Context from: ${normMarker(path.relative(cwd, workspaceRootGeminiFile))} ---
--- Context from: ${normMarker(path.relative(cwd, srcGeminiFile))} ---
Src directory memory
--- End of Context from: ${normMarker(path.relative(cwd, srcGeminiFile))} ---`,
fileCount: 2,
- filePaths: [projectRootGeminiFile, srcGeminiFile],
+ filePaths: [workspaceRootGeminiFile, srcGeminiFile],
});
});
@@ -385,14 +385,14 @@ Src directory memory
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
);
expect(result).toEqual({
- memoryContent: `--- Project ---
+ memoryContent: `--- Workspace ---
--- Context from: ${normMarker(DEFAULT_CONTEXT_FILENAME)} ---
CWD memory
--- End of Context from: ${normMarker(DEFAULT_CONTEXT_FILENAME)} ---
@@ -412,11 +412,11 @@ Subdir memory
);
const rootGeminiFile = await createTestFile(
path.join(testRootDir, DEFAULT_CONTEXT_FILENAME),
- 'Project parent memory',
+ 'Workspace parent memory',
);
- const projectRootGeminiFile = await createTestFile(
- path.join(projectRoot, DEFAULT_CONTEXT_FILENAME),
- 'Project root memory',
+ const workspaceRootGeminiFile = await createTestFile(
+ path.join(workspaceRoot, DEFAULT_CONTEXT_FILENAME),
+ 'Workspace root memory',
);
const cwdGeminiFile = await createTestFile(
path.join(cwd, DEFAULT_CONTEXT_FILENAME),
@@ -432,7 +432,7 @@ Subdir memory
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
@@ -444,14 +444,14 @@ Subdir memory
default context content
--- End of Context from: ${normMarker(path.relative(cwd, defaultContextFile))} ---
---- Project ---
+--- Workspace ---
--- Context from: ${normMarker(path.relative(cwd, rootGeminiFile))} ---
-Project parent memory
+Workspace parent memory
--- End of Context from: ${normMarker(path.relative(cwd, rootGeminiFile))} ---
---- Context from: ${normMarker(path.relative(cwd, projectRootGeminiFile))} ---
-Project root memory
---- End of Context from: ${normMarker(path.relative(cwd, projectRootGeminiFile))} ---
+--- Context from: ${normMarker(path.relative(cwd, workspaceRootGeminiFile))} ---
+Workspace root memory
+--- End of Context from: ${normMarker(path.relative(cwd, workspaceRootGeminiFile))} ---
--- Context from: ${normMarker(path.relative(cwd, cwdGeminiFile))} ---
CWD memory
@@ -464,7 +464,7 @@ Subdir memory
filePaths: [
defaultContextFile,
rootGeminiFile,
- projectRootGeminiFile,
+ workspaceRootGeminiFile,
cwdGeminiFile,
subDirGeminiFile,
],
@@ -472,8 +472,11 @@ Subdir memory
});
it('should ignore specified directories during downward scan', async () => {
- await createEmptyDir(path.join(projectRoot, '.git'));
- await createTestFile(path.join(projectRoot, '.gitignore'), 'node_modules');
+ await createEmptyDir(path.join(workspaceRoot, '.git'));
+ await createTestFile(
+ path.join(workspaceRoot, '.gitignore'),
+ 'node_modules',
+ );
await createTestFile(
path.join(cwd, 'node_modules', DEFAULT_CONTEXT_FILENAME),
@@ -489,7 +492,7 @@ Subdir memory
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
'tree',
@@ -503,7 +506,7 @@ Subdir memory
);
expect(result).toEqual({
- memoryContent: `--- Project ---
+ memoryContent: `--- Workspace ---
--- Context from: ${normMarker(path.relative(cwd, regularSubDirGeminiFile))} ---
My code memory
--- End of Context from: ${normMarker(path.relative(cwd, regularSubDirGeminiFile))} ---`,
@@ -528,7 +531,7 @@ My code memory
cwd,
[],
true,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
'tree', // importFormat
@@ -552,7 +555,7 @@ My code memory
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
@@ -576,7 +579,7 @@ My code memory
cwd,
[],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([
{
contextFiles: [extensionFilePath],
@@ -611,14 +614,14 @@ Extension memory content
cwd,
[includedDir],
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
);
expect(result).toEqual({
- memoryContent: `--- Project ---
+ memoryContent: `--- Workspace ---
--- Context from: ${normMarker(path.relative(cwd, includedFile))} ---
included directory memory
--- End of Context from: ${normMarker(path.relative(cwd, includedFile))} ---`,
@@ -649,7 +652,7 @@ included directory memory
cwd,
createdFiles.map((f) => path.dirname(f)),
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
@@ -687,7 +690,7 @@ included directory memory
parentDir,
[childDir, parentDir], // Deliberately include duplicates
false,
- new FileDiscoveryService(projectRoot),
+ new FileDiscoveryService(workspaceRoot),
new SimpleExtensionLoader([]),
DEFAULT_FOLDER_TRUST,
),
@@ -1030,7 +1033,7 @@ included directory memory
getDebugMode: vi.fn().mockReturnValue(false),
getFileService: vi
.fn()
- .mockReturnValue(new FileDiscoveryService(projectRoot)),
+ .mockReturnValue(new FileDiscoveryService(workspaceRoot)),
getExtensionLoader: vi
.fn()
.mockReturnValue(new SimpleExtensionLoader([])),
@@ -1054,14 +1057,14 @@ included directory memory
expect(mockConfig.setUserMemory).toHaveBeenCalledWith(
expect.objectContaining({
- project: expect.stringContaining(
+ workspace: expect.stringContaining(
"# Instructions for MCP Server 'extension-server'",
),
}),
);
expect(mockConfig.setUserMemory).toHaveBeenCalledWith(
expect.objectContaining({
- project: expect.stringContaining('Always be polite.'),
+ workspace: expect.stringContaining('Always be polite.'),
}),
);
});
diff --git a/packages/core/src/utils/memoryImportProcessor.test.ts b/packages/core/src/utils/memoryImportProcessor.test.ts
index 3c9a74b604..219bd19f82 100644
--- a/packages/core/src/utils/memoryImportProcessor.test.ts
+++ b/packages/core/src/utils/memoryImportProcessor.test.ts
@@ -297,8 +297,8 @@ describe('memoryImportProcessor', () => {
'```',
'More content @./should-import2.md',
].join('\n');
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const importedContent1 = 'Imported 1';
const importedContent2 = 'Imported 2';
// Only the imports outside code blocks should be processed
@@ -311,7 +311,7 @@ describe('memoryImportProcessor', () => {
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
);
// Use marked to verify imported content is present
@@ -338,8 +338,8 @@ describe('memoryImportProcessor', () => {
'`code with import @./should-not-import.md`',
'More content @./should-import2.md',
].join('\n');
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const importedContent1 = 'Imported 1';
const importedContent2 = 'Imported 2';
mockedFs.access.mockResolvedValue(undefined);
@@ -351,7 +351,7 @@ describe('memoryImportProcessor', () => {
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
);
// Verify imported content is present
@@ -387,8 +387,8 @@ describe('memoryImportProcessor', () => {
'Another paragraph with the same `inline code @./should-not-import.md` text.',
'More content @./should-import2.md',
].join('\n');
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const importedContent1 = 'Imported 1';
const importedContent2 = 'Imported 2';
mockedFs.access.mockResolvedValue(undefined);
@@ -400,7 +400,7 @@ describe('memoryImportProcessor', () => {
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
);
// Should process imports outside code regions
@@ -418,15 +418,15 @@ describe('memoryImportProcessor', () => {
it('should not process imports in repeated inline code blocks', async () => {
const content = '`@noimport` and `@noimport`';
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const result = await processImports(
content,
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
);
expect(result.content).toBe(content);
@@ -444,8 +444,8 @@ describe('memoryImportProcessor', () => {
it('should allow imports from parent and subdirectories within project root', async () => {
const content =
'Parent import: @../parent.md Subdir import: @./components/sub.md';
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const importedParent = 'Parent file content';
const importedSub = 'Subdir file content';
mockedFs.access.mockResolvedValue(undefined);
@@ -457,7 +457,7 @@ describe('memoryImportProcessor', () => {
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
);
expect(result.content).toContain(importedParent);
expect(result.content).toContain(importedSub);
@@ -465,14 +465,14 @@ describe('memoryImportProcessor', () => {
it('should reject imports outside project root', async () => {
const content = 'Outside import: @../../../etc/passwd';
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const result = await processImports(
content,
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
);
expect(result.content).toContain(
'',
@@ -481,8 +481,8 @@ describe('memoryImportProcessor', () => {
it('should build import tree structure', async () => {
const content = 'Main content @./nested.md @./simple.md';
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const nestedContent = 'Nested @./inner.md content';
const simpleContent = 'Simple content';
const innerContent = 'Inner content';
@@ -527,27 +527,27 @@ describe('memoryImportProcessor', () => {
// First import: nested.md
// Check that the paths match using includes to handle potential absolute/relative differences
- const expectedNestedPath = testPath(projectRoot, 'src', 'nested.md');
+ const expectedNestedPath = testPath(workspaceRoot, 'src', 'nested.md');
expect(result.importTree.imports![0].path).toContain(expectedNestedPath);
expect(result.importTree.imports![0].imports).toHaveLength(1);
- const expectedInnerPath = testPath(projectRoot, 'src', 'inner.md');
+ const expectedInnerPath = testPath(workspaceRoot, 'src', 'inner.md');
expect(result.importTree.imports![0].imports![0].path).toContain(
expectedInnerPath,
);
expect(result.importTree.imports![0].imports![0].imports).toBeUndefined();
// Second import: simple.md
- const expectedSimplePath = testPath(projectRoot, 'src', 'simple.md');
+ const expectedSimplePath = testPath(workspaceRoot, 'src', 'simple.md');
expect(result.importTree.imports![1].path).toContain(expectedSimplePath);
expect(result.importTree.imports![1].imports).toBeUndefined();
});
it('should produce flat output in Claude-style with unique files in order', async () => {
const content = 'Main @./nested.md content @./simple.md';
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const nestedContent = 'Nested @./inner.md content';
const simpleContent = 'Simple content';
const innerContent = 'Inner content';
@@ -563,7 +563,7 @@ describe('memoryImportProcessor', () => {
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
'flat',
);
@@ -622,8 +622,8 @@ describe('memoryImportProcessor', () => {
it('should not duplicate files in flat output if imported multiple times', async () => {
const content = 'Main @./dup.md again @./dup.md';
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const dupContent = 'Duplicated content';
// Reset mocks
@@ -639,7 +639,7 @@ describe('memoryImportProcessor', () => {
basePath,
true, // followImports
undefined, // allowedPaths
- projectRoot,
+ workspaceRoot,
'flat', // outputFormat
);
@@ -656,8 +656,8 @@ describe('memoryImportProcessor', () => {
it('should handle nested imports in flat output', async () => {
const content = 'Root @./a.md';
- const projectRoot = testPath('test', 'project');
- const basePath = testPath(projectRoot, 'src');
+ const workspaceRoot = testPath('test', 'project');
+ const basePath = testPath(workspaceRoot, 'src');
const aContent = 'A @./b.md';
const bContent = 'B content';
@@ -671,7 +671,7 @@ describe('memoryImportProcessor', () => {
basePath,
true,
undefined,
- projectRoot,
+ workspaceRoot,
'flat',
);
diff --git a/packages/core/src/utils/nextSpeakerChecker.test.ts b/packages/core/src/utils/nextSpeakerChecker.test.ts
index fbf3bb8b90..d0a9836889 100644
--- a/packages/core/src/utils/nextSpeakerChecker.test.ts
+++ b/packages/core/src/utils/nextSpeakerChecker.test.ts
@@ -62,11 +62,14 @@ describe('checkNextSpeaker', () => {
generateContentConfig: {},
};
mockConfig = {
- getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceRoot: vi.fn().mockReturnValue('/test/project/root'),
+ getWorkspaceContext: vi.fn().mockReturnValue({
+ targetDir: '/test/project/root',
+ }),
getSessionId: vi.fn().mockReturnValue('test-session-id'),
getModel: () => 'test-model',
storage: {
- getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
+ getWorkspaceTempDir: vi.fn().mockReturnValue('/test/temp'),
},
modelConfigService: {
getResolvedConfig: vi.fn().mockReturnValue(mockResolvedConfig),
diff --git a/src/services/test-data/resolved-aliases-retry.golden.json b/src/services/test-data/resolved-aliases-retry.golden.json
new file mode 100644
index 0000000000..bb6dabdd6b
--- /dev/null
+++ b/src/services/test-data/resolved-aliases-retry.golden.json
@@ -0,0 +1,256 @@
+{
+ "base": {
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "chat-base": {
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true
+ },
+ "topK": 64
+ }
+ },
+ "chat-base-2.5": {
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "chat-base-3": {
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingLevel": "HIGH"
+ },
+ "topK": 64
+ }
+ },
+ "gemini-3-pro-preview": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingLevel": "HIGH"
+ },
+ "topK": 64
+ }
+ },
+ "gemini-3-flash-preview": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingLevel": "HIGH"
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-pro": {
+ "model": "gemini-2.5-pro",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-flash": {
+ "model": "gemini-2.5-flash",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-flash-lite": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-flash-base": {
+ "model": "gemini-2.5-flash",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "gemini-3-flash-base": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "classifier": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "maxOutputTokens": 1024,
+ "thinkingConfig": {
+ "thinkingBudget": 512
+ }
+ }
+ },
+ "prompt-completion": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0.3,
+ "topP": 1,
+ "maxOutputTokens": 16000,
+ "thinkingConfig": {
+ "thinkingBudget": 0
+ }
+ }
+ },
+ "fast-ack-helper": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0.2,
+ "topP": 1,
+ "maxOutputTokens": 120,
+ "thinkingConfig": {
+ "thinkingBudget": 0
+ }
+ }
+ },
+ "edit-corrector": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "thinkingConfig": {
+ "thinkingBudget": 0
+ }
+ }
+ },
+ "summarizer-default": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "maxOutputTokens": 2000
+ }
+ },
+ "summarizer-shell": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "maxOutputTokens": 2000
+ }
+ },
+ "web-search": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "tools": [
+ {
+ "googleSearch": {}
+ }
+ ]
+ }
+ },
+ "web-fetch": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "tools": [
+ {
+ "urlContext": {}
+ }
+ ]
+ }
+ },
+ "web-fetch-fallback": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "loop-detection": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "loop-detection-double-check": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "llm-edit-fixer": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "next-speaker-checker": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "chat-compression-3-pro": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {}
+ },
+ "chat-compression-3-flash": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {}
+ },
+ "chat-compression-2.5-pro": {
+ "model": "gemini-2.5-pro",
+ "generateContentConfig": {}
+ },
+ "chat-compression-2.5-flash": {
+ "model": "gemini-2.5-flash",
+ "generateContentConfig": {}
+ },
+ "chat-compression-2.5-flash-lite": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {}
+ },
+ "chat-compression-default": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {}
+ }
+}
diff --git a/src/services/test-data/resolved-aliases.golden.json b/src/services/test-data/resolved-aliases.golden.json
new file mode 100644
index 0000000000..bb6dabdd6b
--- /dev/null
+++ b/src/services/test-data/resolved-aliases.golden.json
@@ -0,0 +1,256 @@
+{
+ "base": {
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "chat-base": {
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true
+ },
+ "topK": 64
+ }
+ },
+ "chat-base-2.5": {
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "chat-base-3": {
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingLevel": "HIGH"
+ },
+ "topK": 64
+ }
+ },
+ "gemini-3-pro-preview": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingLevel": "HIGH"
+ },
+ "topK": 64
+ }
+ },
+ "gemini-3-flash-preview": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingLevel": "HIGH"
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-pro": {
+ "model": "gemini-2.5-pro",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-flash": {
+ "model": "gemini-2.5-flash",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-flash-lite": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 1,
+ "topP": 0.95,
+ "thinkingConfig": {
+ "includeThoughts": true,
+ "thinkingBudget": 8192
+ },
+ "topK": 64
+ }
+ },
+ "gemini-2.5-flash-base": {
+ "model": "gemini-2.5-flash",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "gemini-3-flash-base": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "classifier": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "maxOutputTokens": 1024,
+ "thinkingConfig": {
+ "thinkingBudget": 512
+ }
+ }
+ },
+ "prompt-completion": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0.3,
+ "topP": 1,
+ "maxOutputTokens": 16000,
+ "thinkingConfig": {
+ "thinkingBudget": 0
+ }
+ }
+ },
+ "fast-ack-helper": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0.2,
+ "topP": 1,
+ "maxOutputTokens": 120,
+ "thinkingConfig": {
+ "thinkingBudget": 0
+ }
+ }
+ },
+ "edit-corrector": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "thinkingConfig": {
+ "thinkingBudget": 0
+ }
+ }
+ },
+ "summarizer-default": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "maxOutputTokens": 2000
+ }
+ },
+ "summarizer-shell": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "maxOutputTokens": 2000
+ }
+ },
+ "web-search": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "tools": [
+ {
+ "googleSearch": {}
+ }
+ ]
+ }
+ },
+ "web-fetch": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1,
+ "tools": [
+ {
+ "urlContext": {}
+ }
+ ]
+ }
+ },
+ "web-fetch-fallback": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "loop-detection": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "loop-detection-double-check": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "llm-edit-fixer": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "next-speaker-checker": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {
+ "temperature": 0,
+ "topP": 1
+ }
+ },
+ "chat-compression-3-pro": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {}
+ },
+ "chat-compression-3-flash": {
+ "model": "gemini-3-flash-preview",
+ "generateContentConfig": {}
+ },
+ "chat-compression-2.5-pro": {
+ "model": "gemini-2.5-pro",
+ "generateContentConfig": {}
+ },
+ "chat-compression-2.5-flash": {
+ "model": "gemini-2.5-flash",
+ "generateContentConfig": {}
+ },
+ "chat-compression-2.5-flash-lite": {
+ "model": "gemini-2.5-flash-lite",
+ "generateContentConfig": {}
+ },
+ "chat-compression-default": {
+ "model": "gemini-3-pro-preview",
+ "generateContentConfig": {}
+ }
+}