Engineering notes

Why the terminal always knows where you are

How cwdio keeps a file panel and an embedded shell on the same folder both ways: OSC 7 shell integration, an idle-only rule against loops, per-shell snippets.

Retells ADR 0016 Published 15 August 2026 View as Markdown

Every dual-pane file manager with an embedded terminal has the same small, constant friction: you navigate to a folder in the panel, then type cd to the same folder in the shell — or the other way round. cwdio removes it by keeping the two in step in both directions. This is the story of how, retold from ADR 0016 and its directory-sync addendum.

The problem as felt

An embedded terminal that opens in the panel’s folder is table stakes. The friction starts a minute later: you cd into packages/vfs, run a build, then go looking for the output in the panel — which is still three levels up. Or you drill into a folder in the panel, switch to the terminal, and the prompt is still where you left it. Two working directories, and you are the synchronisation mechanism.

Fixing one direction is easy. Fixing both without the two chasing each other is the actual problem.

The signal: OSC 7

A shell has no way of telling a terminal where it is — unless it prints something. Modern terminals agreed on one thing to print: OSC 7, an operating-system-command escape sequence carrying the working directory as a file:// URL:

ESC ] 7 ; file://hostname/C:/projects/cwdio/packages/vfs BEL

Windows Terminal, iTerm2, WezTerm and VS Code all understand it, mostly to name tabs and to open a new tab in the same folder. cwdio uses it as the source of truth for the terminal’s cwd.

The shell has to be taught to emit it. cwdio injects a per-shell snippet at spawn (shell-integration.ts in the terminal package): PowerShell 7 and Windows PowerShell get a wrapped prompt function via an encoded command, so the user’s own prompt is preserved; cmd gets a PROMPT that includes the directory; bash gets a PROMPT_COMMAND. Every prompt then announces the cwd. Shells cwdio does not know how to instrument — a bare zsh, sh — are left alone. That is deliberate graceful degradation: a missing OSC means the sync simply does not fire, and a snippet can never break a terminal it was not written for.

The idle rule

The dangerous direction is panel → terminal, because it means writing into the shell’s stdin. Two things had to be true before cwdio would do that.

First, the terminal must be idle. Injecting cd D:\other into a shell that is halfway through running a build would be a real bug, not an inconvenience. Rather than adopt the fragile OSC 133 prompt-marking protocol, cwdio derives idleness from something it already has: an OSC 7 has arrived since the last line the user submitted. If a prompt has been printed after your last command, the shell is waiting for input.

Second, the two directions must not chase each other. A cd in the terminal moves the panel; the panel moving would normally cd the terminal; which prints a prompt; which reports a cwd. cwdio breaks the loop with idempotent path comparison and a per-session syncedCwd — if the terminal is already where the panel is going, nothing is sent. Boring, and exactly what you want.

The other direction has its own guard: a cd reported by the shell only moves the panel when that terminal has keyboard focus. A background script that wanders through directories does not yank your panels; the cd you type in the terminal you are looking at does.

What shipped

  • Navigating the active panel cds the idle terminal bound to that pane; the tab picks up the folder’s name.
  • A cd typed in a focused terminal moves the active panel.
  • Each session tracks its own cwd, idle state and last-synced path — the first cut kept one global “last shell cwd”, which was wrong the moment there were two sessions.
  • View ▸ Sync terminal to folder turns the whole thing off. It is on by default.
  • Early input is buffered until the pty exists, so a cd sent while the shell is still spawning is not dropped.

Over SSH

When an SFTP panel is open, the terminal for that pane is a real SSH shell on the same authenticated connection — no second login. The same idle model applies, with one more rule: sync is locality-aware. A local pty is only ever cd’d by a local panel; a shell on server A only by a panel on server A. A Windows path never lands in a POSIX shell, and a cd in one server’s shell never moves a panel browsing another. A remote shell with shell integration syncs both ways; one without follows the first navigation and then stops — safe, with no stdin injection into a shell whose state cwdio cannot see.

What it deliberately does not do

  • It never writes into a busy terminal. If that means the panel and the prompt disagree for the length of a long build, that is the correct outcome; they reconcile at the next prompt.
  • It does not use OSC 133 or any protocol the shell has to opt into beyond the snippet cwdio injects.
  • It does not detach terminals into separate OS windows. They are dock panels — movable beside, below or on top of the file panels — but they stay in the window.

The feature was later proven against the real Electron shell with real node-pty sessions: forward sync, reverse sync and a chosen non-default shell all round-tripped in the end-to-end suite. The one thing not covered automatically is each specific shell’s integration snippet beyond PowerShell — cmd, WSL and Git Bash are exercised by hand from the profile picker.

If you want the user-facing version, it is on the terminal sync feature page; the keys are in the shortcut table.

Questions

What is OSC 7?

An escape sequence a shell prints to tell its terminal which directory it is in — ESC ] 7 ; file://host/path. Modern terminals use it for tab titles and 'open new tab here'. cwdio uses it as the signal that moves the file panel when you cd.

Why does the panel not move when a background script changes directory?

Terminal-to-panel sync is gated on the terminal having keyboard focus. A script running in an unfocused session can cd all it likes without yanking your panels around; only a cd you type in the terminal you are looking at moves the panel.

Which shells report their directory?

PowerShell 7, Windows PowerShell, cmd and bash get a small integration snippet at spawn and report on every prompt. Shells cwdio does not know how to instrument still run normally; they simply do not report, so only the panel-to-terminal direction works for them.