From 9bf5be1c4a405ca66e314f6572b4716c6c12ba5a Mon Sep 17 00:00:00 2001 From: mkorwel Date: Tue, 24 Mar 2026 15:36:46 -0700 Subject: [PATCH 1/4] docs(skills): add documentation for skill packaging and distribution --- docs/cli/creating-skills.md | 50 +++++++++++++++++++++++++++++++++++-- docs/cli/skills.md | 12 +++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/docs/cli/creating-skills.md b/docs/cli/creating-skills.md index 71f7e6df8a..9a6fb9ea37 100644 --- a/docs/cli/creating-skills.md +++ b/docs/cli/creating-skills.md @@ -72,9 +72,55 @@ This skill guides the agent in conducting thorough code reviews. - **Local Changes**: If changes are local... ... ``` -- **`name`**: A unique identifier for the skill. This should match the directory - name. - **`description`**: A description of what the skill does and when Gemini should use it. - **Body**: The Markdown body of the file contains the instructions that guide the agent's behavior when the skill is active. + +## Packaging and Distribution + +To share a skill or distribute it as a standalone archive, you can package the +skill directory into a `.skill` file. This is essentially a ZIP archive that +the `gemini skills install` command can process. + +### Using the `skill-creator` toolchain + +The built-in `skill-creator` skill includes a packaging script that validates +your skill (checking for missing frontmatter, unresolved TODOs, etc.) before +creating the archive. + +To package a skill: + +1. **Activate** the `skill-creator` skill in a Gemini session. +2. **Ask** the agent to "package my skill located at `./path/to/skill`". + +The agent will run the necessary validation and create a `.skill` file in your +specified output directory. + +### Manual Packaging + +If you prefer to package manually, you can simply create a ZIP archive of the +skill's root directory (ensuring `SKILL.md` is at the top level) and rename the +extension to `.skill`. + +```bash +# Example: Package 'my-skill' directory into 'my-skill.skill' +zip -r my-skill.skill my-skill/ +``` + +## Installing a Packaged Skill + +Once you have a `.skill` file, you can install it using the `gemini` CLI. + +```bash +# Install to the user scope (global) +gemini skills install ./my-skill.skill + +# Install to the workspace scope (local repository) +gemini skills install ./my-skill.skill --scope workspace +``` + +After installation, remember to reload your session to pick up the new expertise: + +1. In an interactive session, run `/skills reload`. +2. Verify the installation with `/skills list`. diff --git a/docs/cli/skills.md b/docs/cli/skills.md index 73e5eb66eb..4ac83e7c1e 100644 --- a/docs/cli/skills.md +++ b/docs/cli/skills.md @@ -106,6 +106,18 @@ gemini skills enable my-expertise gemini skills disable my-expertise --scope workspace ``` +### Activation and Reloading + +After installing or linking a new skill, it is not automatically available in +currently active sessions. To pick up the new expertise: + +1. **In an interactive session**: Run the `/skills reload` slash command. This + refreshes the list of discovered skills from all tiers and scopes. +2. **Verify**: Run `/skills list` to confirm the new skill is discovered and + enabled. +3. **Automatic Discovery**: New sessions will automatically discover all + available skills during startup. + ## How it Works 1. **Discovery**: At the start of a session, Gemini CLI scans the discovery From 4f7c0b5890ef981b966302bd04cd1024da9236b8 Mon Sep 17 00:00:00 2001 From: matt korwel Date: Tue, 24 Mar 2026 16:46:31 -0700 Subject: [PATCH 2/4] Update docs/cli/creating-skills.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/cli/creating-skills.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli/creating-skills.md b/docs/cli/creating-skills.md index 9a6fb9ea37..6afc33ddde 100644 --- a/docs/cli/creating-skills.md +++ b/docs/cli/creating-skills.md @@ -105,7 +105,7 @@ extension to `.skill`. ```bash # Example: Package 'my-skill' directory into 'my-skill.skill' -zip -r my-skill.skill my-skill/ +(cd my-skill/ && zip -r ../my-skill.skill .) ``` ## Installing a Packaged Skill From 2d443dbd83fb2a48232a0f48e33527f445094e10 Mon Sep 17 00:00:00 2001 From: matt korwel Date: Tue, 24 Mar 2026 16:46:45 -0700 Subject: [PATCH 3/4] Update docs/cli/creating-skills.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/cli/creating-skills.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/cli/creating-skills.md b/docs/cli/creating-skills.md index 6afc33ddde..030ea07513 100644 --- a/docs/cli/creating-skills.md +++ b/docs/cli/creating-skills.md @@ -72,6 +72,7 @@ This skill guides the agent in conducting thorough code reviews. - **Local Changes**: If changes are local... ... ``` +- **`name`**: A unique identifier for the skill. This should match the directory name. - **`description`**: A description of what the skill does and when Gemini should use it. - **Body**: The Markdown body of the file contains the instructions that guide From a978de4363c6fe756647634147674b80b1cefa13 Mon Sep 17 00:00:00 2001 From: mkorwel Date: Tue, 24 Mar 2026 17:09:50 -0700 Subject: [PATCH 4/4] fix(cli): resolve linting errors in tests by using named imports from core --- docs/cli/creating-skills.md | 10 +-- .../src/commands/extensions/install.test.ts | 10 +-- packages/cli/src/config/config.test.ts | 72 +++++++++---------- .../src/config/workspace-policy-cli.test.ts | 38 +++++----- packages/cli/src/utils/worktreeSetup.test.ts | 26 +++---- 5 files changed, 77 insertions(+), 79 deletions(-) diff --git a/docs/cli/creating-skills.md b/docs/cli/creating-skills.md index 030ea07513..6e2e5eed4e 100644 --- a/docs/cli/creating-skills.md +++ b/docs/cli/creating-skills.md @@ -72,7 +72,8 @@ This skill guides the agent in conducting thorough code reviews. - **Local Changes**: If changes are local... ... ``` -- **`name`**: A unique identifier for the skill. This should match the directory name. +- **`name`**: A unique identifier for the skill. This should match the directory + name. - **`description`**: A description of what the skill does and when Gemini should use it. - **Body**: The Markdown body of the file contains the instructions that guide @@ -81,8 +82,8 @@ This skill guides the agent in conducting thorough code reviews. ## Packaging and Distribution To share a skill or distribute it as a standalone archive, you can package the -skill directory into a `.skill` file. This is essentially a ZIP archive that -the `gemini skills install` command can process. +skill directory into a `.skill` file. This is essentially a ZIP archive that the +`gemini skills install` command can process. ### Using the `skill-creator` toolchain @@ -121,7 +122,8 @@ gemini skills install ./my-skill.skill gemini skills install ./my-skill.skill --scope workspace ``` -After installation, remember to reload your session to pick up the new expertise: +After installation, remember to reload your session to pick up the new +expertise: 1. In an interactive session, run `/skills reload`. 2. Verify the installation with `/skills list`. diff --git a/packages/cli/src/commands/extensions/install.test.ts b/packages/cli/src/commands/extensions/install.test.ts index 8b3f8c5807..8ddd355c11 100644 --- a/packages/cli/src/commands/extensions/install.test.ts +++ b/packages/cli/src/commands/extensions/install.test.ts @@ -15,7 +15,7 @@ import { } from 'vitest'; import { handleInstall, installCommand } from './install.js'; import yargs from 'yargs'; -import * as core from '@google/gemini-cli-core'; +import { debugLogger } from '@google/gemini-cli-core'; import type { Stats } from 'node:fs'; import * as path from 'node:path'; import { promptForSetting } from '../../config/extensions/extensionSettings.js'; @@ -116,12 +116,8 @@ describe('handleInstall', () => { let processSpy: MockInstance; beforeEach(() => { - debugLogSpy = vi - .spyOn(core.debugLogger, 'log') - .mockImplementation(() => {}); - debugErrorSpy = vi - .spyOn(core.debugLogger, 'error') - .mockImplementation(() => {}); + debugLogSpy = vi.spyOn(debugLogger, 'log').mockImplementation(() => {}); + debugErrorSpy = vi.spyOn(debugLogger, 'error').mockImplementation(() => {}); processSpy = vi .spyOn(process, 'exit') .mockImplementation(() => undefined as never); diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index 04df366a98..0edaee809a 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -23,6 +23,11 @@ import { Storage, generalistProfile, type ContextManagementConfig, + PolicyDecision, + createPolicyEngineConfig, + loadServerHierarchicalMemory, + type Config, + TelemetryTarget, } from '@google/gemini-cli-core'; import { loadCliConfig, parseArguments, type CliArgs } from './config.js'; import { @@ -30,7 +35,6 @@ import { type MergedSettings, createTestMergedSettings, } from './settings.js'; -import * as ServerConfig from '@google/gemini-cli-core'; import { isWorkspaceTrusted } from './trustedFolders.js'; import { ExtensionManager } from './extension-manager.js'; @@ -150,9 +154,9 @@ vi.mock('@google/gemini-cli-core', async () => { rules: [], checkers: [], defaultDecision: interactive - ? ServerConfig.PolicyDecision.ASK_USER - : ServerConfig.PolicyDecision.DENY, - approvalMode: approvalMode ?? ServerConfig.ApprovalMode.DEFAULT, + ? PolicyDecision.ASK_USER + : PolicyDecision.DENY, + approvalMode: approvalMode ?? ApprovalMode.DEFAULT, nonInteractive: !interactive, }), ), @@ -979,7 +983,7 @@ describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => { ]); const argv = await parseArguments(createTestMergedSettings()); await loadCliConfig(settings, 'session-id', argv); - expect(ServerConfig.loadServerHierarchicalMemory).toHaveBeenCalledWith( + expect(loadServerHierarchicalMemory).toHaveBeenCalledWith( expect.any(String), [], expect.any(Object), @@ -1009,7 +1013,7 @@ describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => { const argv = await parseArguments(settings); await loadCliConfig(settings, 'session-id', argv); - expect(ServerConfig.loadServerHierarchicalMemory).toHaveBeenCalledWith( + expect(loadServerHierarchicalMemory).toHaveBeenCalledWith( expect.any(String), [includeDir], expect.any(Object), @@ -1038,7 +1042,7 @@ describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => { const argv = await parseArguments(settings); await loadCliConfig(settings, 'session-id', argv); - expect(ServerConfig.loadServerHierarchicalMemory).toHaveBeenCalledWith( + expect(loadServerHierarchicalMemory).toHaveBeenCalledWith( expect.any(String), [], expect.any(Object), @@ -2739,7 +2743,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); it('should set YOLO approval mode when --yolo flag is used', async () => { @@ -2750,7 +2754,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO); + expect(config.getApprovalMode()).toBe(ApprovalMode.YOLO); }); it('should set YOLO approval mode when -y flag is used', async () => { @@ -2761,7 +2765,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO); + expect(config.getApprovalMode()).toBe(ApprovalMode.YOLO); }); it('should set DEFAULT approval mode when --approval-mode=default', async () => { @@ -2772,7 +2776,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); it('should set AUTO_EDIT approval mode when --approval-mode=auto_edit', async () => { @@ -2783,7 +2787,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.AUTO_EDIT); + expect(config.getApprovalMode()).toBe(ApprovalMode.AUTO_EDIT); }); it('should set YOLO approval mode when --approval-mode=yolo', async () => { @@ -2794,7 +2798,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO); + expect(config.getApprovalMode()).toBe(ApprovalMode.YOLO); }); it('should prioritize --approval-mode over --yolo when both would be valid (but validation prevents this)', async () => { @@ -2809,7 +2813,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); it('should fall back to --yolo behavior when --approval-mode is not set', async () => { @@ -2820,7 +2824,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO); + expect(config.getApprovalMode()).toBe(ApprovalMode.YOLO); }); it('should set Plan approval mode when --approval-mode=plan is used and plan is enabled', async () => { @@ -2832,7 +2836,7 @@ describe('loadCliConfig approval mode', () => { }, }); const config = await loadCliConfig(settings, 'test-session', argv); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.PLAN); + expect(config.getApprovalMode()).toBe(ApprovalMode.PLAN); }); it('should ignore "yolo" in settings.tools.approvalMode and fall back to DEFAULT', async () => { @@ -2845,7 +2849,7 @@ describe('loadCliConfig approval mode', () => { }); const argv = await parseArguments(settings); const config = await loadCliConfig(settings, 'test-session', argv); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); it('should throw error when --approval-mode=plan is used but plan is disabled', async () => { @@ -2902,7 +2906,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); it('should override --approval-mode=auto_edit to DEFAULT', async () => { @@ -2913,7 +2917,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); it('should override --yolo flag to DEFAULT', async () => { @@ -2924,7 +2928,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); it('should remain DEFAULT when --approval-mode=default', async () => { @@ -2935,7 +2939,7 @@ describe('loadCliConfig approval mode', () => { 'test-session', argv, ); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT); + expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT); }); }); @@ -2947,9 +2951,7 @@ describe('loadCliConfig approval mode', () => { }); const argv = await parseArguments(settings); const config = await loadCliConfig(settings, 'test-session', argv); - expect(config.getApprovalMode()).toBe( - ServerConfig.ApprovalMode.AUTO_EDIT, - ); + expect(config.getApprovalMode()).toBe(ApprovalMode.AUTO_EDIT); }); it('should prioritize --approval-mode flag over settings', async () => { @@ -2959,9 +2961,7 @@ describe('loadCliConfig approval mode', () => { }); const argv = await parseArguments(settings); const config = await loadCliConfig(settings, 'test-session', argv); - expect(config.getApprovalMode()).toBe( - ServerConfig.ApprovalMode.AUTO_EDIT, - ); + expect(config.getApprovalMode()).toBe(ApprovalMode.AUTO_EDIT); }); it('should prioritize --yolo flag over settings', async () => { @@ -2971,7 +2971,7 @@ describe('loadCliConfig approval mode', () => { }); const argv = await parseArguments(settings); const config = await loadCliConfig(settings, 'test-session', argv); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO); + expect(config.getApprovalMode()).toBe(ApprovalMode.YOLO); }); it('should respect plan mode from settings when plan is enabled', async () => { @@ -2984,7 +2984,7 @@ describe('loadCliConfig approval mode', () => { }); const argv = await parseArguments(settings); const config = await loadCliConfig(settings, 'test-session', argv); - expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.PLAN); + expect(config.getApprovalMode()).toBe(ApprovalMode.PLAN); }); it('should fall back to default if plan mode is in settings but disabled', async () => { @@ -3084,7 +3084,7 @@ describe('loadCliConfig fileFiltering', () => { >; const testCases: Array<{ property: keyof FileFilteringSettings; - getter: (config: ServerConfig.Config) => boolean; + getter: (config: Config) => boolean; value: boolean; }> = [ { @@ -3328,7 +3328,7 @@ describe('Telemetry configuration via environment variables', () => { process.argv = ['node', 'script.js']; const argv = await parseArguments(createTestMergedSettings()); const settings = createTestMergedSettings({ - telemetry: { target: ServerConfig.TelemetryTarget.LOCAL }, + telemetry: { target: TelemetryTarget.LOCAL }, }); const config = await loadCliConfig(settings, 'test-session', argv); expect(config.getTelemetryTarget()).toBe('gcp'); @@ -3339,7 +3339,7 @@ describe('Telemetry configuration via environment variables', () => { process.argv = ['node', 'script.js']; const argv = await parseArguments(createTestMergedSettings()); const settings = createTestMergedSettings({ - telemetry: { target: ServerConfig.TelemetryTarget.GCP }, + telemetry: { target: TelemetryTarget.GCP }, }); await expect(loadCliConfig(settings, 'test-session', argv)).rejects.toThrow( /Invalid telemetry configuration: .*Invalid telemetry target/i, @@ -3417,7 +3417,7 @@ describe('Telemetry configuration via environment variables', () => { process.argv = ['node', 'script.js']; const argv = await parseArguments(createTestMergedSettings()); const settings = createTestMergedSettings({ - telemetry: { target: ServerConfig.TelemetryTarget.LOCAL }, + telemetry: { target: TelemetryTarget.LOCAL }, }); const config = await loadCliConfig(settings, 'test-session', argv); expect(config.getTelemetryTarget()).toBe('local'); @@ -3541,7 +3541,7 @@ describe('Policy Engine Integration in loadCliConfig', () => { await loadCliConfig(settings, 'test-session', argv); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ tools: expect.objectContaining({ allowed: expect.arrayContaining(['cli-tool']), @@ -3564,7 +3564,7 @@ describe('Policy Engine Integration in loadCliConfig', () => { await loadCliConfig(settings, 'test-session', argv); // In non-interactive mode, only ask_user is excluded by default - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ tools: expect.objectContaining({ exclude: expect.arrayContaining([ASK_USER_TOOL_NAME]), @@ -3588,7 +3588,7 @@ describe('Policy Engine Integration in loadCliConfig', () => { await loadCliConfig(settings, 'test-session', argv); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ policyPaths: [ path.normalize('/path/to/policy1.toml'), diff --git a/packages/cli/src/config/workspace-policy-cli.test.ts b/packages/cli/src/config/workspace-policy-cli.test.ts index bd9bcd0105..39868b0c3d 100644 --- a/packages/cli/src/config/workspace-policy-cli.test.ts +++ b/packages/cli/src/config/workspace-policy-cli.test.ts @@ -6,9 +6,13 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import * as path from 'node:path'; +import { + isHeadlessMode, + Storage, + createPolicyEngineConfig, +} from '@google/gemini-cli-core'; import { loadCliConfig, type CliArgs } from './config.js'; import { createTestMergedSettings } from './settings.js'; -import * as ServerConfig from '@google/gemini-cli-core'; import { isWorkspaceTrusted } from './trustedFolders.js'; import * as Policy from './policy.js'; @@ -21,9 +25,9 @@ const mockCheckIntegrity = vi.fn(); const mockAcceptIntegrity = vi.fn(); vi.mock('@google/gemini-cli-core', async () => { - const actual = await vi.importActual( - '@google/gemini-cli-core', - ); + const actual = await vi.importActual< + typeof import('@google/gemini-cli-core') + >('@google/gemini-cli-core'); return { ...actual, loadServerHierarchicalMemory: vi.fn().mockResolvedValue({ @@ -61,11 +65,11 @@ describe('Workspace-Level Policy CLI Integration', () => { hash: 'test-hash', fileCount: 1, }); - vi.mocked(ServerConfig.isHeadlessMode).mockReturnValue(false); + vi.mocked(isHeadlessMode).mockReturnValue(false); }); it('should have getWorkspacePoliciesDir on Storage class', () => { - const storage = new ServerConfig.Storage(MOCK_CWD); + const storage = new Storage(MOCK_CWD); expect(storage.getWorkspacePoliciesDir).toBeDefined(); expect(typeof storage.getWorkspacePoliciesDir).toBe('function'); }); @@ -81,7 +85,7 @@ describe('Workspace-Level Policy CLI Integration', () => { await loadCliConfig(settings, 'test-session', argv, { cwd: MOCK_CWD }); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ workspacePoliciesDir: expect.stringContaining( path.join('.gemini', 'policies'), @@ -104,7 +108,7 @@ describe('Workspace-Level Policy CLI Integration', () => { await loadCliConfig(settings, 'test-session', argv, { cwd: MOCK_CWD }); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ workspacePoliciesDir: undefined, }), @@ -130,7 +134,7 @@ describe('Workspace-Level Policy CLI Integration', () => { await loadCliConfig(settings, 'test-session', argv, { cwd: MOCK_CWD }); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ workspacePoliciesDir: undefined, }), @@ -150,7 +154,7 @@ describe('Workspace-Level Policy CLI Integration', () => { hash: 'new-hash', fileCount: 1, }); - vi.mocked(ServerConfig.isHeadlessMode).mockReturnValue(true); // Non-interactive + vi.mocked(isHeadlessMode).mockReturnValue(true); // Non-interactive const settings = createTestMergedSettings(); const argv = { prompt: 'do something' } as unknown as CliArgs; @@ -162,7 +166,7 @@ describe('Workspace-Level Policy CLI Integration', () => { MOCK_CWD, 'new-hash', ); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ workspacePoliciesDir: expect.stringContaining( path.join('.gemini', 'policies'), @@ -184,7 +188,7 @@ describe('Workspace-Level Policy CLI Integration', () => { hash: 'new-hash', fileCount: 1, }); - vi.mocked(ServerConfig.isHeadlessMode).mockReturnValue(false); // Interactive + vi.mocked(isHeadlessMode).mockReturnValue(false); // Interactive const settings = createTestMergedSettings(); const argv = { @@ -202,7 +206,7 @@ describe('Workspace-Level Policy CLI Integration', () => { MOCK_CWD, 'new-hash', ); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ workspacePoliciesDir: expect.stringContaining( path.join('.gemini', 'policies'), @@ -224,7 +228,7 @@ describe('Workspace-Level Policy CLI Integration', () => { hash: 'new-hash', fileCount: 5, }); - vi.mocked(ServerConfig.isHeadlessMode).mockReturnValue(false); // Interactive + vi.mocked(isHeadlessMode).mockReturnValue(false); // Interactive const settings = createTestMergedSettings(); const argv = { query: 'test' } as unknown as CliArgs; @@ -240,7 +244,7 @@ describe('Workspace-Level Policy CLI Integration', () => { 'new-hash', ); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ workspacePoliciesDir: expect.stringContaining( path.join('.gemini', 'policies'), @@ -267,7 +271,7 @@ describe('Workspace-Level Policy CLI Integration', () => { hash: 'new-hash', fileCount: 1, }); - vi.mocked(ServerConfig.isHeadlessMode).mockReturnValue(false); // Interactive + vi.mocked(isHeadlessMode).mockReturnValue(false); // Interactive const settings = createTestMergedSettings(); const argv = { @@ -285,7 +289,7 @@ describe('Workspace-Level Policy CLI Integration', () => { policyDir: expect.stringContaining(path.join('.gemini', 'policies')), newHash: 'new-hash', }); - expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( + expect(createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ workspacePoliciesDir: undefined, }), diff --git a/packages/cli/src/utils/worktreeSetup.test.ts b/packages/cli/src/utils/worktreeSetup.test.ts index e1bd201a8b..9b6087628a 100644 --- a/packages/cli/src/utils/worktreeSetup.test.ts +++ b/packages/cli/src/utils/worktreeSetup.test.ts @@ -6,7 +6,11 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { setupWorktree } from './worktreeSetup.js'; -import * as coreFunctions from '@google/gemini-cli-core'; +import { + getProjectRootForWorktree, + createWorktreeService, + writeToStderr, +} from '@google/gemini-cli-core'; // Mock dependencies vi.mock('@google/gemini-cli-core', async (importOriginal) => { @@ -47,12 +51,8 @@ describe('setupWorktree', () => { }); // Mock successful execution of core utilities - vi.mocked(coreFunctions.getProjectRootForWorktree).mockResolvedValue( - '/mock/project', - ); - vi.mocked(coreFunctions.createWorktreeService).mockResolvedValue( - mockService as never, - ); + vi.mocked(getProjectRootForWorktree).mockResolvedValue('/mock/project'); + vi.mocked(createWorktreeService).mockResolvedValue(mockService as never); mockService.setup.mockResolvedValue({ name: 'my-feature', path: '/mock/project/.gemini/worktrees/my-feature', @@ -69,12 +69,8 @@ describe('setupWorktree', () => { it('should create and switch to a new worktree', async () => { await setupWorktree('my-feature'); - expect(coreFunctions.getProjectRootForWorktree).toHaveBeenCalledWith( - '/mock/project', - ); - expect(coreFunctions.createWorktreeService).toHaveBeenCalledWith( - '/mock/project', - ); + expect(getProjectRootForWorktree).toHaveBeenCalledWith('/mock/project'); + expect(createWorktreeService).toHaveBeenCalledWith('/mock/project'); expect(mockService.setup).toHaveBeenCalledWith('my-feature'); expect(process.chdir).toHaveBeenCalledWith( '/mock/project/.gemini/worktrees/my-feature', @@ -99,7 +95,7 @@ describe('setupWorktree', () => { await setupWorktree('my-feature'); - expect(coreFunctions.createWorktreeService).not.toHaveBeenCalled(); + expect(createWorktreeService).not.toHaveBeenCalled(); expect(process.chdir).not.toHaveBeenCalled(); }); @@ -112,7 +108,7 @@ describe('setupWorktree', () => { await expect(setupWorktree('my-feature')).rejects.toThrow('PROCESS_EXIT'); - expect(coreFunctions.writeToStderr).toHaveBeenCalledWith( + expect(writeToStderr).toHaveBeenCalledWith( expect.stringContaining( 'Failed to create or switch to worktree: Git failure', ),