feat: Add custom URL support for the /bug command (#1017)
This commit is contained in:
parent
d5c6bb9740
commit
643bdf31d5
|
@ -44,6 +44,18 @@ When you create a `.gemini/settings.json` file for project-specific settings, or
|
||||||
- **Default:** `GEMINI.md`
|
- **Default:** `GEMINI.md`
|
||||||
- **Example:** `"contextFileName": "AGENTS.md"`
|
- **Example:** `"contextFileName": "AGENTS.md"`
|
||||||
|
|
||||||
|
- **`bugCommand`** (object, optional):
|
||||||
|
|
||||||
|
- **Description:** Overrides the default URL for the `/bug` command.
|
||||||
|
- **Properties:**
|
||||||
|
- **`urlTemplate`** (string, required): A URL that can contain `{title}` and `{body}` placeholders.
|
||||||
|
- **Example:**
|
||||||
|
```json
|
||||||
|
"bugCommand": {
|
||||||
|
"urlTemplate": "https://bug.example.com/new?title={title}&body={body}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
- **`fileFiltering`** (object, optional):
|
- **`fileFiltering`** (object, optional):
|
||||||
|
|
||||||
- **Description:** Controls git-aware file filtering behavior for @ commands and file discovery tools.
|
- **Description:** Controls git-aware file filtering behavior for @ commands and file discovery tools.
|
||||||
|
|
|
@ -211,6 +211,7 @@ export async function loadCliConfig(
|
||||||
telemetryOtlpEndpoint:
|
telemetryOtlpEndpoint:
|
||||||
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? settings.telemetryOtlpEndpoint,
|
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? settings.telemetryOtlpEndpoint,
|
||||||
fileDiscoveryService: fileService,
|
fileDiscoveryService: fileService,
|
||||||
|
bugCommand: settings.bugCommand,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,11 @@
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import { MCPServerConfig, getErrorMessage } from '@gemini-cli/core';
|
import {
|
||||||
|
MCPServerConfig,
|
||||||
|
getErrorMessage,
|
||||||
|
BugCommandSettings,
|
||||||
|
} from '@gemini-cli/core';
|
||||||
import stripJsonComments from 'strip-json-comments';
|
import stripJsonComments from 'strip-json-comments';
|
||||||
import { DefaultLight } from '../ui/themes/default-light.js';
|
import { DefaultLight } from '../ui/themes/default-light.js';
|
||||||
import { DefaultDark } from '../ui/themes/default.js';
|
import { DefaultDark } from '../ui/themes/default.js';
|
||||||
|
@ -40,6 +44,7 @@ export interface Settings {
|
||||||
telemetry?: boolean;
|
telemetry?: boolean;
|
||||||
telemetryOtlpEndpoint?: string;
|
telemetryOtlpEndpoint?: string;
|
||||||
preferredEditor?: string;
|
preferredEditor?: string;
|
||||||
|
bugCommand?: BugCommandSettings;
|
||||||
|
|
||||||
// Git-aware file filtering settings
|
// Git-aware file filtering settings
|
||||||
fileFiltering?: {
|
fileFiltering?: {
|
||||||
|
|
|
@ -129,6 +129,7 @@ describe('useSlashCommandProcessor', () => {
|
||||||
getModel: vi.fn(() => 'test-model'),
|
getModel: vi.fn(() => 'test-model'),
|
||||||
getProjectRoot: vi.fn(() => '/test/dir'),
|
getProjectRoot: vi.fn(() => '/test/dir'),
|
||||||
getCheckpointEnabled: vi.fn(() => true),
|
getCheckpointEnabled: vi.fn(() => true),
|
||||||
|
getBugCommand: vi.fn(() => undefined),
|
||||||
} as unknown as Config;
|
} as unknown as Config;
|
||||||
mockCorgiMode = vi.fn();
|
mockCorgiMode = vi.fn();
|
||||||
mockUseSessionStats.mockReturnValue({
|
mockUseSessionStats.mockReturnValue({
|
||||||
|
@ -418,6 +419,47 @@ Add any other context about the problem here.
|
||||||
expect(open).toHaveBeenCalledWith(expectedUrl);
|
expect(open).toHaveBeenCalledWith(expectedUrl);
|
||||||
expect(commandResult).toBe(true);
|
expect(commandResult).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use the custom bug command URL from config if available', async () => {
|
||||||
|
const bugCommand = {
|
||||||
|
urlTemplate:
|
||||||
|
'https://custom-bug-tracker.com/new?title={title}&body={body}',
|
||||||
|
};
|
||||||
|
mockConfig = {
|
||||||
|
...mockConfig,
|
||||||
|
getBugCommand: vi.fn(() => bugCommand),
|
||||||
|
} as unknown as Config;
|
||||||
|
|
||||||
|
const { handleSlashCommand } = getProcessor();
|
||||||
|
const bugDescription = 'This is a custom bug';
|
||||||
|
const diagnosticInfo = `
|
||||||
|
## Describe the bug
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
## Additional context
|
||||||
|
Add any other context about the problem here.
|
||||||
|
|
||||||
|
## Diagnostic Information
|
||||||
|
* **CLI Version:** unknown
|
||||||
|
* **Git Commit:** ${GIT_COMMIT_INFO}
|
||||||
|
* **Operating System:** test-platform test-node-version
|
||||||
|
* **Sandbox Environment:** no sandbox
|
||||||
|
* **Model Version:** test-model
|
||||||
|
* **Memory Usage:** 11.8 MB
|
||||||
|
`;
|
||||||
|
const expectedUrl = bugCommand.urlTemplate
|
||||||
|
.replace('{title}', encodeURIComponent(bugDescription))
|
||||||
|
.replace('{body}', encodeURIComponent(diagnosticInfo));
|
||||||
|
|
||||||
|
let commandResult: SlashCommandActionReturn | boolean = false;
|
||||||
|
await act(async () => {
|
||||||
|
commandResult = await handleSlashCommand(`/bug ${bugDescription}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockAddItem).toHaveBeenCalledTimes(2);
|
||||||
|
expect(open).toHaveBeenCalledWith(expectedUrl);
|
||||||
|
expect(commandResult).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('/quit and /exit commands', () => {
|
describe('/quit and /exit commands', () => {
|
||||||
|
|
|
@ -512,13 +512,14 @@ Add any other context about the problem here.
|
||||||
`;
|
`;
|
||||||
|
|
||||||
let bugReportUrl =
|
let bugReportUrl =
|
||||||
'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.md';
|
'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.md&title={title}&body={body}';
|
||||||
if (bugDescription) {
|
const bugCommand = config?.getBugCommand();
|
||||||
const encodedArgs = encodeURIComponent(bugDescription);
|
if (bugCommand?.urlTemplate) {
|
||||||
bugReportUrl += `&title=${encodedArgs}`;
|
bugReportUrl = bugCommand.urlTemplate;
|
||||||
}
|
}
|
||||||
const encodedBody = encodeURIComponent(diagnosticInfo);
|
bugReportUrl = bugReportUrl
|
||||||
bugReportUrl += `&body=${encodedBody}`;
|
.replace('{title}', encodeURIComponent(bugDescription))
|
||||||
|
.replace('{body}', encodeURIComponent(diagnosticInfo));
|
||||||
|
|
||||||
addMessage({
|
addMessage({
|
||||||
type: MessageType.INFO,
|
type: MessageType.INFO,
|
||||||
|
|
|
@ -36,6 +36,10 @@ export interface AccessibilitySettings {
|
||||||
disableLoadingPhrases?: boolean;
|
disableLoadingPhrases?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BugCommandSettings {
|
||||||
|
urlTemplate: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class MCPServerConfig {
|
export class MCPServerConfig {
|
||||||
constructor(
|
constructor(
|
||||||
// For stdio transport
|
// For stdio transport
|
||||||
|
@ -87,6 +91,7 @@ export interface ConfigParameters {
|
||||||
proxy?: string;
|
proxy?: string;
|
||||||
cwd: string;
|
cwd: string;
|
||||||
fileDiscoveryService?: FileDiscoveryService;
|
fileDiscoveryService?: FileDiscoveryService;
|
||||||
|
bugCommand?: BugCommandSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
|
@ -121,6 +126,7 @@ export class Config {
|
||||||
private readonly checkpoint: boolean;
|
private readonly checkpoint: boolean;
|
||||||
private readonly proxy: string | undefined;
|
private readonly proxy: string | undefined;
|
||||||
private readonly cwd: string;
|
private readonly cwd: string;
|
||||||
|
private readonly bugCommand: BugCommandSettings | undefined;
|
||||||
|
|
||||||
constructor(params: ConfigParameters) {
|
constructor(params: ConfigParameters) {
|
||||||
this.sessionId = params.sessionId;
|
this.sessionId = params.sessionId;
|
||||||
|
@ -154,6 +160,7 @@ export class Config {
|
||||||
this.proxy = params.proxy;
|
this.proxy = params.proxy;
|
||||||
this.cwd = params.cwd ?? process.cwd();
|
this.cwd = params.cwd ?? process.cwd();
|
||||||
this.fileDiscoveryService = params.fileDiscoveryService ?? null;
|
this.fileDiscoveryService = params.fileDiscoveryService ?? null;
|
||||||
|
this.bugCommand = params.bugCommand;
|
||||||
|
|
||||||
if (params.contextFileName) {
|
if (params.contextFileName) {
|
||||||
setGeminiMdFilename(params.contextFileName);
|
setGeminiMdFilename(params.contextFileName);
|
||||||
|
@ -309,6 +316,10 @@ export class Config {
|
||||||
return this.cwd;
|
return this.cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBugCommand(): BugCommandSettings | undefined {
|
||||||
|
return this.bugCommand;
|
||||||
|
}
|
||||||
|
|
||||||
async getFileService(): Promise<FileDiscoveryService> {
|
async getFileService(): Promise<FileDiscoveryService> {
|
||||||
if (!this.fileDiscoveryService) {
|
if (!this.fileDiscoveryService) {
|
||||||
this.fileDiscoveryService = new FileDiscoveryService(this.targetDir);
|
this.fileDiscoveryService = new FileDiscoveryService(this.targetDir);
|
||||||
|
|
Loading…
Reference in New Issue