mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
update /bug to new slash command arch (#4246)
Co-authored-by: Abhi <abhipatel@google.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
This commit is contained in:
98
packages/cli/src/ui/commands/bugCommand.test.ts
Normal file
98
packages/cli/src/ui/commands/bugCommand.test.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import open from 'open';
|
||||
import { bugCommand } from './bugCommand.js';
|
||||
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
|
||||
import { getCliVersion } from '../../utils/version.js';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
import { formatMemoryUsage } from '../utils/formatters.js';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('open');
|
||||
vi.mock('../../utils/version.js');
|
||||
vi.mock('../utils/formatters.js');
|
||||
vi.mock('node:process', () => ({
|
||||
default: {
|
||||
platform: 'test-platform',
|
||||
version: 'v20.0.0',
|
||||
// Keep other necessary process properties if needed by other parts of the code
|
||||
env: process.env,
|
||||
memoryUsage: () => ({ rss: 0 }),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('bugCommand', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(getCliVersion).mockResolvedValue('0.1.0');
|
||||
vi.mocked(formatMemoryUsage).mockReturnValue('100 MB');
|
||||
vi.stubEnv('SANDBOX', 'gemini-test');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should generate the default GitHub issue URL', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getModel: () => 'gemini-pro',
|
||||
getBugCommand: () => undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!bugCommand.action) throw new Error('Action is not defined');
|
||||
await bugCommand.action(mockContext, 'A test bug');
|
||||
|
||||
const expectedInfo = `
|
||||
* **CLI Version:** 0.1.0
|
||||
* **Git Commit:** ${GIT_COMMIT_INFO}
|
||||
* **Operating System:** test-platform v20.0.0
|
||||
* **Sandbox Environment:** test
|
||||
* **Model Version:** gemini-pro
|
||||
* **Memory Usage:** 100 MB
|
||||
`;
|
||||
const expectedUrl =
|
||||
'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title=A%20test%20bug&info=' +
|
||||
encodeURIComponent(expectedInfo);
|
||||
|
||||
expect(open).toHaveBeenCalledWith(expectedUrl);
|
||||
});
|
||||
|
||||
it('should use a custom URL template from config if provided', async () => {
|
||||
const customTemplate =
|
||||
'https://internal.bug-tracker.com/new?desc={title}&details={info}';
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getModel: () => 'gemini-pro',
|
||||
getBugCommand: () => ({ urlTemplate: customTemplate }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!bugCommand.action) throw new Error('Action is not defined');
|
||||
await bugCommand.action(mockContext, 'A custom bug');
|
||||
|
||||
const expectedInfo = `
|
||||
* **CLI Version:** 0.1.0
|
||||
* **Git Commit:** ${GIT_COMMIT_INFO}
|
||||
* **Operating System:** test-platform v20.0.0
|
||||
* **Sandbox Environment:** test
|
||||
* **Model Version:** gemini-pro
|
||||
* **Memory Usage:** 100 MB
|
||||
`;
|
||||
const expectedUrl = customTemplate
|
||||
.replace('{title}', encodeURIComponent('A custom bug'))
|
||||
.replace('{info}', encodeURIComponent(expectedInfo));
|
||||
|
||||
expect(open).toHaveBeenCalledWith(expectedUrl);
|
||||
});
|
||||
});
|
||||
78
packages/cli/src/ui/commands/bugCommand.ts
Normal file
78
packages/cli/src/ui/commands/bugCommand.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import open from 'open';
|
||||
import process from 'node:process';
|
||||
import { type CommandContext, type SlashCommand } from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
import { formatMemoryUsage } from '../utils/formatters.js';
|
||||
import { getCliVersion } from '../../utils/version.js';
|
||||
|
||||
export const bugCommand: SlashCommand = {
|
||||
name: 'bug',
|
||||
description: 'submit a bug report',
|
||||
action: async (context: CommandContext, args?: string): Promise<void> => {
|
||||
const bugDescription = (args || '').trim();
|
||||
const { config } = context.services;
|
||||
|
||||
const osVersion = `${process.platform} ${process.version}`;
|
||||
let sandboxEnv = 'no sandbox';
|
||||
if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
|
||||
sandboxEnv = process.env.SANDBOX.replace(/^gemini-(?:code-)?/, '');
|
||||
} else if (process.env.SANDBOX === 'sandbox-exec') {
|
||||
sandboxEnv = `sandbox-exec (${
|
||||
process.env.SEATBELT_PROFILE || 'unknown'
|
||||
})`;
|
||||
}
|
||||
const modelVersion = config?.getModel() || 'Unknown';
|
||||
const cliVersion = await getCliVersion();
|
||||
const memoryUsage = formatMemoryUsage(process.memoryUsage().rss);
|
||||
|
||||
const info = `
|
||||
* **CLI Version:** ${cliVersion}
|
||||
* **Git Commit:** ${GIT_COMMIT_INFO}
|
||||
* **Operating System:** ${osVersion}
|
||||
* **Sandbox Environment:** ${sandboxEnv}
|
||||
* **Model Version:** ${modelVersion}
|
||||
* **Memory Usage:** ${memoryUsage}
|
||||
`;
|
||||
|
||||
let bugReportUrl =
|
||||
'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title={title}&info={info}';
|
||||
|
||||
const bugCommandSettings = config?.getBugCommand();
|
||||
if (bugCommandSettings?.urlTemplate) {
|
||||
bugReportUrl = bugCommandSettings.urlTemplate;
|
||||
}
|
||||
|
||||
bugReportUrl = bugReportUrl
|
||||
.replace('{title}', encodeURIComponent(bugDescription))
|
||||
.replace('{info}', encodeURIComponent(info));
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `To submit your bug report, please open the following URL in your browser:\n${bugReportUrl}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
try {
|
||||
await open(bugReportUrl);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Could not open URL in browser: ${errorMessage}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -71,7 +71,6 @@ import { Config, GeminiClient } from '@google/gemini-cli-core';
|
||||
import { useSessionStats } from '../contexts/SessionContext.js';
|
||||
import { LoadedSettings } from '../../config/settings.js';
|
||||
import * as ShowMemoryCommandModule from './useShowMemoryCommand.js';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
import { CommandService } from '../../services/CommandService.js';
|
||||
import { SlashCommand } from '../commands/types.js';
|
||||
|
||||
@@ -453,114 +452,6 @@ describe('useSlashCommandProcessor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('/bug command', () => {
|
||||
const originalEnv = process.env;
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
mockGetCliVersionFn.mockResolvedValue('0.1.0');
|
||||
process.env = { ...originalEnv };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
const getExpectedUrl = (
|
||||
description?: string,
|
||||
sandboxEnvVar?: string,
|
||||
seatbeltProfileVar?: string,
|
||||
cliVersion?: string,
|
||||
) => {
|
||||
const osVersion = 'test-platform test-node-version';
|
||||
let sandboxEnvStr = 'no sandbox';
|
||||
if (sandboxEnvVar && sandboxEnvVar !== 'sandbox-exec') {
|
||||
sandboxEnvStr = sandboxEnvVar.replace(/^gemini-(?:code-)?/, '');
|
||||
} else if (sandboxEnvVar === 'sandbox-exec') {
|
||||
sandboxEnvStr = `sandbox-exec (${seatbeltProfileVar || 'unknown'})`;
|
||||
}
|
||||
const modelVersion = 'test-model';
|
||||
// Use the mocked memoryUsage value
|
||||
const memoryUsage = '11.8 MB';
|
||||
|
||||
const info = `
|
||||
* **CLI Version:** ${cliVersion}
|
||||
* **Git Commit:** ${GIT_COMMIT_INFO}
|
||||
* **Operating System:** ${osVersion}
|
||||
* **Sandbox Environment:** ${sandboxEnvStr}
|
||||
* **Model Version:** ${modelVersion}
|
||||
* **Memory Usage:** ${memoryUsage}
|
||||
`;
|
||||
let url =
|
||||
'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml';
|
||||
if (description) {
|
||||
url += `&title=${encodeURIComponent(description)}`;
|
||||
}
|
||||
url += `&info=${encodeURIComponent(info)}`;
|
||||
return url;
|
||||
};
|
||||
|
||||
it('should call open with the correct GitHub issue URL and return true', async () => {
|
||||
mockGetCliVersionFn.mockResolvedValue('test-version');
|
||||
process.env.SANDBOX = 'gemini-sandbox';
|
||||
process.env.SEATBELT_PROFILE = 'test_profile';
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
const bugDescription = 'This is a test bug';
|
||||
const expectedUrl = getExpectedUrl(
|
||||
bugDescription,
|
||||
process.env.SANDBOX,
|
||||
process.env.SEATBELT_PROFILE,
|
||||
'test-version',
|
||||
);
|
||||
let commandResult: SlashCommandProcessorResult | false = false;
|
||||
await act(async () => {
|
||||
commandResult = await handleSlashCommand(`/bug ${bugDescription}`);
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenCalledTimes(2);
|
||||
expect(open).toHaveBeenCalledWith(expectedUrl);
|
||||
expect(commandResult).toEqual({ type: 'handled' });
|
||||
});
|
||||
|
||||
it('should use the custom bug command URL from config if available', async () => {
|
||||
process.env.CLI_VERSION = '0.1.0';
|
||||
process.env.SANDBOX = 'sandbox-exec';
|
||||
process.env.SEATBELT_PROFILE = 'permissive-open';
|
||||
const bugCommand = {
|
||||
urlTemplate:
|
||||
'https://custom-bug-tracker.com/new?title={title}&info={info}',
|
||||
};
|
||||
mockConfig = {
|
||||
...mockConfig,
|
||||
getBugCommand: vi.fn(() => bugCommand),
|
||||
} as unknown as Config;
|
||||
process.env.CLI_VERSION = '0.1.0';
|
||||
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
const bugDescription = 'This is a custom bug';
|
||||
const info = `
|
||||
* **CLI Version:** 0.1.0
|
||||
* **Git Commit:** ${GIT_COMMIT_INFO}
|
||||
* **Operating System:** test-platform test-node-version
|
||||
* **Sandbox Environment:** sandbox-exec (permissive-open)
|
||||
* **Model Version:** test-model
|
||||
* **Memory Usage:** 11.8 MB
|
||||
`;
|
||||
const expectedUrl = bugCommand.urlTemplate
|
||||
.replace('{title}', encodeURIComponent(bugDescription))
|
||||
.replace('{info}', encodeURIComponent(info));
|
||||
|
||||
let commandResult: SlashCommandProcessorResult | false = false;
|
||||
await act(async () => {
|
||||
commandResult = await handleSlashCommand(`/bug ${bugDescription}`);
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenCalledTimes(2);
|
||||
expect(open).toHaveBeenCalledWith(expectedUrl);
|
||||
expect(commandResult).toEqual({ type: 'handled' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('/quit and /exit commands', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
import { useCallback, useMemo, useEffect, useState } from 'react';
|
||||
import { type PartListUnion } from '@google/genai';
|
||||
import open from 'open';
|
||||
import process from 'node:process';
|
||||
import { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||
import { useStateAndRef } from './useStateAndRef.js';
|
||||
@@ -21,9 +20,7 @@ import {
|
||||
} from '../types.js';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
import { formatDuration, formatMemoryUsage } from '../utils/formatters.js';
|
||||
import { getCliVersion } from '../../utils/version.js';
|
||||
import { formatDuration } from '../utils/formatters.js';
|
||||
import { LoadedSettings } from '../../config/settings.js';
|
||||
import {
|
||||
type CommandContext,
|
||||
@@ -205,69 +202,6 @@ export const useSlashCommandProcessor = (
|
||||
toggleCorgiMode();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bug',
|
||||
description: 'submit a bug report',
|
||||
action: async (_mainCommand, _subCommand, args) => {
|
||||
let bugDescription = _subCommand || '';
|
||||
if (args) {
|
||||
bugDescription += ` ${args}`;
|
||||
}
|
||||
bugDescription = bugDescription.trim();
|
||||
|
||||
const osVersion = `${process.platform} ${process.version}`;
|
||||
let sandboxEnv = 'no sandbox';
|
||||
if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
|
||||
sandboxEnv = process.env.SANDBOX.replace(/^gemini-(?:code-)?/, '');
|
||||
} else if (process.env.SANDBOX === 'sandbox-exec') {
|
||||
sandboxEnv = `sandbox-exec (${
|
||||
process.env.SEATBELT_PROFILE || 'unknown'
|
||||
})`;
|
||||
}
|
||||
const modelVersion = config?.getModel() || 'Unknown';
|
||||
const cliVersion = await getCliVersion();
|
||||
const memoryUsage = formatMemoryUsage(process.memoryUsage().rss);
|
||||
|
||||
const info = `
|
||||
* **CLI Version:** ${cliVersion}
|
||||
* **Git Commit:** ${GIT_COMMIT_INFO}
|
||||
* **Operating System:** ${osVersion}
|
||||
* **Sandbox Environment:** ${sandboxEnv}
|
||||
* **Model Version:** ${modelVersion}
|
||||
* **Memory Usage:** ${memoryUsage}
|
||||
`;
|
||||
|
||||
let bugReportUrl =
|
||||
'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title={title}&info={info}';
|
||||
const bugCommand = config?.getBugCommand();
|
||||
if (bugCommand?.urlTemplate) {
|
||||
bugReportUrl = bugCommand.urlTemplate;
|
||||
}
|
||||
bugReportUrl = bugReportUrl
|
||||
.replace('{title}', encodeURIComponent(bugDescription))
|
||||
.replace('{info}', encodeURIComponent(info));
|
||||
|
||||
addMessage({
|
||||
type: MessageType.INFO,
|
||||
content: `To submit your bug report, please open the following URL in your browser:\n${bugReportUrl}`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
(async () => {
|
||||
try {
|
||||
await open(bugReportUrl);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
addMessage({
|
||||
type: MessageType.ERROR,
|
||||
content: `Could not open URL in browser: ${errorMessage}`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
}
|
||||
})();
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'quit',
|
||||
altName: 'exit',
|
||||
|
||||
Reference in New Issue
Block a user