Research: how should DropTerm close the gap between its shipped autosuggestions and Warp’s autocomplete — the interactive surface, spec breadth, dynamic values, fuzzy matching, and aliases — without breaking the no-injection verdict or the terminal-side architecture?

Date: 2026-11-02 Question: Warp’s autocomplete is a spec-driven popup menu with descriptions, dynamic value candidates (branches, containers), fuzzy matching, and alias resolution. DropTerm ships terminal-side ghost text from four merged providers (terminal-autocomplete, stages 1–2 complete). Which slices of the remaining Warp delta should DropTerm adopt, in what order, and which must be rejected?

TL;DR

DropTerm should add a second, explicit-trigger completion menu beside the shipped ghost — Ctrl+Space to open, fuzzy-filterable, descriptions from spec data, accept via Tab/Enter only while the menu is open (VS Code’s scoped mechanism, which leaves bare Tab owned by the shell) — and this supersedes the 2026-10-17 record’s “completion-spec menus … deferred indefinitely” rejection on that record’s own terms: its stated condition (“value arrives only after inline suggestions prove the prompt-detection layer”) is now met, and the Tab-conflict objection now has a pinned, shipping solution. The ghost itself stays single-choice and strict-prefix — fish deliberately turns fuzzy matching and descriptions off in autosuggestion mode, and those two features belong to the menu surface. Spec breadth grows down the path the stage-2 record already named: the curated table moves behind a loadable data file, and the eventual broad library is derived from withfig/autocomplete — MIT, 715 specs, the one source both Warp and VS Code build on — under the per-content licensing audit; carapace as a runtime dependency and any Warp AGPL client code are rejected. Dynamic value candidates split: file-derived dynamics are allowed (git branches/tags by reading .git/refs in the tab’s live cwd — the path provider’s own category), while spawn-based generators (git branch, docker ps run by DropTerm) are rejected for now — Warp executes them through a live shell session it owns and VS Code through session env vars its shell integration reports; DropTerm has neither the session env nor the shell’s alias/function state, so a generator run by DropTerm is a command DropTerm chose, in an environment it cannot reproduce. Alias resolution is rejected on the same grounds (the expansion lives in the user’s shell state). AI stays rejected — re-verified: neither of Warp’s two completion pages mentions AI, cloud, or models today; its completions are specs plus local generators.

This record extends terminal-de-integration (no shipped scripts, no env injection, no OSC engine fork — nothing here needs that verdict revisited), extends the display-mode record (autosuggest-display-mode, 2026-10-24: the menu is a separate surface, not a display mode), and supersedes exactly one ruling of terminal-autocomplete (2026-10-17): the phase-1 rejection of completion-spec menus. That record’s “a menu is a later, separate decision with zero regression risk to the inline path” is the decision now made; both records stay on file. The stage-2 crosscheck’s named growth path (“the flag-specs table keeps a nameable path to a loadable data file” — autosuggest-stage2-crosscheck, 2026-10-21) is consumed here, not superseded.

What DropTerm has today

Grounded in the working tree at HEAD fe1cd72 (2026-11-01):

Findings

1. Warp today — the delta decomposed against docs and, since April 2026, open source

Warp’s two completion surfaces, read 2026-11-02 (autosuggestions, completions):

Two facts upgrade this survey from docs-only to source-pinned:

2. The surface precedent is now unanimous: always-on ghost + explicit menu — nobody auto-pops by default

Every system surveyed puts the interactive list behind an explicit trigger (or a deliberate opt-in), keeping the zero-UI ghost as the ambient surface:

For DropTerm: the ghost is shipped and correct as the ambient surface; the menu is the missing second surface, and it should ship explicit-trigger — matching every peer’s default, with ble.sh’s idle-delayed auto-menu and Warp’s as-you-type both available as later opt-ins if ever asked for.

3. The Tab tension has a pinned, shipping solution — VS Code’s scoped keybinding

DropTerm’s own constraint is written down (src/keybinds.rs:90-92). VS Code solves the same conflict with a state-scoped binding rather than a global one — terminal.suggest.contribution.ts:558-563 @ 67710841ad061a:

{
    primary: KeyCode.Tab,
    // Tab is bound to other workbench keybindings that this needs to beat
    weight: KeybindingWeight.WorkbenchContrib + 2,
    when: ContextKeyExpr.and(SimpleSuggestContext.HasFocusedSuggestion)
},

Tab is swallowed only while the suggest widget is open with a focused item; with no widget visible, Tab reaches the shell untouched. Enter carries additional conditions (accept when explicitly invoked, or not in partial selection mode, or after navigation — :565-569), the default selectionMode: 'partial' being “Tab can be used to accept the first suggestion, only after navigating the suggestions via Down will Enter also accept” (terminalSuggestConfiguration.ts:148-152). DropTerm’s widget already routes soft keys exactly this way — accept_kind consults terminal.suggestion() before swallowing Right (src/terminal/widget.rs:1252-1259) — so “while the completion menu is open, Tab/Enter accept; otherwise Tab falls through to \x09” is the same pattern with one more state bit. Warp’s version of the same trade: rebinding Tab to ghost-accept moves the menu trigger to Ctrl+Space (docs, finding 1) — the menu trigger is the movable piece, the shell’s Tab never is.

4. Spec data — the whole industry builds on one dormant MIT repo; licenses verified

License verdict table (the per-content audit precedent from terminal-theming applies): Fig specs MIT — usable with attribution; VS Code’s bundled specs MIT — usable; carapace-bin MIT — usable as data format reference, rejected as runtime dependency; Warp client AGPL-3.0 — not usable.

5. Dynamic values — who spawns what, and what it costs

The objections, graded: (a) side effects — a spec file that says make clean is indistinguishable from one that says git branch unless DropTerm ships an allowlist of commands and arguments; VS Code’s specs mark dangerous options (isDangerous: true on rm -f in upstream/rm.ts) but nothing gates the spawn itself. (b) wrong environment — DropTerm’s process env is the desktop session’s, not the tab’s shell’s: no rc-file aliases/functions, no direnv, possibly a stale PATH; Warp patches PATH by hand and Termius pays with exec channels. (c) wrong directory — solvable locally: /proc/<shell_pid>/cwd readlink is the same local truth the path provider’s gate already reasons about. (d) revocation/trust — this is the first DropTerm feature where the terminal would execute a command it chose from a data file rather than bytes the user typed; the autosuggest invariant so far is “inert pixels until an accept key sends it,” and spawns break that frame.

6. Fuzzy matching and descriptions belong to the menu, not the ghost

Warp’s fuzzy is a menu/list behavior (finding 1: subsequence fallback in matchers.rs). fish disables fuzzy and descriptions precisely in autosuggestion mode — fn fuzzy_match(&self) -> bool { !self.autosuggestion() } (src/complete.rs, pinned in autosuggest-stage2-crosscheck R2/R3) — because a single silent choice that must be accepted whole pays a wrong accept for an approximate guess. Descriptions are the same story: they are rendered in menus and inspectors (Warp’s describe.rs; VS Code’s widget details pane), never in ghost text. DropTerm should therefore keep both out of the ghost (the shipped behavior is already correct) and add both with the menu, where every peer has them. Within the menu, Warp’s exact policy is worth copying verbatim: prefix/case-insensitive first, subsequence fallback only for queries with ≥1 alphanumeric character.

7. AI — still no vendor puts a model in the completion loop; Warp’s open-sourcing changes nothing here

Re-verified 2026-11-02: neither Warp completion page mentions AI, cloud, models, or telemetry — the mechanism is specs plus locally executed generators. VS Code’s suggest docs mention no AI (its AI features are separate extensions; the inlineSuggestion setting only integrates shell- provided ghost text such as PredictiveIntelliSense). Warp’s 2026-04-28 open-sourcing post is agent-first (“agentic development environment”) and does not mention completions. DropTerm’s standing local-only stance (terminal-autocomplete §8 privacy) is therefore not merely defensible but the same line Warp and VS Code themselves draw for this feature: completions are deterministic lookups; AI lives elsewhere.

8. Peer re-check since 2026-10-17 — the “no terminal-side peer” claims still hold, with version bumps

Checked 2026-11-02: Ghostty has shipped v1.4.0 (after v1.3.1) — no completions feature; the release notes’ only completion mention is “nu: Tab completion for ssh and sudo now works with external completers such as carapace” (shell integration for the ghostty CLI wrapper), and src/config/Config.zig @ master 042b16b6146a still contains zero completion/autosuggest keys. kitty v0.49.2 (2026-10-01): unchanged, still CLI-only completion. Alacritty still v0.17.0 (2026-04-06). WezTerm @ e68a8ec277 (2026-10-26): no completion/ghost code. Windows Terminal: the Suggestions UI is still “only available in Windows Terminal Preview” (actions docs, updated 2025-08-21) and recentCommands is still “powered by shell integration.” Termius: per finding 5. So DropTerm’s shipped feature remains without a terminal-emulator precedent; VS Code (an editor) and Warp (an editor-paradigm terminal, now AGPL) remain the only implementations with a menu, and Clink/ble.sh the line-editor analogues.

Recommendation

Phase A — the completion menu (the supersession). A second surface, explicit-trigger, drawing on machinery that all exists:

  1. Trigger: a new Action::ShowCompletions bound to Ctrl+Space (VS Code and Clink’s trigger; Warp’s fallback), customizable through the existing keybind overlay (src/keybinds.rs defaults + custom model). Note the desktop caveat both VS Code documents (IME conflict) — a customizable default is the mitigation. The action is a soft trigger: with no completion context (the guard chain’s own gates — not idle, scrolled, unanchored), it falls through to the shell byte.
  2. Eligibility = the shipped guard chain verbatim (showing’s gates minus the single-candidate requirement): the menu opens only at an idle prompt with a typed segment, exactly where the ghost already computes.
  3. Contents = the merged providers’ full candidate sets, not the merged single choice: history prefix matches, path listing matches, spec subcommands/flags, catalog names — ranked by the same source_rank order, descriptions shown where the data has them (the loadable specs of phase B; the curated table gains descriptions as it grows). This is fish’s Tab split (one ghost vs the whole ranked list) rendered terminal-side.
  4. Keys inside the menu: Up/Down navigate; Tab and Enter accept only while the menu is open — the VS Code scoped-when mechanism, DropTerm’s accept_kind pattern with a menu-open state bit; Esc/any other key/scroll/focus-loss closes and forwards. Bare Tab with the menu closed remains the shell’s \x09, preserving src/keybinds.rs:90-92 verbatim.
  5. Filtering: Warp’s pinned policy — case-insensitive prefix first, subsequence fallback only for queries containing an alphanumeric. The ghost stays strict-prefix single-choice, unchanged (finding 6).
  6. Rendering: an overlay in the widget’s draw(), the ghost overlay’s own pattern (src/terminal/widget.rs:920-970) — background quad, rows, selection highlight, never the grid. src/context_menu.rs stays the app-menu precedent; this is a terminal-surface popup and belongs in the widget.
  7. Acceptance: the chosen completion’s suffix (minus the typed prefix) through Terminal::input_no_scroll, the ghost-accept path — the VS Code addon’s synthesis model DropTerm already proved with Right/Alt+Right.
  8. Config: autosuggestions_menu_enabled (default true — the ghost and the trigger are separable; false restores today’s surface exactly), documented under the every_config_key_is_documented gate; the long-pending autosuggestions_specs_enabled key lands with phase B.

Phase B — spec breadth down the named path. Move SPECS behind a loader: a versioned data file (TOML or JSON, one table per command — subcommands, long flags, descriptions) built-in as compiled-in defaults plus a user-writable location for overrides/additions, the exact shape the stage-2 record reserved (“a loadable data file … without touching the merge”, src/terminal/suggest.rs’s growth-path comments at :568-574). Breadth beyond hand-curation: a one-time offline conversion from withfig/autocomplete (MIT) into DropTerm’s minimal format — subcommands, flags, descriptions, without generators/templates (see phase C) — for a chosen top-N command set, shipped under the per-content licensing audit with the MIT notice preserved, exactly as the theming record governs schemes. Rejected alternatives for the data source: carapace as a runtime binary (external dependency, Go-program completers, no terminal precedent), parsing Fig TS at runtime (a TS parser in a Rust terminal), and any Warp AGPL-derived table. The upstream dormancy (2025-05-05) is a maintenance risk priced in: the conversion is a snapshot DropTerm owns, not a live dependency.

Phase C — dynamic values, split. Allowed: file-derived candidates — git branches and tags by reading .git/HEAD, .git/refs/heads/**, and .git/packed-refs under the tab’s live cwd (/proc readlink, the local truth the shell gate already keys on), offered at the known argument seats of the specs data (e.g. git checkout <branch>). No execution, no env problem, wrong-directory solved by construction; the same category as the path provider. Rejected now: spawn-based generators (git branch, docker ps --format) — the security/correctness ledger of finding 5: no session env to reproduce the user’s shell (Warp runs generators through its session and even then force-sets PATH; VS Code passes session env its shell integration reports), no way to prove a data-file command read-only, and it breaks the “inert until accepted” frame by making DropTerm the actor. The named revisit trigger: a per-command user opt-in and an env story (e.g. accepting a documented, user-invoked env-reporting mechanism — which would be a new record superseding part of the no-injection verdict’s rationale, not this one). Rejected with it: alias resolution — aliases live in shell state DropTerm cannot read without executing alias or parsing rc files; note honestly that DropTerm’s history seed cannot learn them either (the grid sees the alias word, never the expansion).

AI: rejected, confirmed. No model, no network, no cloud fallback — even Warp’s completions, re-verified on both pages and in the open-source engine, are deterministic spec+generator lookups; the industry’s AI work touches agents and shell-provided ghost text, not terminal-computed completions.

Rejected, and why

Relation to prior records

Sources

All fetched 2026-11-02 unless noted.