How AI Agents Run Terminal Commands in VS Code
▶ Watch on YouTube & subscribe to The Stack Underflow
When an AI agent runs your build, fixes the error, and retries — all without you touching the keyboard — it looks like magic. It isn’t. The agent is doing exactly what you do: typing into the terminal. Understanding the plumbing behind that makes you a sharper developer and a better debugger when AI-assisted workflows go sideways.
This episode (part 4 of the “How Claude Actually Works” series) peels back the terminal layer to show you the three components that make agent-driven command execution work, and the one invisible mechanism that ties them together.
The one-sentence version: An AI agent runs terminal commands by writing into the same pseudo-terminal stream you use, and reads shell integration markers injected by the editor to know when each command finishes and whether it succeeded.
The Terminal Is Just a Renderer
The terminal panel you see in VS Code is not a terminal. It is a renderer — a UI component that displays text. It shows you output, it shows you a cursor, but it holds no execution state of its own.
Behind the renderer sits the real workhorse: the pseudo-terminal, or pty. The pty is a kernel-level construct that bridges two sides:
- The input side: where keystrokes (or programmatic writes) are fed in.
- The output side: where the shell writes back everything you see.
┌─────────────────────────────────────────────┐
│ VS Code UI │
│ ┌───────────────┐ ┌───────────────────┐ │
│ │Terminal Panel │ │ Editor / Agent │ │
│ │ (renderer) │ │ (Copilot/Claude)│ │
│ └──────┬────────┘ └────────┬──────────┘ │
│ │ display output │ write input │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ pseudo-terminal (pty) │ │
│ └──────────────────────┬───────────────────┘ │
│ │ │
│ ┌────▼────┐ │
│ │ shell │ │
│ └─────────┘ │
└─────────────────────────────────────────────┘
Everything flows through this one bridge. The pty is the choke point.
The Agent Sits Right Next to You
Here is the part that surprises most developers: the AI agent has no secret backdoor into the shell. It feeds input into the same pty input stream that your keyboard does. The shell cannot tell the difference between a keystroke from you and a write from the agent process. They look identical at the pty level.
This means the agent is subject to the same constraints you are. If the shell is waiting for a password prompt, the agent blocks. If a long-running process does not exit, the agent waits. No magic, no privileged channel.
Shell Integration: The Invisible Markers
Knowing how to write a command into the pty is only half the problem. The harder problem is knowing when the command is done and whether it succeeded. A raw pty output stream is just bytes — there is no built-in signal that says “the previous command finished with exit code 1.”
VS Code solves this with shell integration. When you open a terminal, VS Code injects a small script into your shell’s init sequence (.bashrc, .zshrc, or the PowerShell profile equivalent). That script plants invisible escape-sequence markers around each command lifecycle event:
| Marker event | What it signals |
|---|---|
CommandStart | A command is about to be executed |
CommandExecuted | The command line has been sent to the shell |
CommandFinished | The command has exited |
| Exit code annotation | The numeric exit code of the last command |
These markers are embedded as OSC (Operating System Command) escape sequences — invisible to the human eye in the rendered terminal, but parseable by the editor process reading the pty output stream.
# What the shell actually emits (simplified):
\e]633;A\a # CommandStart marker
\e]633;E;npm run build\a # CommandLine marker
npm run build # visible output begins
...build output...
\e]633;D;1\a # CommandFinished, exit code 1
Without these markers, the agent would be staring at an undifferentiated stream of text with no way to tell where one command ends and the next begins, or whether anything failed.
The Full Loop: How an Agent Runs Your Build
Putting it all together, here is what happens when an AI agent runs a command and retries on failure:
- The agent writes the command string (e.g.
npm run build\n) to the pty input. - The shell receives it, executes it, and streams output back through the pty.
- The editor reads that output stream and watches for the
CommandFinishedmarker. - The marker includes the exit code. A non-zero code tells the agent the build failed.
- The agent reads the preceding output (also captured from the stream) to see the error.
- The agent writes a corrective command to the pty input and repeats from step 2.
That single mechanism — pty in, markers out — is what makes agentic iteration over your build loop possible.
Common Misconceptions
- “The AI has direct access to my shell process.” It does not. The agent writes to the pty input stream, exactly as your keyboard does. The shell process itself does not know it is talking to an AI.
- “Shell integration is optional.” For basic display, yes. But without shell integration markers, the agent cannot reliably detect command completion or parse exit codes. Many agentic features silently degrade or become impossible without it.
- “The terminal renderer is where execution happens.” The renderer is purely display. Execution lives in the shell, accessed through the pty. You could swap the renderer entirely and the shell would not notice.
- “Agents have faster or privileged I/O into the terminal.” No. Agent writes go through the same pty buffer as your keystrokes. If you and the agent both try to write at the same time, you get a race condition — which is one reason agentic tools typically lock the terminal while a command is running.
Frequently Asked Questions
Why does the agent sometimes miss output from commands I run manually? Shell integration markers are generated by the shell for commands it runs. If you run a command in a separate terminal window (outside VS Code) or in a shell session that pre-dates the injected integration script, there are no markers for the agent to read, so it may miss that run entirely.
What happens if shell integration is disabled or fails to inject? The agent falls back to heuristics — watching for prompt patterns in the raw text stream, or timing out after a fixed interval. This is less reliable. You may notice the agent seeming to “wait too long” or acting on stale output. Checking that shell integration is active (VS Code status bar shows a terminal icon with a checkmark) is a good first debugging step.
Can an agent send input to an interactive program like vim or a REPL?
Technically yes — it can write bytes to the pty input. In practice, interactive TUI programs use raw mode and respond to specific key sequences, which makes reliable agentic interaction much harder. Most AI coding tools avoid this and either error out or instruct you to handle interactive prompts yourself.
Does this mechanism work the same on Windows (PowerShell/cmd) as on macOS/Linux? Shell integration scripts exist for PowerShell, bash, zsh, and fish. The underlying pty mechanism on Windows uses a ConPTY (Console Pseudo Terminal) introduced in Windows 10, which behaves similarly to the Unix pty for this purpose. The marker escape sequences are the same; only the shell init file differs.
Where This Fits in the Series
This episode is part 4 of “How Claude Actually Works” — a course that dismantles the VS Code AI stack one layer at a time, from tokens and context windows all the way up to multi-machine agent architectures. Understanding the pty and shell integration layer is foundational: it explains not just how agents run commands today, but why certain limitations exist and where the abstraction boundaries live. The next episode covers how VS Code splits the UI and the language model brain across machines.
Browse all tutorials to follow the full series in order.
Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.
Subscribe on YouTube →