mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-23 16:20:57 -07:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 487f2e46e0 | |||
| 78c25f30c5 | |||
| 4599c8e984 |
@@ -28,6 +28,7 @@ This skill guides the agent in conducting professional and thorough code reviews
|
||||
npm run preflight
|
||||
```
|
||||
3. **Context**: Read the PR description and any existing comments to understand the goal and history.
|
||||
4. **Local Verification**: If reviewing a fix or feature for the Gemini CLI itself, activate the `debug-cli` skill to build and interactively test the changes locally using `tmux`.
|
||||
|
||||
#### For Local Changes:
|
||||
1. **Identify Changes**:
|
||||
@@ -45,6 +46,7 @@ Analyze the code changes based on the following pillars:
|
||||
* **Security**: Are there any potential security vulnerabilities or insecure coding practices?
|
||||
* **Edge Cases and Error Handling**: Does the code appropriately handle edge cases and potential errors?
|
||||
* **Testability**: Is the new or modified code adequately covered by tests (even if preflight checks pass)? Suggest additional test cases that would improve coverage or robustness.
|
||||
* **Interactive Verification**: If the changes affect the Gemini CLI behavior or TUI, use the `debug-cli` skill to manually verify the UI/UX or behavioral changes.
|
||||
|
||||
### 4. Provide Feedback
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
name: debug-cli
|
||||
description: Instructions for running and debugging the Gemini CLI locally from source. Use this when asked to diagnose issues or run the gemini cli application.
|
||||
---
|
||||
|
||||
# Debug CLI
|
||||
|
||||
This skill provides instructions for the agent to run the Gemini CLI locally built from source to diagnose issues. **Always reproduce the issue interactively first** — before analyzing source code, running unit tests, or attempting fixes.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Understand the Issue
|
||||
|
||||
- **Gather Context:** Fetch and read the GitHub issue description (if applicable) or the user's report. Identify the delta between expected and observed behavior.
|
||||
- **Consult Documentation:** Use the `cli_help` subagent to clarify intended CLI behavior, command usage, or configuration schemas. For example: `@cli_help "how does proxy configuration work?"`.
|
||||
- **Deep Dive:** If the documentation isn't enough, use the `codebase_investigator` subagent to analyze the underlying logic, identify relevant files, and map dependencies related to the issue.
|
||||
- **Live Exploration:** Depending on the exact problem you're debugging, you might have to play with the `/settings` command or other application commands in a live session.
|
||||
- **Smart Agent Advantage:** Since the Gemini CLI is a smart agent, you can always ask the CLI itself for help figuring out how it works or how to use a specific feature!
|
||||
|
||||
### 2. Build from Source
|
||||
|
||||
Always start by cleaning, building, and bundling the source code to ensure you are running the latest version:
|
||||
|
||||
```bash
|
||||
npm run clean && npm run build && npm run bundle
|
||||
```
|
||||
|
||||
The application can then be started via `./bundle/gemini.js`.
|
||||
|
||||
### 3. Setup Test Directory
|
||||
|
||||
Before running the CLI, create a temporary test directory and configure it with the appropriate files. This ensures the CLI runs with the correct settings for the feature being tested.
|
||||
|
||||
```bash
|
||||
mkdir -p /path/to/test-dir/.gemini
|
||||
```
|
||||
|
||||
Create a `.gemini/settings.json` in that directory with any settings needed for the test. For example, to allowlist domains for the browser agent:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"browser": {
|
||||
"allowedDomains": ["*.example.com", "example.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You may also need `.gemini/keybindings.json`, `GEMINI.md`, and any test project files depending on what you're testing.
|
||||
|
||||
### 4. Launch with tmux
|
||||
|
||||
Always run the Gemini CLI application using `tmux`. This is essential because the application is a complex TUI (Terminal User Interface). `tmux` commands make it easy to read the screen state, send programmatic inputs, and interact with the TUI in a robust way.
|
||||
|
||||
Launch the CLI from within the test directory so it picks up the local settings:
|
||||
|
||||
```bash
|
||||
cd /path/to/test-dir
|
||||
/absolute/path/to/bundle/gemini.js [flags]
|
||||
```
|
||||
|
||||
### 5. Interact and Reproduce
|
||||
|
||||
Use `tmux send-keys` to send text and keypresses, and `tmux capture-pane` to read the screen.
|
||||
|
||||
#### Submitting Prompts (Paste-Injection Protection)
|
||||
|
||||
The CLI has paste-injection protection. You **must** split text entry and the Enter keypress into separate steps:
|
||||
|
||||
1. **Send the prompt text** (without pressing Enter).
|
||||
2. **Wait briefly** (~1 second) to let the CLI process the pasted text.
|
||||
3. **Send only the Enter key** to submit.
|
||||
|
||||
Sending text and Enter together in one command will be rejected by the paste-injection guard.
|
||||
|
||||
#### Authentication
|
||||
|
||||
If the app presents an authentication screen:
|
||||
- Select the option to authenticate with a **Gemini API key**.
|
||||
- The key should be prepopulated automatically. Otherwise if a `.env` file is available copy it inside the test directory.
|
||||
|
||||
### 6. Document Results
|
||||
|
||||
- Record observed vs. expected behavior.
|
||||
- Note any error messages or visual glitches.
|
||||
|
||||
### 7. Fix and Verify (if applicable)
|
||||
|
||||
- Only after confirming the repro, investigate source code and implement a fix.
|
||||
- Rebuild (`npm run build && npm run bundle`), re-launch, and verify through the interactive flow (Steps 4–5).
|
||||
|
||||
### 8. Cleanup
|
||||
|
||||
- Kill remaining background processes (tmux sessions, etc.).
|
||||
- Remove the tmp test directory if no longer needed.
|
||||
@@ -806,8 +806,7 @@ function toolUsageInteractive(
|
||||
function toolUsageAiShell(options: OperationalGuidelinesOptions): string {
|
||||
if (options.interactiveShellMode !== 'ai') return '';
|
||||
return `
|
||||
- **AI-Driven Interactive Shell:** Commands using \`wait_for_output_seconds\` auto-promote to background when they stall. Once promoted, use ${formatToolName(READ_SHELL_TOOL_NAME)} to see the terminal screen, then ${formatToolName(WRITE_TO_SHELL_TOOL_NAME)} to send text input and/or special keys (arrows, Enter, Ctrl-C, etc.).
|
||||
- Set \`wait_for_output_seconds\` **low (2-5)** for commands that prompt for input (npx, installers, REPLs). Set **high (60+)** for long builds. Omit for instant commands.
|
||||
- **AI-Driven Interactive Shell:** Commands auto-promote to background if they stall for 3 seconds. Once promoted, use ${formatToolName(READ_SHELL_TOOL_NAME)} to see the terminal screen, then ${formatToolName(WRITE_TO_SHELL_TOOL_NAME)} to send text input and/or special keys (arrows, Enter, Ctrl-C, etc.).
|
||||
- **Always read the screen before writing input.** The screen state tells you what the process is waiting for.
|
||||
- When waiting for a command to finish (e.g. npm install), use ${formatToolName(READ_SHELL_TOOL_NAME)} with \`wait_seconds\` to delay before reading. Do NOT poll in a tight loop.
|
||||
- **Clean up when done:** when your task is complete, kill background processes with ${formatToolName(WRITE_TO_SHELL_TOOL_NAME)} sending Ctrl-C, or note the PID for the user to clean up.
|
||||
|
||||
@@ -1341,6 +1341,11 @@ export class ShellExecutionService {
|
||||
const activePty = this.activePtys.get(pid);
|
||||
const activeChild = this.activeChildProcesses.get(pid);
|
||||
|
||||
// If the process has already exited, there is no need to background it.
|
||||
if (!activePty && !activeChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedSessionId =
|
||||
sessionId ?? activePty?.sessionId ?? activeChild?.sessionId;
|
||||
const resolvedCommand =
|
||||
|
||||
@@ -56,7 +56,6 @@ export const READ_FILE_PARAM_END_LINE = 'end_line';
|
||||
export const SHELL_TOOL_NAME = 'run_shell_command';
|
||||
export const SHELL_PARAM_COMMAND = 'command';
|
||||
export const SHELL_PARAM_IS_BACKGROUND = 'is_background';
|
||||
export const SHELL_PARAM_WAIT_SECONDS = 'wait_for_output_seconds';
|
||||
|
||||
// -- write_to_shell --
|
||||
export const WRITE_TO_SHELL_TOOL_NAME = 'write_to_shell';
|
||||
|
||||
@@ -75,7 +75,6 @@ export {
|
||||
LS_PARAM_IGNORE,
|
||||
SHELL_PARAM_COMMAND,
|
||||
SHELL_PARAM_IS_BACKGROUND,
|
||||
SHELL_PARAM_WAIT_SECONDS,
|
||||
WRITE_TO_SHELL_PARAM_PID,
|
||||
WRITE_TO_SHELL_PARAM_INPUT,
|
||||
WRITE_TO_SHELL_PARAM_SPECIAL_KEYS,
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
PARAM_DIR_PATH,
|
||||
SHELL_PARAM_IS_BACKGROUND,
|
||||
EXIT_PLAN_PARAM_PLAN_FILENAME,
|
||||
SHELL_PARAM_WAIT_SECONDS,
|
||||
SKILL_PARAM_NAME,
|
||||
PARAM_ADDITIONAL_PERMISSIONS,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
@@ -60,7 +59,7 @@ export function getShellToolDescription(
|
||||
Process Group PGID: Only included if available.`;
|
||||
|
||||
if (isAiMode) {
|
||||
const autoPromoteInstructions = `Commands that do not complete within \`${SHELL_PARAM_WAIT_SECONDS}\` seconds are automatically promoted to background. Once promoted, use \`write_to_shell\` and \`read_shell\` to interact with the process. Do NOT use \`&\` to background commands.`;
|
||||
const autoPromoteInstructions = `Commands that do not complete within 3 seconds are automatically promoted to background. Once promoted, use \`write_to_shell\` and \`read_shell\` to interact with the process. Do NOT use \`&\` to background commands.`;
|
||||
return `This tool executes a given shell command as \`bash -c <command>\`. ${autoPromoteInstructions} Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.${efficiencyGuidelines}${returnedInfo}`;
|
||||
}
|
||||
|
||||
@@ -98,15 +97,9 @@ export function getShellDeclaration(
|
||||
): FunctionDeclaration {
|
||||
const isAiMode = interactiveShellMode === 'ai';
|
||||
|
||||
// In AI mode, use wait_for_output_seconds instead of is_background
|
||||
// In AI mode, auto-promotion is enabled by default, no background param needed
|
||||
const backgroundParam = isAiMode
|
||||
? {
|
||||
[SHELL_PARAM_WAIT_SECONDS]: {
|
||||
type: 'number' as const,
|
||||
description:
|
||||
'Max seconds to wait for command to complete before auto-promoting to background (default: 5). Set low (2-5) for commands likely to prompt for input (npx, installers, REPLs). Set high (60-300) for long builds or installs. Once promoted, use write_to_shell/read_shell to interact.',
|
||||
},
|
||||
}
|
||||
? {}
|
||||
: {
|
||||
[SHELL_PARAM_IS_BACKGROUND]: {
|
||||
type: 'boolean' as const,
|
||||
|
||||
@@ -72,7 +72,6 @@ export interface ShellToolParams {
|
||||
is_background?: boolean;
|
||||
delay_ms?: number;
|
||||
[PARAM_ADDITIONAL_PERMISSIONS]?: SandboxPermissions;
|
||||
wait_for_output_seconds?: number;
|
||||
}
|
||||
|
||||
export class ShellToolInvocation extends BaseToolInvocation<
|
||||
@@ -228,8 +227,8 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
override getExplanation(): string {
|
||||
let explanation = this.getContextualDetails().trim();
|
||||
const isAiMode = this.context.config.getInteractiveShellMode() === 'ai';
|
||||
if (this.params.wait_for_output_seconds !== undefined || isAiMode) {
|
||||
explanation += ` [auto-background after ${this.params.wait_for_output_seconds ?? 5}s]`;
|
||||
if (isAiMode) {
|
||||
explanation += ` [auto-background after 3s]`;
|
||||
}
|
||||
return explanation;
|
||||
}
|
||||
@@ -507,15 +506,20 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
|
||||
let currentPid: number | undefined;
|
||||
const isAiMode = this.context.config.getInteractiveShellMode() === 'ai';
|
||||
const shouldAutoPromote =
|
||||
this.params.wait_for_output_seconds !== undefined || isAiMode;
|
||||
const waitMs = (this.params.wait_for_output_seconds ?? 5) * 1000;
|
||||
const shouldAutoPromote = isAiMode;
|
||||
const waitMs = isAiMode ? 3000 : 0;
|
||||
|
||||
const resetAutoPromoteTimer = () => {
|
||||
if (shouldAutoPromote && currentPid) {
|
||||
if (this._autoPromoteTimer) clearTimeout(this._autoPromoteTimer);
|
||||
this._autoPromoteTimer = setTimeout(() => {
|
||||
ShellExecutionService.background(currentPid!);
|
||||
const sessionId =
|
||||
this.context.config?.getSessionId?.() ?? 'default';
|
||||
ShellExecutionService.background(
|
||||
currentPid!,
|
||||
sessionId,
|
||||
strippedCommand,
|
||||
);
|
||||
}, waitMs);
|
||||
}
|
||||
};
|
||||
@@ -644,7 +648,7 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
}
|
||||
}
|
||||
|
||||
// In AI mode with wait_for_output_seconds, set up auto-promotion timer.
|
||||
// In AI mode, set up auto-promotion timer.
|
||||
// When the timer fires, promote to background instead of cancelling.
|
||||
currentPid = pid;
|
||||
resetAutoPromoteTimer();
|
||||
|
||||
@@ -167,7 +167,7 @@ export class WriteToShellTool extends BaseDeclarativeTool<
|
||||
super(
|
||||
WriteToShellTool.Name,
|
||||
'WriteToShell',
|
||||
'Sends input to a running background shell process. Use this to interact with TUI applications, REPLs, and interactive commands. After writing, the current screen state is returned. Works with processes that were auto-promoted to background via wait_for_output_seconds or started with is_background=true.',
|
||||
'Sends input to a running background shell process. Use this to interact with TUI applications, REPLs, and interactive commands. After writing, the current screen state is returned. Works with processes that were auto-promoted to background or started with is_background=true.',
|
||||
Kind.Execute,
|
||||
{
|
||||
type: 'object',
|
||||
|
||||
Reference in New Issue
Block a user