# 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.

- Canonical: https://cwdio.com/blog/why-the-terminal-knows-where-you-are/
- Updated: 2026-08-15
- Product: Cwdio File Manager (cwdio) — https://cwdio.com

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:

```text
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 `cd`s 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](/features/terminal-sync/); the
keys are in the [shortcut table](/docs/keyboard-shortcuts/).

## Frequently asked 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.
