mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-28 14:04:41 -07:00
feat(ui): reduce home directory warning noise and add opt-out setting (#16229)
This commit is contained in:
@@ -184,6 +184,12 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
title
|
title
|
||||||
- **Default:** `false`
|
- **Default:** `false`
|
||||||
|
|
||||||
|
- **`ui.showHomeDirectoryWarning`** (boolean):
|
||||||
|
- **Description:** Show a warning when running Gemini CLI in the home
|
||||||
|
directory.
|
||||||
|
- **Default:** `true`
|
||||||
|
- **Requires restart:** Yes
|
||||||
|
|
||||||
- **`ui.hideTips`** (boolean):
|
- **`ui.hideTips`** (boolean):
|
||||||
- **Description:** Hide helpful tips in the UI
|
- **Description:** Hide helpful tips in the UI
|
||||||
- **Default:** `false`
|
- **Default:** `false`
|
||||||
|
|||||||
@@ -384,6 +384,16 @@ const SETTINGS_SCHEMA = {
|
|||||||
'Show Gemini CLI status and thoughts in the terminal window title',
|
'Show Gemini CLI status and thoughts in the terminal window title',
|
||||||
showInDialog: true,
|
showInDialog: true,
|
||||||
},
|
},
|
||||||
|
showHomeDirectoryWarning: {
|
||||||
|
type: 'boolean',
|
||||||
|
label: 'Show Home Directory Warning',
|
||||||
|
category: 'UI',
|
||||||
|
requiresRestart: true,
|
||||||
|
default: true,
|
||||||
|
description:
|
||||||
|
'Show a warning when running Gemini CLI in the home directory.',
|
||||||
|
showInDialog: true,
|
||||||
|
},
|
||||||
hideTips: {
|
hideTips: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
label: 'Hide Tips',
|
label: 'Hide Tips',
|
||||||
|
|||||||
@@ -588,7 +588,7 @@ export async function main() {
|
|||||||
let input = config.getQuestion();
|
let input = config.getQuestion();
|
||||||
const startupWarnings = [
|
const startupWarnings = [
|
||||||
...(await getStartupWarnings()),
|
...(await getStartupWarnings()),
|
||||||
...(await getUserStartupWarnings()),
|
...(await getUserStartupWarnings(settings.merged)),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Handle --resume flag
|
// Handle --resume flag
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import { getUserStartupWarnings } from './userStartupWarnings.js';
|
|||||||
import * as os from 'node:os';
|
import * as os from 'node:os';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
import {
|
||||||
|
isFolderTrustEnabled,
|
||||||
|
isWorkspaceTrusted,
|
||||||
|
} from '../config/trustedFolders.js';
|
||||||
|
|
||||||
// Mock os.homedir to control the home directory in tests
|
// Mock os.homedir to control the home directory in tests
|
||||||
vi.mock('os', async (importOriginal) => {
|
vi.mock('os', async (importOriginal) => {
|
||||||
@@ -28,6 +32,11 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
vi.mock('../config/trustedFolders.js', () => ({
|
||||||
|
isFolderTrustEnabled: vi.fn(),
|
||||||
|
isWorkspaceTrusted: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('getUserStartupWarnings', () => {
|
describe('getUserStartupWarnings', () => {
|
||||||
let testRootDir: string;
|
let testRootDir: string;
|
||||||
let homeDir: string;
|
let homeDir: string;
|
||||||
@@ -37,6 +46,11 @@ describe('getUserStartupWarnings', () => {
|
|||||||
homeDir = path.join(testRootDir, 'home');
|
homeDir = path.join(testRootDir, 'home');
|
||||||
await fs.mkdir(homeDir, { recursive: true });
|
await fs.mkdir(homeDir, { recursive: true });
|
||||||
vi.mocked(os.homedir).mockReturnValue(homeDir);
|
vi.mocked(os.homedir).mockReturnValue(homeDir);
|
||||||
|
vi.mocked(isFolderTrustEnabled).mockReturnValue(false);
|
||||||
|
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||||
|
isTrusted: false,
|
||||||
|
source: undefined,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -46,16 +60,44 @@ describe('getUserStartupWarnings', () => {
|
|||||||
|
|
||||||
describe('home directory check', () => {
|
describe('home directory check', () => {
|
||||||
it('should return a warning when running in home directory', async () => {
|
it('should return a warning when running in home directory', async () => {
|
||||||
const warnings = await getUserStartupWarnings(homeDir);
|
const warnings = await getUserStartupWarnings({}, homeDir);
|
||||||
expect(warnings).toContainEqual(
|
expect(warnings).toContainEqual(
|
||||||
expect.stringContaining('home directory'),
|
expect.stringContaining(
|
||||||
|
'Warning you are running Gemini CLI in your home directory',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(warnings).toContainEqual(
|
||||||
|
expect.stringContaining('warning can be disabled in /settings'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not return a warning when running in a project directory', async () => {
|
it('should not return a warning when running in a project directory', async () => {
|
||||||
const projectDir = path.join(testRootDir, 'project');
|
const projectDir = path.join(testRootDir, 'project');
|
||||||
await fs.mkdir(projectDir);
|
await fs.mkdir(projectDir);
|
||||||
const warnings = await getUserStartupWarnings(projectDir);
|
const warnings = await getUserStartupWarnings({}, projectDir);
|
||||||
|
expect(warnings).not.toContainEqual(
|
||||||
|
expect.stringContaining('home directory'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return a warning when showHomeDirectoryWarning is false', async () => {
|
||||||
|
const warnings = await getUserStartupWarnings(
|
||||||
|
{ ui: { showHomeDirectoryWarning: false } },
|
||||||
|
homeDir,
|
||||||
|
);
|
||||||
|
expect(warnings).not.toContainEqual(
|
||||||
|
expect.stringContaining('home directory'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return a warning when folder trust is enabled and workspace is trusted', async () => {
|
||||||
|
vi.mocked(isFolderTrustEnabled).mockReturnValue(true);
|
||||||
|
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||||
|
isTrusted: true,
|
||||||
|
source: 'file',
|
||||||
|
});
|
||||||
|
|
||||||
|
const warnings = await getUserStartupWarnings({}, homeDir);
|
||||||
expect(warnings).not.toContainEqual(
|
expect(warnings).not.toContainEqual(
|
||||||
expect.stringContaining('home directory'),
|
expect.stringContaining('home directory'),
|
||||||
);
|
);
|
||||||
@@ -65,7 +107,7 @@ describe('getUserStartupWarnings', () => {
|
|||||||
describe('root directory check', () => {
|
describe('root directory check', () => {
|
||||||
it('should return a warning when running in a root directory', async () => {
|
it('should return a warning when running in a root directory', async () => {
|
||||||
const rootDir = path.parse(testRootDir).root;
|
const rootDir = path.parse(testRootDir).root;
|
||||||
const warnings = await getUserStartupWarnings(rootDir);
|
const warnings = await getUserStartupWarnings({}, rootDir);
|
||||||
expect(warnings).toContainEqual(
|
expect(warnings).toContainEqual(
|
||||||
expect.stringContaining('root directory'),
|
expect.stringContaining('root directory'),
|
||||||
);
|
);
|
||||||
@@ -77,7 +119,7 @@ describe('getUserStartupWarnings', () => {
|
|||||||
it('should not return a warning when running in a non-root directory', async () => {
|
it('should not return a warning when running in a non-root directory', async () => {
|
||||||
const projectDir = path.join(testRootDir, 'project');
|
const projectDir = path.join(testRootDir, 'project');
|
||||||
await fs.mkdir(projectDir);
|
await fs.mkdir(projectDir);
|
||||||
const warnings = await getUserStartupWarnings(projectDir);
|
const warnings = await getUserStartupWarnings({}, projectDir);
|
||||||
expect(warnings).not.toContainEqual(
|
expect(warnings).not.toContainEqual(
|
||||||
expect.stringContaining('root directory'),
|
expect.stringContaining('root directory'),
|
||||||
);
|
);
|
||||||
@@ -87,7 +129,7 @@ describe('getUserStartupWarnings', () => {
|
|||||||
describe('error handling', () => {
|
describe('error handling', () => {
|
||||||
it('should handle errors when checking directory', async () => {
|
it('should handle errors when checking directory', async () => {
|
||||||
const nonExistentPath = path.join(testRootDir, 'non-existent');
|
const nonExistentPath = path.join(testRootDir, 'non-existent');
|
||||||
const warnings = await getUserStartupWarnings(nonExistentPath);
|
const warnings = await getUserStartupWarnings({}, nonExistentPath);
|
||||||
const expectedWarning =
|
const expectedWarning =
|
||||||
'Could not verify the current directory due to a file system error.';
|
'Could not verify the current directory due to a file system error.';
|
||||||
expect(warnings).toEqual([expectedWarning, expectedWarning]);
|
expect(warnings).toEqual([expectedWarning, expectedWarning]);
|
||||||
|
|||||||
@@ -8,16 +8,25 @@ import fs from 'node:fs/promises';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import { homedir } from '@google/gemini-cli-core';
|
import { homedir } from '@google/gemini-cli-core';
|
||||||
|
import type { Settings } from '../config/settingsSchema.js';
|
||||||
|
import {
|
||||||
|
isFolderTrustEnabled,
|
||||||
|
isWorkspaceTrusted,
|
||||||
|
} from '../config/trustedFolders.js';
|
||||||
|
|
||||||
type WarningCheck = {
|
type WarningCheck = {
|
||||||
id: string;
|
id: string;
|
||||||
check: (workspaceRoot: string) => Promise<string | null>;
|
check: (workspaceRoot: string, settings: Settings) => Promise<string | null>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Individual warning checks
|
// Individual warning checks
|
||||||
const homeDirectoryCheck: WarningCheck = {
|
const homeDirectoryCheck: WarningCheck = {
|
||||||
id: 'home-directory',
|
id: 'home-directory',
|
||||||
check: async (workspaceRoot: string) => {
|
check: async (workspaceRoot: string, settings: Settings) => {
|
||||||
|
if (settings.ui?.showHomeDirectoryWarning === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [workspaceRealPath, homeRealPath] = await Promise.all([
|
const [workspaceRealPath, homeRealPath] = await Promise.all([
|
||||||
fs.realpath(workspaceRoot),
|
fs.realpath(workspaceRoot),
|
||||||
@@ -25,7 +34,15 @@ const homeDirectoryCheck: WarningCheck = {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (workspaceRealPath === homeRealPath) {
|
if (workspaceRealPath === homeRealPath) {
|
||||||
return 'You are running Gemini CLI in your home directory. It is recommended to run in a project-specific directory.';
|
// If folder trust is enabled and the user trusts the home directory, don't show the warning.
|
||||||
|
if (
|
||||||
|
isFolderTrustEnabled(settings) &&
|
||||||
|
isWorkspaceTrusted(settings).isTrusted
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Warning you are running Gemini CLI in your home directory.\nThis warning can be disabled in /settings';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (_err: unknown) {
|
} catch (_err: unknown) {
|
||||||
@@ -36,7 +53,7 @@ const homeDirectoryCheck: WarningCheck = {
|
|||||||
|
|
||||||
const rootDirectoryCheck: WarningCheck = {
|
const rootDirectoryCheck: WarningCheck = {
|
||||||
id: 'root-directory',
|
id: 'root-directory',
|
||||||
check: async (workspaceRoot: string) => {
|
check: async (workspaceRoot: string, _settings: Settings) => {
|
||||||
try {
|
try {
|
||||||
const workspaceRealPath = await fs.realpath(workspaceRoot);
|
const workspaceRealPath = await fs.realpath(workspaceRoot);
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
@@ -61,10 +78,11 @@ const WARNING_CHECKS: readonly WarningCheck[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export async function getUserStartupWarnings(
|
export async function getUserStartupWarnings(
|
||||||
|
settings: Settings,
|
||||||
workspaceRoot: string = process.cwd(),
|
workspaceRoot: string = process.cwd(),
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
WARNING_CHECKS.map((check) => check.check(workspaceRoot)),
|
WARNING_CHECKS.map((check) => check.check(workspaceRoot, settings)),
|
||||||
);
|
);
|
||||||
return results.filter((msg) => msg !== null);
|
return results.filter((msg) => msg !== null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,6 +194,13 @@
|
|||||||
"default": false,
|
"default": false,
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"showHomeDirectoryWarning": {
|
||||||
|
"title": "Show Home Directory Warning",
|
||||||
|
"description": "Show a warning when running Gemini CLI in the home directory.",
|
||||||
|
"markdownDescription": "Show a warning when running Gemini CLI in the home directory.\n\n- Category: `UI`\n- Requires restart: `yes`\n- Default: `true`",
|
||||||
|
"default": true,
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"hideTips": {
|
"hideTips": {
|
||||||
"title": "Hide Tips",
|
"title": "Hide Tips",
|
||||||
"description": "Hide helpful tips in the UI",
|
"description": "Hide helpful tips in the UI",
|
||||||
|
|||||||
Reference in New Issue
Block a user