2025-07-16 21:46:35 -04:00
/ * *
* @license
* Copyright 2025 Google LLC
* SPDX - License - Identifier : Apache - 2.0
* /
import open from 'open' ;
import process from 'node:process' ;
2025-07-20 16:57:34 -04:00
import {
type CommandContext ,
type SlashCommand ,
CommandKind ,
} from './types.js' ;
2025-07-16 21:46:35 -04:00
import { MessageType } from '../types.js' ;
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js' ;
import { formatMemoryUsage } from '../utils/formatters.js' ;
2026-01-08 03:43:55 -08:00
import {
IdeClient ,
sessionId ,
getVersion ,
INITIAL_HISTORY_LENGTH ,
debugLogger ,
} from '@google/gemini-cli-core' ;
2025-12-18 10:36:48 -08:00
import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js' ;
2026-01-08 03:43:55 -08:00
import { exportHistoryToFile } from '../utils/historyExportUtils.js' ;
import path from 'node:path' ;
2025-07-16 21:46:35 -04:00
export const bugCommand : SlashCommand = {
name : 'bug' ,
2025-10-17 13:20:15 -07:00
description : 'Submit a bug report' ,
2025-07-20 16:57:34 -04:00
kind : CommandKind.BUILT_IN ,
2025-12-01 12:29:03 -05:00
autoExecute : false ,
2025-07-16 21:46:35 -04:00
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' ;
2025-08-17 12:43:21 -04:00
if ( process . env [ 'SANDBOX' ] && process . env [ 'SANDBOX' ] !== 'sandbox-exec' ) {
sandboxEnv = process . env [ 'SANDBOX' ] . replace ( /^gemini-(?:code-)?/ , '' ) ;
} else if ( process . env [ 'SANDBOX' ] === 'sandbox-exec' ) {
2025-07-16 21:46:35 -04:00
sandboxEnv = ` sandbox-exec ( ${
2025-08-17 12:43:21 -04:00
process . env [ 'SEATBELT_PROFILE' ] || 'unknown'
2025-07-16 21:46:35 -04:00
} ) ` ;
}
const modelVersion = config ? . getModel ( ) || 'Unknown' ;
2025-12-09 16:38:33 -08:00
const cliVersion = await getVersion ( ) ;
2025-07-16 21:46:35 -04:00
const memoryUsage = formatMemoryUsage ( process . memoryUsage ( ) . rss ) ;
2025-09-04 09:32:09 -07:00
const ideClient = await getIdeClientName ( context ) ;
2025-12-18 10:36:48 -08:00
const terminalName =
terminalCapabilityManager . getTerminalName ( ) || 'Unknown' ;
const terminalBgColor =
terminalCapabilityManager . getTerminalBackgroundColor ( ) || 'Unknown' ;
const kittyProtocol = terminalCapabilityManager . isKittyProtocolEnabled ( )
? 'Supported'
: 'Unsupported' ;
2025-07-16 21:46:35 -04:00
2025-08-19 11:22:21 -07:00
let info = `
2025-07-16 21:46:35 -04:00
* * * CLI Version : * * $ { cliVersion }
* * * Git Commit : * * $ { GIT_COMMIT_INFO }
2025-08-15 10:15:54 -05:00
* * * Session ID : * * $ { sessionId }
2025-07-16 21:46:35 -04:00
* * * Operating System : * * $ { osVersion }
* * * Sandbox Environment : * * $ { sandboxEnv }
* * * Model Version : * * $ { modelVersion }
* * * Memory Usage : * * $ { memoryUsage }
2025-12-18 10:36:48 -08:00
* * * Terminal Name : * * $ { terminalName }
* * * Terminal Background : * * $ { terminalBgColor }
* * * Kitty Keyboard Protocol : * * $ { kittyProtocol }
2025-07-16 21:46:35 -04:00
` ;
2025-08-19 11:22:21 -07:00
if ( ideClient ) {
info += ` * **IDE Client:** ${ ideClient } \ n ` ;
}
2025-07-16 21:46:35 -04:00
2026-01-08 03:43:55 -08:00
const chat = config ? . getGeminiClient ( ) ? . getChat ( ) ;
const history = chat ? . getHistory ( ) || [ ] ;
let historyFileMessage = '' ;
let problemValue = bugDescription ;
if ( history . length > INITIAL_HISTORY_LENGTH ) {
const tempDir = config ? . storage ? . getProjectTempDir ( ) ;
if ( tempDir ) {
const historyFileName = ` bug-report-history- ${ Date . now ( ) } .json ` ;
const historyFilePath = path . join ( tempDir , historyFileName ) ;
try {
await exportHistoryToFile ( { history , filePath : historyFilePath } ) ;
historyFileMessage = ` \ n \ n-------------------------------------------------------------------------------- \ n \ n📄 **Chat History Exported** \ nTo help us debug, we've exported your current chat history to: \ n ${ historyFilePath } \ n \ nPlease consider attaching this file to your GitHub issue if you feel comfortable doing so. \ n \ n**Privacy Disclaimer:** Please do not upload any logs containing sensitive or private information that you are not comfortable sharing publicly. ` ;
problemValue += ` \ n \ n[ACTION REQUIRED] 📎 PLEASE ATTACH THE EXPORTED CHAT HISTORY JSON FILE TO THIS ISSUE IF YOU FEEL COMFORTABLE SHARING IT. ` ;
} catch ( err ) {
const errorMessage = err instanceof Error ? err.message : String ( err ) ;
debugLogger . error (
` Failed to export chat history for bug report: ${ errorMessage } ` ,
) ;
}
}
}
2025-07-16 21:46:35 -04:00
let bugReportUrl =
2026-01-08 03:43:55 -08:00
'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title={title}&info={info}&problem={problem}' ;
2025-07-16 21:46:35 -04:00
const bugCommandSettings = config ? . getBugCommand ( ) ;
if ( bugCommandSettings ? . urlTemplate ) {
bugReportUrl = bugCommandSettings . urlTemplate ;
}
bugReportUrl = bugReportUrl
. replace ( '{title}' , encodeURIComponent ( bugDescription ) )
2026-01-08 03:43:55 -08:00
. replace ( '{info}' , encodeURIComponent ( info ) )
. replace ( '{problem}' , encodeURIComponent ( problemValue ) ) ;
2025-07-16 21:46:35 -04:00
context . ui . addItem (
{
type : MessageType . INFO ,
2026-01-08 03:43:55 -08:00
text : ` To submit your bug report, please open the following URL in your browser: \ n ${ bugReportUrl } ${ historyFileMessage } ` ,
2025-07-16 21:46:35 -04:00
} ,
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 ( ) ,
) ;
}
} ,
} ;
2025-09-04 09:32:09 -07:00
async function getIdeClientName ( context : CommandContext ) {
if ( ! context . services . config ? . getIdeMode ( ) ) {
return '' ;
}
const ideClient = await IdeClient . getInstance ( ) ;
return ideClient . getDetectedIdeDisplayName ( ) ? ? '' ;
}