mirror of
https://github.com/google-gemini/gemini-cli
synced 2026-04-21 13:37:17 +00:00
Merge a978de4363 into a38e2f0048
This commit is contained in:
commit
c973a8aff0
6 changed files with 132 additions and 75 deletions
|
|
@ -78,3 +78,52 @@ This skill guides the agent in conducting thorough code reviews.
|
|||
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'
|
||||
(cd my-skill/ && zip -r ../my-skill.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`.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}),
|
||||
),
|
||||
|
|
@ -986,7 +990,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),
|
||||
|
|
@ -1016,7 +1020,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),
|
||||
|
|
@ -1045,7 +1049,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),
|
||||
|
|
@ -2746,7 +2750,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 () => {
|
||||
|
|
@ -2757,7 +2761,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 () => {
|
||||
|
|
@ -2768,7 +2772,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 () => {
|
||||
|
|
@ -2779,7 +2783,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 () => {
|
||||
|
|
@ -2790,7 +2794,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 () => {
|
||||
|
|
@ -2801,7 +2805,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 () => {
|
||||
|
|
@ -2816,7 +2820,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 () => {
|
||||
|
|
@ -2827,7 +2831,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 () => {
|
||||
|
|
@ -2839,7 +2843,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 () => {
|
||||
|
|
@ -2852,7 +2856,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 () => {
|
||||
|
|
@ -2909,7 +2913,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 () => {
|
||||
|
|
@ -2920,7 +2924,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 () => {
|
||||
|
|
@ -2931,7 +2935,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 () => {
|
||||
|
|
@ -2942,7 +2946,7 @@ describe('loadCliConfig approval mode', () => {
|
|||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
||||
expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -2954,9 +2958,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 () => {
|
||||
|
|
@ -2966,9 +2968,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 () => {
|
||||
|
|
@ -2978,7 +2978,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 () => {
|
||||
|
|
@ -2991,7 +2991,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 () => {
|
||||
|
|
@ -3097,7 +3097,7 @@ describe('loadCliConfig fileFiltering', () => {
|
|||
>;
|
||||
const testCases: Array<{
|
||||
property: keyof FileFilteringSettings;
|
||||
getter: (config: ServerConfig.Config) => boolean;
|
||||
getter: (config: Config) => boolean;
|
||||
value: boolean;
|
||||
}> = [
|
||||
{
|
||||
|
|
@ -3341,7 +3341,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');
|
||||
|
|
@ -3352,7 +3352,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,
|
||||
|
|
@ -3430,7 +3430,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');
|
||||
|
|
@ -3554,7 +3554,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']),
|
||||
|
|
@ -3577,7 +3577,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]),
|
||||
|
|
@ -3601,7 +3601,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'),
|
||||
|
|
|
|||
|
|
@ -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<typeof ServerConfig>(
|
||||
'@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,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in a new issue