Skip to content

Terminal autocomplete for shell prompts and Claude Code TUI#790

Draft
akramj13 wants to merge 1 commit into
FuJacob:mainfrom
akramj13:codex/implement-issue-762
Draft

Terminal autocomplete for shell prompts and Claude Code TUI#790
akramj13 wants to merge 1 commit into
FuJacob:mainfrom
akramj13:codex/implement-issue-762

Conversation

@akramj13

@akramj13 akramj13 commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Closes #762

Summary

This PR brings Cotabby's inline autocomplete to terminal environments
while preserving the reliability improvements introduced in #656.

Instead of relying on Accessibility APIs inside terminals, this
implementation uses cooperative shell integration over a local
Unix-domain socket. When shell integration is unavailable, Claude Code
can fall back to OCR-based prompt detection. Suggestions are only
enabled when a verified live integration source exists, so unsupported
terminals continue behaving exactly as they do today.

The feature is opt-in and disabled by default.

What's included

Shell integration

  • Added shell integration for:
    • zsh
    • bash
    • fish
  • Live prompt and cursor updates are sent over a local Unix-domain
    socket.
  • Resources are bundled with the application and can be installed
    directly from Settings.

Secure local IPC

  • Per-product socket endpoints (Dev and production are isolated)
  • Private 0700 runtime directory
  • 0600 Unix socket
  • Same-user peer validation
  • Bounded message sizes
  • Session nonce and revision tracking
  • Automatic cleanup on shutdown

Terminal autocomplete

  • Suggestions are enabled only while a verified shell integration is
    active.
  • Existing terminal suppression remains in place when no integration
    source is available.
  • Terminal acceptance uses paste-based insertion rather than
    Accessibility text insertion.
  • Optimistic shell state keeps the ghost text synchronized after
    acceptance.

Claude Code support

  • OCR-based prompt detection for Claude Code when shell integration is
    unavailable.
  • Process-tree and window validation to ensure OCR results belong to
    the active terminal.
  • Fail-closed behavior if validation cannot be established.

Natural language command translation

Terminal prompts that are clearly natural-language instructions are
treated as command translation requests rather than autocomplete
continuations.

For example:

delete folder named dork

becomes

rm -rf -- dork

or

go to documents folder

becomes

cd ~/Documents

Accepting the suggestion replaces the entire line but does not execute
the command
. The user must still press Return manually.

Normal shell commands (git status, cd, ls, flags, pipes,
redirects, etc.) continue using standard inline autocomplete.

Settings

Adds a new opt-in Terminal Autocomplete (Beta) setting.

The Settings UI includes commands for enabling shell integration in zsh,
bash, and fish.

Testing

Verified:

  • Shell integration (zsh/bash/fish resources)
  • Terminal-only availability gating
  • Default-off behavior
  • Secure socket permissions and cleanup
  • Unicode cursor handling
  • Prompt anchoring
  • Claude Code OCR fallback
  • Terminal acceptance path
  • Natural-language command replacement
  • Atomic replacement ordering
  • No automatic command execution

Static validation:

  • SwiftLint
  • git diff --check
  • Shell script syntax validation (bash/zsh)

Notes

A bug discovered during implementation was also fixed:

  • macOS Unix sockets have a sun_path length limit (~104 bytes). The
    runtime socket now lives in the user's private temporary directory
    while installation resources remain in Application Support,
    preventing IPC failures on long home-directory paths.

Another regression fixed during review ensured terminal command
replacement is atomic. Previously, queued backspaces could race with
paste, causing the accepted command to be partially deleted. Replacement
now always deletes the original input before pasting the generated
command.

Greptile Summary

This PR adds opt-in terminal autocomplete support. The main changes are:

  • Shell integration scripts for zsh, bash, and fish.
  • A local Unix socket service for terminal prompt state.
  • Terminal and Claude Code focus adapters.
  • Paste-based terminal acceptance and command replacement.
  • Settings, docs, tests, and build resources for installation.

Confidence Score: 4/5

The terminal command replacement path needs a fix before merging.

  • Unicode prompt text can be under-deleted before the generated command is pasted.
  • The whole-line replacement event path may need to match the existing synthetic-event suppression path.
  • The IPC and resource packaging paths look consistent from the changed code reviewed.

Cotabby/App/Coordinators/SuggestionCoordinator+Acceptance.swift; Cotabby/Services/Suggestion/SuggestionInserter.swift

Important Files Changed

Filename Overview
Cotabby/Services/Terminal/TerminalIntegrationService.swift Adds the Unix socket listener, peer checks, framing, session tracking, revision checks, and cleanup.
Cotabby/App/Coordinators/SuggestionCoordinator+Acceptance.swift Adds terminal paste acceptance, optimistic terminal refresh, and terminal command replacement.
Cotabby/Services/Suggestion/SuggestionInserter.swift Adds paste-backed terminal insertion and ordered whole-line terminal replacement.
scripts/shell-integration/cotabby.bash Adds bash prompt and cursor reporting over the local socket.
scripts/shell-integration/cotabby.zsh Adds zsh prompt and cursor reporting over the local socket.
scripts/shell-integration/cotabby.fish Adds fish prompt and cursor reporting over the local socket.
Cotabby/Services/Terminal/TerminalIntegrationResourceInstaller.swift Copies bundled shell integration resources into the per-product hook directory.
Cotabby.xcodeproj/project.pbxproj Adds terminal sources and shell integration resources to the app targets.
project.yml Adds XcodeGen configuration for copying shell integration resources.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Shell as Shell integration
    participant IPC as TerminalIntegrationService
    participant Focus as Terminal focus model
    participant Coord as SuggestionCoordinator
    participant Insert as SuggestionInserter
    Shell->>IPC: Prompt and cursor update
    IPC->>Focus: Verified terminal snapshot
    Focus->>Coord: Focused input context
    Coord->>Coord: Build autocomplete or command replacement
    Coord->>Insert: Accept via paste or line replacement
    Insert->>Shell: Backspace burst then paste
    Coord->>IPC: Optimistic terminal buffer update
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Shell as Shell integration
    participant IPC as TerminalIntegrationService
    participant Focus as Terminal focus model
    participant Coord as SuggestionCoordinator
    participant Insert as SuggestionInserter
    Shell->>IPC: Prompt and cursor update
    IPC->>Focus: Verified terminal snapshot
    Focus->>Coord: Focused input context
    Coord->>Coord: Build autocomplete or command replacement
    Coord->>Insert: Accept via paste or line replacement
    Insert->>Shell: Backspace burst then paste
    Coord->>IPC: Optimistic terminal buffer update
Loading

Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(terminal): fix command replacement o..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@akramj13 akramj13 requested review from FuJacob and Copilot July 11, 2026 03:14
@akramj13 akramj13 self-assigned this Jul 11, 2026
@akramj13

Copy link
Copy Markdown
Collaborator Author

@greptileai

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in “Terminal Autocomplete (Beta)” pipeline that enables Cotabby suggestions in terminal environments using cooperative shell hooks over a local Unix-domain socket, with a fail-closed Claude Code TUI OCR fallback. This fits Cotabby’s architecture by introducing terminal-specific source adapters and keeping the existing suggestion coordinator as the central orchestration point, while avoiding unreliable AX text reads in terminal surfaces unless an authoritative source is active.

Changes:

  • Introduces a secure local IPC server + bundled shell hook resources (zsh/bash/fish) to stream prompt buffer + cursor state into Cotabby.
  • Adds terminal/TUI focus arbitration and OCR-based Claude Code prompt extraction, publishing authoritative focus snapshots into the existing suggestion pipeline.
  • Adds terminal-specific prompting, normalization, and acceptance behavior (paste-based insertion; whole-line command replacement mode for natural-language instructions).

Reviewed changes

Copilot reviewed 59 out of 59 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
TERMINAL_AUTOCOMPLETE.md User-facing setup + security/privacy/limitations documentation for terminal autocomplete.
README.md Adds terminal autocomplete feature mention + link to detailed doc.
project.yml Bundles shell-integration scripts into app resources.
scripts/shell-integration/cotabby.zsh zsh hook to report ZLE buffer/cursor over Unix socket.
scripts/shell-integration/cotabby.bash bash hook + readline key wrapping for per-keystroke buffer reporting (Bash 4+).
scripts/shell-integration/cotabby.fish fish hook + key bindings wrapping for buffer reporting.
Cotabby/App/Core/CotabbyAppEnvironment.swift Composes terminal subsystem (IPC + OCR + focus arbitration) and wires it into the coordinator.
Cotabby/App/Core/AppDelegate.swift Starts/stops terminal integration with the opt-in setting and screen-recording permission changes.
Cotabby/App/Coordinators/SuggestionCoordinator.swift Adds terminal/TUI observer hooks and terminal scheduling state.
Cotabby/App/Coordinators/SuggestionCoordinator+Input.swift Adjusts prediction scheduling to avoid polling terminal AX and to feed TUI OCR observer.
Cotabby/App/Coordinators/SuggestionCoordinator+Prediction.swift Adds terminal replacement mode handling (no streaming for replacements; fail-closed validation).
Cotabby/App/Coordinators/SuggestionCoordinator+Acceptance.swift Implements terminal paste insertion mode + whole-line terminal replacement acceptance.
Cotabby/App/Coordinators/SuggestionCoordinator+Lifecycle.swift Threads terminal integration gating into lifecycle evaluator calls.
Cotabby/Models/FocusModels.swift Extends focus snapshot with terminal metadata + terminal role helpers + focus sequence retagging.
Cotabby/Models/SuggestionModels.swift Adds request/result mode and terminal replacement SuggestionKind plumbing.
Cotabby/Models/SuggestionSubsystemContracts.swift Extends insertion contract for terminal paste + whole-line terminal replacement.
Cotabby/Models/SuggestionSettingsData.swift Reframes integrated-terminal toggle as terminal autocomplete master opt-in (source-verified).
Cotabby/Models/SuggestionSettingsModel.swift Updates setting documentation for terminal opt-in semantics.
Cotabby/Models/TerminalIntegrationPaths.swift Defines per-product socket/hook filesystem layout (incl. sun_path constraints).
Cotabby/Models/TerminalFocusModels.swift Adds terminal IPC message model + authoritative terminal focus snapshot/state types.
Cotabby/Models/TerminalAwareFocusModel.swift Arbitrates between AX focus and authoritative terminal (hook/OCR) focus for suggestions.
Cotabby/Services/Suggestion/ContextBuffer.swift Ensures terminal identity changes (elementIdentifier/role) advance generation safely.
Cotabby/Services/Suggestion/SuggestionInserter.swift Adds terminal paste mode + ordered backspace-then-paste whole-line replacement with shared clipboard restore.
Cotabby/Services/Suggestion/SuggestionOverlayPresenter.swift Hides overlay until terminal caret geometry is non-zero/trustworthy.
Cotabby/Services/Terminal/TerminalIntegrationService.swift Implements IPC session model + bounded same-user socket server + message validation.
Cotabby/Services/Terminal/TerminalIntegrationResourceInstaller.swift Installs bundled shell hooks into per-product Application Support with private permissions.
Cotabby/Services/Terminal/TerminalGeometryResolver.swift Reads terminal window frame/title via AX for OCR coordinate mapping and cheap TUI signals.
Cotabby/Services/Terminal/TerminalWindowScreenshotService.swift Captures frontmost terminal window (or embedded pane region) via ScreenCaptureKit for OCR.
Cotabby/Services/Terminal/ShellPromptGeometryCoordinator.swift Performs debounced OCR to anchor shell prompt row once, then tracks caret arithmetically.
Cotabby/Services/Terminal/TuiContextReader.swift Reduces OCR output to Claude Code prompt text + cursor estimate using fail-closed heuristics.
Cotabby/Services/Terminal/TuiContextCoordinator.swift Owns debounced/permissioned Claude Code OCR lifecycle and injects snapshots into effective focus.
Cotabby/Services/Runtime/OpenAICompatibleSuggestionEngine.swift Threads request mode through result reporting.
Cotabby/Services/Runtime/LlamaSuggestionEngine.swift Threads request mode through result reporting.
Cotabby/Services/Runtime/FoundationModelSuggestionEngine.swift Threads request mode through result reporting.
Cotabby/Support/FoundationModelPromptRenderer.swift Adds terminal-specific instructions/prompt shapes for Foundation Models path.
Cotabby/Support/ProcessTreeInspector.swift Adds lightweight Darwin process ancestry inspection for TUI detection.
Cotabby/Support/SuggestionAvailabilityEvaluator.swift Adds terminal integration gating (opt-in + authoritative live source required).
Cotabby/Support/SuggestionRequestFactory.swift Adds terminal request mode selection + terminal prompt rendering and token floor for replacements.
Cotabby/Support/SuggestionSessionReconciler.swift Invalidates sessions when terminal source identity changes.
Cotabby/Support/SuggestionTextNormalizer.swift Adds terminal replacement scaffolding removal (fences/Command:/$).
Cotabby/Support/TerminalAppDetector.swift Expands terminal host detection to embedded-terminal hosts + terminal host helper.
Cotabby/Support/TerminalCommandIntentPolicy.swift Deterministic classifier for “natural language command replacement” mode.
Cotabby/Support/TerminalCompletionPromptRenderer.swift Terminal-specific base-model prompt shaping (shell continuation vs Claude Code vs replacement).
Cotabby/Support/TerminalFocusAdapter.swift Adapts hook-sourced terminal snapshots into FocusedInputSnapshot form.
Cotabby/Support/TerminalPromptAnchor.swift OCR prompt anchoring model + caret geometry calculation utilities.
Cotabby/Support/TuiSessionDetector.swift Cheap pre-OCR classifier for Claude Code via title/process signals.
Cotabby/Support/TuiFocusAdapter.swift Adapts OCR-sourced Claude Code prompt into FocusedInputSnapshot form.
Cotabby/Services/Visual/ScreenTextExtractor.swift Publishes OCR lines with geometry (positionedLines) alongside existing hygiene pipeline output.
Cotabby/UI/Settings/SettingsIndex.swift Renames/retargets settings label/description/keywords for terminal autocomplete.
Cotabby/UI/Settings/Panes/AppsPaneView.swift Adds opt-in toggle UI + shell setup command copying UI with quoting.
CotabbyTests/TerminalAppDetectorTests.swift Updates expected disable reason string for terminal gating.
CotabbyTests/SuggestionAvailabilityEvaluatorTests.swift Updates gating expectations: opt-in alone doesn’t authorize raw terminal AX.
CotabbyTests/SuggestionStateHelperTests.swift Ensures terminal session identity participates in generation advancement.
CotabbyTests/SuggestionCoordinatorTestSupport.swift Updates test inserter to record insertion modes.
CotabbyTests/SuggestionCoordinatorAcceptanceTests.swift Adds tests for terminal paste insertion and whole-line command replacement acceptance.
CotabbyTests/SyntheticReplacePlannerTests.swift Adds tests ensuring terminal replacement ordering is delete-then-paste and never submits commands.
CotabbyTests/TerminalIntegrationModelTests.swift Adds pure-model tests for terminal offsets, replacement intent, prompting, and resource install perms.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +52 to +57
} catch {
logger.error("Terminal integration failed to start: \(error.localizedDescription)")
server = nil
isRunning = false
return false
}
Comment on lines +451 to +456
var bytes = [UInt8](repeating: 0, count: 4_096)
while true {
let count = Darwin.read(descriptor, &bytes, bytes.count)
if count > 0 {
clients[descriptor]?.buffer.append(contentsOf: bytes[0..<count])
guard processFrames(for: descriptor) else {
Comment on lines +530 to +533
guard suggestionInserter.replaceTerminalLine(
deletingCharacterCount: originalText.count,
with: session.fullText
) else {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unicode Delete Count Mismatch

When a terminal command-replacement prompt contains emoji or another multi-unit character, this path deletes originalText.count graphemes even though the inserter sends repeated Backspace events. The terminal can leave part of the original instruction in the buffer before pasting the generated command, so the user may execute a corrupted command after pressing Return.

Suggested change
guard suggestionInserter.replaceTerminalLine(
deletingCharacterCount: originalText.count,
with: session.fullText
) else {
guard suggestionInserter.replaceTerminalLine(
deletingCharacterCount: originalText.utf16.count,
with: session.fullText
) else {

Fix in Codex Fix in Claude Code

Comment on lines +280 to +281
for event in events {
event.post(tap: .cgAnnotatedSessionEventTap)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Replacement Events Bypass Suppression

This whole-line terminal replacement posts Backspace and Cmd-V events at .cgAnnotatedSessionEventTap, while the other synthetic insert and replace paths use .cghidEventTap and register the expected synthetic keydown burst. If the input monitor only observes the HID tap path, the synthetic Cmd-V or Backspace events can be treated as real input during the accept callback, which can re-enter acceptance handling or interleave with the user's held Tab while the prompt is being deleted and pasted.

Fix in Codex Fix in Claude Code

@akramj13

Copy link
Copy Markdown
Collaborator Author

Terminal Autocomplete USAGE / REVIEWER GUIDE (Beta)

Cotabby can provide inline autocomplete in dedicated terminal apps and integrated terminal panes.
It is off by default because terminal output is not a normal editable text field and must never be
treated as one without a trustworthy input source.

Enable and set up

  1. Open Settings → Apps and enable Terminal Autocomplete (Beta).
  2. Grant Screen Recording if prompted. Cotabby uses it to locate the visible shell prompt and to
    read Claude Code's input box; screenshots and recognized text stay on the Mac.
  3. Copy the zsh, Bash, or fish command shown in Settings into that shell's startup file.
  4. Open a new terminal window.

Settings generates paths for the exact app build being used. This matters during development:
Cotabby and Cotabby Dev use different sockets and installed hook directories, so they can run
side-by-side without unlinking or impersonating each other's endpoint.

zsh is recommended. It uses line-pre-redraw and does not replace normal key bindings. Bash 4+
and fish do not expose an equivalent generic redraw hook, so their beta hooks wrap printable-key
bindings and may conflict with custom Readline/fish bindings. The stock Bash included with macOS
only provides prompt heartbeats; install Bash 4 or newer for live per-keystroke suggestions.

How it works

Shell prompts and terminal TUIs use separate source adapters:

  • zsh, Bash, and fish: a sourced hook sends the exact line-editor buffer, cursor, shell, TTY,
    working directory, session nonce, and monotonic revision over a local Unix-domain socket. OCR
    matches that exact buffer to the visible prompt once, then cursor movement is tracked from the
    hook's character offset until the anchor expires or the prompt moves.
  • Claude Code: the shell no longer owns stdin, so Cotabby checks the frontmost terminal title
    and process tree, captures only the active terminal window or integrated pane, and runs Apple's
    Vision OCR locally. A Claude Code screen fingerprint and a prompt line must both be present before
    the OCR text becomes autocomplete focus.

Both sources feed the existing suggestion coordinator. The normal accept shortcuts still apply;
there is no separate terminal-only shortcut. Accepted terminal text is inserted through paste so
bracketed-paste-aware shells and TUIs receive literal text.

At an end-of-line shell prompt, a short imperative English instruction such as
delete folder named dork enters command-replacement mode. Cotabby shows the complete translated
command as a replacement offer; accepting deletes the unchanged English line and pastes the command.
It never presses Return, so commands—including destructive ones—remain visible for review and only
run after the user explicitly submits them. Real shell syntax, paths, flags, pipes, and redirects
remain on the normal continuation path.

Security and privacy boundaries

  • The socket lives under the current user's private temporary directory so its endpoint stays below
    macOS's Unix-socket path limit. Its directory is mode 0700, the socket is mode 0600, and peer
    user IDs are checked before any frame is decoded. Installed hook copies remain in Application
    Support.
  • Existing non-socket paths, sockets owned by another user, and live endpoints owned by another
    Cotabby process are never removed. Only a same-user, non-listening stale socket is replaced.
  • Frames are newline-delimited JSON capped at 64 KiB. Shell PIDs must belong to the current user;
    bundle IDs, TTY paths, nonces, revisions, cursor offsets, and text sizes are validated.
  • The IPC/OCR subsystem does not write terminal data to disk or make network requests. Like normal
    autocomplete text, the result flows to the engine selected in Cotabby; choosing a remote
    OpenAI-compatible endpoint therefore carries the same privacy tradeoff as it does in other apps.
  • Enabling the preference is only a master switch. Cotabby still refuses terminal autocomplete
    unless a live hook snapshot or a currently verified Claude Code OCR snapshot owns focus.

Current limitations

  • Screen Recording is required for trustworthy prompt positioning and for Claude Code OCR.
  • Claude Code OCR is best-effort and intentionally fail-closed. If the UI fingerprint, prompt line,
    frontmost app, or window geometry changes during OCR, Cotabby hides the terminal suggestion.
  • Multiplexer panes and heavily customized prompt themes can make OCR anchoring less reliable.
  • Bash/fish custom key bindings may need manual adjustment; zsh is the compatibility-first path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Inline autocomplete inside terminals (shell-hook IPC + Claude Code TUI OCR)

2 participants