Skip to main content

vtcode_core/terminal_setup/features/
shell_integration.rs

1//! Shell integration feature configuration generator.
2//!
3//! Generates terminal-specific configuration for shell integration:
4//! - Working directory tracking (OSC 7 sequences)
5//! - Command status tracking (exit codes)
6//! - Prompt integration
7//! - Command duration tracking
8
9use crate::terminal_setup::detector::TerminalType;
10use anyhow::Result;
11
12/// Generate shell integration configuration for the specified terminal
13pub fn generate_config(terminal: TerminalType) -> Result<String> {
14    let config = match terminal {
15        TerminalType::Ghostty => r#"# Shell integration
16shell-integration = detect
17shell-integration-features = cursor,sudo,title
18
19# Working directory tracking
20working-directory = inherit
21"#
22        .to_string(),
23
24        TerminalType::Kitty => r#"# Shell integration
25shell_integration enabled
26
27# Features
28shell_integration_features title cwd
29
30# Automatically inject shell integration
31shell_integration_startup_mode enabled
32"#
33        .to_string(),
34
35        TerminalType::Alacritty => r#"# Shell integration via OSC sequences
36# Add this to your shell RC file (~/.bashrc, ~/.zshrc, etc.):
37#
38# For Bash:
39# if [ -n "$ALACRITTY_SOCKET" ]; then
40#     PROMPT_COMMAND='printf "\033]7;file://%s%s\033\\" "$HOSTNAME" "$PWD"'
41# fi
42#
43# For Zsh:
44# if [ -n "$ALACRITTY_SOCKET" ]; then
45#     precmd() { printf "\033]7;file://%s%s\033\\" "$HOST" "$PWD"; }
46# fi
47#
48# For Fish:
49# if set -q ALACRITTY_SOCKET
50#     function __alacritty_osc7_helper --on-variable PWD
51#         printf "\033]7;file://%s%s\033\\" $hostname $PWD
52#     end
53# end
54
55# Note: Alacritty doesn't have built-in shell integration
56# The configuration above enables working directory tracking
57"#
58        .to_string(),
59
60        TerminalType::WezTerm => r#"-- WezTerm shell integration is available via shell integration scripts.
61-- See: https://wezfurlong.org/wezterm/shell-integration.html
62"#
63        .to_string(),
64
65        TerminalType::TerminalApp => r#"Terminal.app supports shell integration through shell startup files.
66Use OSC 7 sequences in your prompt hooks for directory tracking.
67"#
68        .to_string(),
69
70        TerminalType::Xterm => r#"xterm shell integration relies on OSC escape sequences in shell prompt hooks.
71"#
72        .to_string(),
73
74        TerminalType::Zed => r#"// Zed terminal has built-in shell integration
75// Working directory and command tracking enabled by default
76{
77  "terminal": {
78    "shell": {
79      "with_arguments": {
80        "program": "zsh",
81        "args": ["-l"]
82      }
83    },
84    "working_directory": "current_project_directory"
85  }
86}
87"#
88        .to_string(),
89
90        TerminalType::Warp => r#"# Warp has advanced built-in shell integration
91# Features automatically enabled:
92# - Working directory tracking
93# - Command history
94# - Command status (success/failure)
95# - Git status integration
96# - AI command suggestions
97
98# No additional configuration needed
99# Shell integration is automatic
100"#
101        .to_string(),
102
103        TerminalType::WindowsTerminal => r#"{
104  "profiles": {
105    "defaults": {
106      "startingDirectory": "%USERPROFILE%"
107    }
108  },
109  "experimental.rendering.forceFullRepaint": true
110}
111
112// Note: Windows Terminal supports shell integration via:
113// 1. PowerShell: Built-in PSReadLine module
114// 2. WSL: Add OSC sequences to .bashrc/.zshrc
115// 3. Git Bash: Configure prompt in .bash_profile
116"#
117        .to_string(),
118
119        TerminalType::Hyper => r#"// Shell integration for Hyper
120// Install hyper-statusline plugin for enhanced integration
121
122config: {
123  // Working directory in tab title
124  showWorkingDirectory: true,
125
126  // Command execution feedback
127  showCommandFeedback: true,
128}
129
130// Install recommended plugins:
131// hyper install hyper-statusline
132// hyper install hyper-search
133"#
134        .to_string(),
135
136        TerminalType::Tabby => r#"terminal:
137  shellIntegration: true
138  workingDirectory: auto
139
140  # Shell-specific integration
141  shell:
142    command: auto  # Auto-detect shell
143    args: ['-l']   # Login shell
144
145  # Command tracking
146  trackCommands: true
147  showCommandStatus: true
148"#
149        .to_string(),
150
151        TerminalType::ITerm2 => r#"Manual iTerm2 Shell Integration Setup:
152
153METHOD 1: Automatic Installation (Recommended)
1541. Open iTerm2
1552. Go to iTerm2 → Install Shell Integration
1563. Select your shell (bash/zsh/fish)
1574. Restart your terminal
158
159METHOD 2: Manual Installation
1601. Download: curl -L https://iterm2.com/shell_integration/install_shell_integration.sh | bash
1612. Restart your shell
1623. Verify installation: echo $ITERM_SESSION_ID
163
164Features enabled:
165- Working directory tracking
166- Command history sync
167- Command status badges
168- Shell prompt marks
169- Automatic profile switching
170
171Configuration in Preferences:
1721. Profiles → General → Working Directory
1732. Set to "Reuse previous session's directory"
1743. Profiles → Terminal → Enable "Shell Integration"
175"#
176        .to_string(),
177
178        TerminalType::VSCode => r#"VS Code Shell Integration Configuration:
179
180Shell integration is built-in and enabled by default.
181
182To configure in settings.json:
183{
184  "terminal.integrated.shellIntegration.enabled": true,
185  "terminal.integrated.shellIntegration.decorationsEnabled": "both",
186  "terminal.integrated.shellIntegration.history": 100,
187  "terminal.integrated.enablePersistentSessions": true,
188  "terminal.integrated.cwd": "${workspaceFolder}"
189}
190
191Features:
192- Automatic working directory detection
193- Command navigation (Ctrl+Up/Down)
194- Command status decorations
195- Re-run command in new terminal
196- Sticky scroll for command output
197
198Keyboard shortcuts:
199- Ctrl+Up/Down: Navigate between commands
200- Ctrl+Shift+G: Go to recent directory
201"#
202        .to_string(),
203
204        TerminalType::Unknown => {
205            anyhow::bail!("Cannot generate shell integration config for unknown terminal type");
206        }
207    };
208
209    Ok(config)
210}
211
212/// Generate shell RC file snippet for manual integration
213pub fn generate_shell_rc_snippet(shell: &str) -> Result<String> {
214    let snippet = match shell {
215        "bash" => {
216            r#"# VT Code Shell Integration for Bash
217if [ -n "$VTCODE_SESSION" ]; then
218    # Working directory tracking via OSC 7
219    __vtcode_osc7() {
220        printf '\033]7;file://%s%s\033\\' "$HOSTNAME" "$PWD"
221    }
222
223    # Add to PROMPT_COMMAND
224    if [[ "$PROMPT_COMMAND" != *__vtcode_osc7* ]]; then
225        PROMPT_COMMAND="__vtcode_osc7${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
226    fi
227
228    # Command status tracking
229    __vtcode_command_status() {
230        local status=$?
231        printf '\033]133;D;%s\033\\' "$status"
232        return $status
233    }
234
235    trap '__vtcode_command_status' DEBUG
236fi
237"#
238        }
239
240        "zsh" => {
241            r#"# VT Code Shell Integration for Zsh
242if [ -n "$VTCODE_SESSION" ]; then
243    # Working directory tracking via OSC 7
244    __vtcode_osc7() {
245        printf '\033]7;file://%s%s\033\\' "$HOST" "$PWD"
246    }
247
248    # Add to precmd hook
249    if ! (( ${precmd_functions[(I)__vtcode_osc7]} )); then
250        precmd_functions+=(__vtcode_osc7)
251    fi
252
253    # Command status tracking
254    __vtcode_command_status() {
255        printf '\033]133;D;%s\033\\' "$?"
256    }
257
258    if ! (( ${preexec_functions[(I)__vtcode_command_status]} )); then
259        preexec_functions+=(__vtcode_command_status)
260    fi
261fi
262"#
263        }
264
265        "fish" => {
266            r#"# VT Code Shell Integration for Fish
267if set -q VTCODE_SESSION
268    # Working directory tracking via OSC 7
269    function __vtcode_osc7_helper --on-variable PWD
270        printf '\033]7;file://%s%s\033\\' $hostname $PWD
271    end
272
273    # Command status tracking
274    function __vtcode_command_status --on-event fish_postexec
275        printf '\033]133;D;%s\033\\' $status
276    end
277
278    __vtcode_osc7_helper
279end
280"#
281        }
282
283        _ => {
284            anyhow::bail!("Unsupported shell type: {shell}");
285        }
286    };
287
288    Ok(snippet.to_string())
289}
290
291#[cfg(test)]
292mod tests {
293    use super::*;
294
295    #[test]
296    fn test_generate_ghostty_config() {
297        let config = generate_config(TerminalType::Ghostty).unwrap();
298        assert!(config.contains("shell-integration"));
299        assert!(config.contains("working-directory"));
300    }
301
302    #[test]
303    fn test_generate_kitty_config() {
304        let config = generate_config(TerminalType::Kitty).unwrap();
305        assert!(config.contains("shell_integration"));
306        assert!(config.contains("cwd"));
307    }
308
309    #[test]
310    fn test_generate_alacritty_config() {
311        let config = generate_config(TerminalType::Alacritty).unwrap();
312        assert!(config.contains("OSC"));
313        assert!(config.contains("PROMPT_COMMAND") || config.contains("precmd"));
314    }
315
316    #[test]
317    fn test_generate_vscode_config() {
318        let config = generate_config(TerminalType::VSCode).unwrap();
319        assert!(config.contains("shellIntegration"));
320        assert!(config.contains("settings.json"));
321    }
322
323    #[test]
324    fn test_generate_iterm2_instructions() {
325        let config = generate_config(TerminalType::ITerm2).unwrap();
326        assert!(config.contains("iTerm2"));
327        assert!(config.contains("Install Shell Integration"));
328    }
329
330    #[test]
331    fn test_generate_bash_snippet() {
332        let snippet = generate_shell_rc_snippet("bash").unwrap();
333        assert!(snippet.contains("PROMPT_COMMAND"));
334        assert!(snippet.contains("__vtcode_osc7"));
335    }
336
337    #[test]
338    fn test_generate_zsh_snippet() {
339        let snippet = generate_shell_rc_snippet("zsh").unwrap();
340        assert!(snippet.contains("precmd"));
341        assert!(snippet.contains("__vtcode_osc7"));
342    }
343
344    #[test]
345    fn test_generate_fish_snippet() {
346        let snippet = generate_shell_rc_snippet("fish").unwrap();
347        assert!(snippet.contains("on-variable PWD"));
348        assert!(snippet.contains("__vtcode_osc7_helper"));
349    }
350
351    #[test]
352    fn test_unknown_terminal_error() {
353        let result = generate_config(TerminalType::Unknown);
354        result.unwrap_err();
355    }
356
357    #[test]
358    fn test_unsupported_shell_error() {
359        let result = generate_shell_rc_snippet("tcsh");
360        result.unwrap_err();
361    }
362
363    #[test]
364    fn test_generate_config() {
365        // This test exists for backward compatibility with the stub
366        generate_config(TerminalType::Kitty).unwrap();
367    }
368}