#!/usr/bin/env zsh # Terminal context capture for forge plugin # # Provides three layers of terminal context: # 1. preexec/precmd hooks: ring buffer of recent commands + exit codes # 2. OSC 133 emission: semantic terminal markers for compatible terminals # 3. Terminal-specific output capture: Kitty > WezTerm > tmux # # Context is organized by command blocks: each command's metadata and its # full output are grouped together, using the known command strings from # the ring buffer to detect boundaries in the terminal scrollback. # --------------------------------------------------------------------------- # OSC 133 helpers # --------------------------------------------------------------------------- # Determines whether OSC 133 semantic markers should be emitted. # Auto-detection is conservative: only emit for terminals known to support it # to avoid garbled output in unsupported terminals. # The detection result is cached per session in _FORGE_TERM_OSC133_CACHED # ("1" = emit, "0" = don't emit) to avoid repeated detection overhead. typeset -g _FORGE_TERM_OSC133_CACHED="" function _forge_osc133_should_emit() { if [[ -n "$_FORGE_TERM_OSC133_CACHED" ]]; then [[ "$_FORGE_TERM_OSC133_CACHED" == "1" ]] && return 0 || return 1 fi case "$_FORGE_TERM_OSC133" in on) _FORGE_TERM_OSC133_CACHED="1"; return 0 ;; off) _FORGE_TERM_OSC133_CACHED="0"; return 1 ;; auto) # Kitty sets KITTY_PID if [[ -n "${KITTY_PID:-}" ]]; then _FORGE_TERM_OSC133_CACHED="1"; return 0; fi # Detect by TERM_PROGRAM case "${TERM_PROGRAM:-}" in WezTerm|iTerm.app|vscode|WarpTerminal) _FORGE_TERM_OSC133_CACHED="1"; return 0 ;; esac # Foot terminal if [[ "${TERM:-}" == "foot"* ]]; then _FORGE_TERM_OSC133_CACHED="1"; return 0; fi # Ghostty if [[ "${TERM_PROGRAM:-}" == "ghostty" ]]; then _FORGE_TERM_OSC133_CACHED="1"; return 0; fi # Unknown terminal: don't emit _FORGE_TERM_OSC133_CACHED="0" return 1 ;; *) _FORGE_TERM_OSC133_CACHED="0"; return 1 ;; esac } # Emits an OSC 133 marker if the terminal supports it. # Usage: _forge_osc133_emit "A" or _forge_osc133_emit "D;0" function _forge_osc133_emit() { _forge_osc133_should_emit || return 0 printf '\e]133;%s\a' "$1" } # --------------------------------------------------------------------------- # preexec / precmd hooks # --------------------------------------------------------------------------- # Ring buffer storage uses parallel arrays declared in config.zsh: # _FORGE_TERM_COMMANDS, _FORGE_TERM_EXIT_CODES, _FORGE_TERM_TIMESTAMPS # Pending command state: typeset -g _FORGE_TERM_PENDING_CMD="" typeset -g _FORGE_TERM_PENDING_TS="" # Called before each command executes. # Records the command text and timestamp, emits OSC 133 B+C markers. function _forge_context_preexec() { [[ "$_FORGE_TERM" != "true" ]] && return _FORGE_TERM_PENDING_CMD="$1" _FORGE_TERM_PENDING_TS="$(date +%s)" # OSC 133 B: prompt end / command start _forge_osc133_emit "B" # OSC 133 C: command output start _forge_osc133_emit "C" } # Called after each command completes, before the next prompt is drawn. # Captures exit code, pushes to ring buffer, emits OSC 133 D+A markers. function _forge_context_precmd() { local last_exit=$? # MUST be first line to capture exit code # OSC 133 D: command finished with exit code. # Emitted unconditionally (before the enabled check) so that terminals # relying on paired A/B/C/D markers never receive an unpaired sequence, # even when context capture is disabled. _forge_osc133_emit "D;$last_exit" [[ "$_FORGE_TERM" != "true" ]] && return # Only record if we have a pending command from preexec if [[ -n "$_FORGE_TERM_PENDING_CMD" ]]; then _FORGE_TERM_COMMANDS+=("$_FORGE_TERM_PENDING_CMD") _FORGE_TERM_EXIT_CODES+=("$last_exit") _FORGE_TERM_TIMESTAMPS+=("$_FORGE_TERM_PENDING_TS") # Trim ring buffer to max size while (( ${#_FORGE_TERM_COMMANDS} > _FORGE_TERM_MAX_COMMANDS )); do shift _FORGE_TERM_COMMANDS shift _FORGE_TERM_EXIT_CODES shift _FORGE_TERM_TIMESTAMPS done _FORGE_TERM_PENDING_CMD="" _FORGE_TERM_PENDING_TS="" fi # OSC 133 A: prompt start (for the next prompt) _forge_osc133_emit "A" } # Hook registration # Register using standard zsh hook arrays for coexistence with other plugins. # precmd is prepended so it runs first and captures the real $? from the # command, before other plugins (powerlevel10k, starship, etc.) overwrite it. if [[ "$_FORGE_TERM" == "true" ]]; then preexec_functions+=(_forge_context_preexec) precmd_functions=(_forge_context_precmd "${precmd_functions[@]}") fi