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