Compare commits

...

3 Commits

9 changed files with 120 additions and 23 deletions
+2
View File
@@ -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
+96
View File
@@ -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 45).
### 8. Cleanup
- Kill remaining background processes (tmux sessions, etc.).
- Remove the tmp test directory if no longer needed.
+1 -2
View File
@@ -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,
+12 -8
View File
@@ -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();
+1 -1
View File
@@ -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',