From 42b1e06ad14382bf955851698eb427521fae474f Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 3 Jan 2026 01:59:08 +0100 Subject: [PATCH] feat(coding-agent): configurable double-escape action (tree vs branch) Add doubleEscapeAction setting to choose whether double-escape with an empty editor opens /tree (default) or /branch. - Add setting to Settings interface and SettingsManager - Add to /settings UI for easy toggling - Update interactive-mode to respect the setting - Document in README.md settings table fixes #404 --- .pi/settings.json | 4 ---- packages/coding-agent/CHANGELOG.md | 1 + packages/coding-agent/README.md | 1 + packages/coding-agent/src/core/settings-manager.ts | 10 ++++++++++ .../interactive/components/settings-selector.ts | 12 ++++++++++++ .../src/modes/interactive/interactive-mode.ts | 12 ++++++++++-- 6 files changed, 34 insertions(+), 6 deletions(-) delete mode 100644 .pi/settings.json diff --git a/.pi/settings.json b/.pi/settings.json deleted file mode 100644 index a9626a63..00000000 --- a/.pi/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "customTools": ["packages/coding-agent/examples/custom-tools/todo/index.ts"], - "hooks": ["packages/coding-agent/examples/hooks/todo/index.ts"] -} diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 333fd861..b3260840 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -25,6 +25,7 @@ ### Added +- Configurable double-escape action: choose whether double-escape with empty editor opens `/tree` (default) or `/branch`. Configure via `/settings` or `doubleEscapeAction` in settings.json ([#404](https://github.com/badlogic/pi-mono/issues/404)) - Vertex AI provider (`google-vertex`): access Gemini models via Google Cloud Vertex AI using Application Default Credentials ([#300](https://github.com/badlogic/pi-mono/pull/300) by [@default-anton](https://github.com/default-anton)) - Built-in provider overrides in `models.json`: override just `baseUrl` to route a built-in provider through a proxy while keeping all its models, or define `models` to fully replace the provider ([#406](https://github.com/badlogic/pi-mono/pull/406) by [@yevhen](https://github.com/yevhen)) - Automatic image resizing: images larger than 2000x2000 are resized for better model compatibility. Original dimensions are injected into the prompt. Controlled via `/settings` or `images.autoResize` in settings.json. ([#402](https://github.com/badlogic/pi-mono/pull/402) by [@mitsuhiko](https://github.com/mitsuhiko)) diff --git a/packages/coding-agent/README.md b/packages/coding-agent/README.md index 71c2d1cd..747d5b37 100644 --- a/packages/coding-agent/README.md +++ b/packages/coding-agent/README.md @@ -586,6 +586,7 @@ Global `~/.pi/agent/settings.json` stores persistent preferences: | `retry.baseDelayMs` | Base delay for exponential backoff | `2000` | | `terminal.showImages` | Render images inline (supported terminals) | `true` | | `images.autoResize` | Auto-resize images to 2000x2000 max for better model compatibility | `true` | +| `doubleEscapeAction` | Action for double-escape with empty editor: `tree` or `branch` | `tree` | | `hooks` | Additional hook file paths | `[]` | | `customTools` | Additional custom tool file paths | `[]` | diff --git a/packages/coding-agent/src/core/settings-manager.ts b/packages/coding-agent/src/core/settings-manager.ts index 788e07e9..ab894dcd 100644 --- a/packages/coding-agent/src/core/settings-manager.ts +++ b/packages/coding-agent/src/core/settings-manager.ts @@ -58,6 +58,7 @@ export interface Settings { terminal?: TerminalSettings; images?: ImageSettings; enabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag) + doubleEscapeAction?: "branch" | "tree"; // Action for double-escape with empty editor (default: "tree") } /** Deep merge settings: project/overrides take precedence, nested objects merge recursively */ @@ -410,4 +411,13 @@ export class SettingsManager { getEnabledModels(): string[] | undefined { return this.settings.enabledModels; } + + getDoubleEscapeAction(): "branch" | "tree" { + return this.settings.doubleEscapeAction ?? "tree"; + } + + setDoubleEscapeAction(action: "branch" | "tree"): void { + this.globalSettings.doubleEscapeAction = action; + this.save(); + } } diff --git a/packages/coding-agent/src/modes/interactive/components/settings-selector.ts b/packages/coding-agent/src/modes/interactive/components/settings-selector.ts index e21ef91e..a04f63cc 100644 --- a/packages/coding-agent/src/modes/interactive/components/settings-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/settings-selector.ts @@ -33,6 +33,7 @@ export interface SettingsConfig { availableThemes: string[]; hideThinkingBlock: boolean; collapseChangelog: boolean; + doubleEscapeAction: "branch" | "tree"; } export interface SettingsCallbacks { @@ -46,6 +47,7 @@ export interface SettingsCallbacks { onThemePreview?: (theme: string) => void; onHideThinkingBlockChange: (hidden: boolean) => void; onCollapseChangelogChange: (collapsed: boolean) => void; + onDoubleEscapeActionChange: (action: "branch" | "tree") => void; onCancel: () => void; } @@ -160,6 +162,13 @@ export class SettingsSelectorComponent extends Container { currentValue: config.collapseChangelog ? "true" : "false", values: ["true", "false"], }, + { + id: "double-escape-action", + label: "Double-escape action", + description: "Action when pressing Escape twice with empty editor", + currentValue: config.doubleEscapeAction, + values: ["tree", "branch"], + }, { id: "thinking", label: "Thinking level", @@ -264,6 +273,9 @@ export class SettingsSelectorComponent extends Container { case "collapse-changelog": callbacks.onCollapseChangelogChange(newValue === "true"); break; + case "double-escape-action": + callbacks.onDoubleEscapeActionChange(newValue as "branch" | "tree"); + break; } }, callbacks.onCancel, diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 10224016..f46d6097 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -755,10 +755,14 @@ export class InteractiveMode { this.isBashMode = false; this.updateEditorBorderColor(); } else if (!this.editor.getText().trim()) { - // Double-escape with empty editor triggers /branch + // Double-escape with empty editor triggers /tree or /branch based on setting const now = Date.now(); if (now - this.lastEscapeTime < 500) { - this.showUserMessageSelector(); + if (this.settingsManager.getDoubleEscapeAction() === "tree") { + this.showTreeSelector(); + } else { + this.showUserMessageSelector(); + } this.lastEscapeTime = 0; } else { this.lastEscapeTime = now; @@ -1682,6 +1686,7 @@ export class InteractiveMode { availableThemes: getAvailableThemes(), hideThinkingBlock: this.hideThinkingBlock, collapseChangelog: this.settingsManager.getCollapseChangelog(), + doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(), }, { onAutoCompactChange: (enabled) => { @@ -1739,6 +1744,9 @@ export class InteractiveMode { onCollapseChangelogChange: (collapsed) => { this.settingsManager.setCollapseChangelog(collapsed); }, + onDoubleEscapeActionChange: (action) => { + this.settingsManager.setDoubleEscapeAction(action); + }, onCancel: () => { done(); this.ui.requestRender();