Skip to content
| Marketplace
Sign in
Visual Studio Code>AI>Pi Agent VSCodeNew to Visual Studio Code? Get it now.
Pi Agent VSCode

Pi Agent VSCode

Rahul Arya

|
95 installs
| (0) | Free
Cursor-like chat sidebar for the local Pi coding agent
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Pi Coding Agent

Cursor-like chat sidebar for your local Pi coding agent.

The extension loads your installed Pi SDK directly in the VS Code extension host. It shares ~/.pi/agent (auth, models, packages, sessions, skills, and extensions) and renders the whole agent loop — streaming replies, tool cards, diffs, terminal output, model and thinking controls, slash commands, todos, and session history — inside a VS Code / Cursor webview.


Contents

  • Requirements
  • Install
  • The chat
    • How a turn is laid out
    • Tool cards
    • Code, diffs, and terminal output
    • Navigating to code
    • Message actions
    • Context, tokens, and cost
  • The composer
    • Mentions and context sources
    • Slash commands
    • Attachments
    • Drafts and history
  • Sessions
  • Keyboard shortcuts
  • Commands
  • Settings
  • Settings and accounts page
  • Development
  • Architecture

Requirements

  • VS Code or Cursor 1.90+
  • Local Pi: npm i -g @earendil-works/pi-coding-agent or bun i -g @earendil-works/pi-coding-agent
  • A provider configured in ~/.pi/agent, or use the extension's Accounts page to sign in

Install

If the pi CLI is missing, the chat shows an install card instead of an error: it detects macOS, Linux, WSL, or Windows, picks bun when you already have it and npm otherwise, prints the exact command, and runs it in a terminal so you can watch it. Press Retry afterwards, or point piChat.piPath at a binary you installed yourself.

Once Pi is installed but no provider is signed in, the chat shows a connect a provider card with a button straight to the Accounts page — API key, browser sign-in, and device-code flows all run through your local Pi install. It clears itself the moment a credential exists, including one supplied by an environment variable.

The extension activates with the editor and starts Pi in the background, so the chat is ready the first time you open it rather than spending a few seconds booting. Set piChat.startOnLaunch to false to start it on first use instead.

From a packaged build:

npm run package
code --install-extension pi-agent-vscode-<version>.vsix    # or: cursor --install-extension ...

Reload the window afterwards — a running window keeps the previous build loaded. Open the chat from the Pi icon in the activity bar, the command palette (Pi Coding Agent: Open), or ctrl+alt+i.


The chat

How a turn is laid out

One agent run is one chat bubble, and inside it prose and tool calls appear in the order the model produced them:

I'll read the file first.
  ┌ read  src/greet.ts                      3 lines ┐
Found it — the helper ignores the excited flag.
  ┌ edit  src/greet.ts                        +5 -2 ┐
  ┌ bash  npm test                             4.2s ┐
Tests pass.

Prose written after a tool call renders below that tool's card rather than being glued onto the sentence before it. Consecutive calls share one stack, but each call always keeps its own card.

Assistant messages show reasoning in a collapsible Thought section (hidden entirely when Pi's hideThinkingBlock is on), a "Worked for 12s" footer, and a copy button. Answers longer than ~4,000 characters fold to their tail with a Show full response button so one long reply cannot bury the rest of the conversation.

Tool cards

Every tool call renders as its own card labelled with the exact name Pi registered — bash stays bash, hypa_read stays hypa_read. Cards are typed by what the tool does:

Kind Rendering
diff Cursor-style unified diff with per-side gutters and +n -n stats
terminal Terminal body with the command echoed as $ …
file Syntax-highlighted contents with line numbers
search One row per hit, each opening the file at that line
todo Compact one-liner; the live list is the Todos panel

Status is honest about what happened: running cards spin, failures show failed and auto-expand once, and a call still running when the turn ends (abort, crash, or a reloaded transcript with no persisted result) is marked interrupted instead of spinning forever. Calls that took over a second show their duration.

Code, diffs, and terminal output

Syntax colours come from your theme. Every token colour resolves from VS Code theme variables (symbolIcon.*, debugTokenExpression.*, falling back to charts.*), so code matches whatever theme you use, light or dark. Markdown fences, file cards, and diff rows all share one palette. Fences that declare a language highlight while they stream; auto-detection runs once the reply settles.

Terminal output renders like a terminal. ANSI colours map to your terminal palette (terminal.ansi*), including bright variants, 256-colour, and truecolour, plus bold, dim, italic, underline, and inverse. Carriage-return progress lines collapse to their final frame instead of unrolling into hundreds of rows, and erase sequences clear the line the way a real shell does.

Navigating to code

Everything that names code is clickable:

  • File paths in tool card headers open that file — edit results land on the first changed line.
  • Diff rows open the file at that specific line.
  • Search hits open the file at the matching line.
  • Inline code in replies links too: a path (src/pages/CardGallery.tsx, with optional :42) opens the file, and an identifier (handleConfirmDelete, CardGallery, renderPaginationItems()) jumps to its declaration through VS Code's symbol index, with a picker when the name is ambiguous.

Single lowercase words in backticks stay plain text — a link that goes nowhere is worse than no link.

Message actions

Hover any message:

  • User — edit and resend (drops the prompt back in the composer), resend as-is, copy.
  • Assistant — regenerate (re-sends the prompt that produced it), copy.

Context, tokens, and cost

The composer toolbar shows a live meter fed by the usage Pi reports on every turn: a fill bar with the percentage of the model's context window, and session cost when the provider prices tokens. It turns amber at 75% and red at 90%, and clicking it compacts the conversation. While Pi compacts, it says so; prompts queued behind a running turn show as ↑n.


The composer

Mentions and context sources

Type @ to search the workspace. The index covers the whole tree (build output excluded) and searching happens on the host, so a deep file like src/pages/Loyalty/_components/CardGallery.tsx is found by typing CardGallery, cardgallery, or even cardgal — matching is case-insensitive with subsequence support, filename hits ranked first.

The same picker offers live editor context:

Source Inserts
@problems Errors and warnings across the workspace, file:line
@terminal The last 80 lines of the active terminal
@git Working tree status and diff stat

Drag files or folders in, or use the paperclip. Paths with spaces are quoted automatically, and a file you already typed as @path is never re-sent as an attachment chip.

Slash commands

Type / for Pi's commands, each showing its argument shape (/fork <entry>) inferred from its description. Editor context is not injected into slash commands — they are directives to Pi, not questions about the open file.

Attachments

Mentions render as pills inside the prompt, where you wrote them — picking a suggestion, dropping a file, or using the paperclip inserts a pill at the caret rather than stacking chips in a row above the input. A pill shows the filename with an icon for its type — the React mark for .tsx/.jsx, a TS or JS badge, CSS, npm for package.json, Docker, images, folders, and so on — carries the full path in its tooltip, and behaves as one character for selection and backspace. Each pill also has a ✕ to remove it with the mouse, which tidies the surrounding spaces as it goes.

The text Pi receives is unchanged: each pill serialises back to @path (quoted when the path has spaces), so drafts, prompt history, and @ search all keep working on plain strings.

Drafts and history

An unsent prompt (text and chips) is saved per chat, so it survives switching chats, hiding the panel, and VS Code tearing the webview down. ↑ walks previous prompts shell-style, most recent first, from an empty box.

You can type while Pi is still launching — the message is queued and sent once it is ready.


Sessions

The header holds new chat, rename, fork, clone, export, and history. History is grouped Today / Yesterday / Previous 7 days / Older, searchable, and each row can be deleted (moved to trash, with confirmation). Pi's direct .jsonl layout and legacy nested session directories are both supported.


Keyboard shortcuts

Most defaults live in the ctrl+alt block, which keymaps rarely touch, and are identical on macOS so a config shared across machines behaves the same. The two exceptions are the model list and thinking levels, which sit on cmd+/ and option+b where they are quickest to reach.

Shortcut Action
ctrl+alt+i Focus the chat input (opens the view first)
cmd+/ (ctrl+/) Open the model list (outside the editor)
alt+b (option+b) Open thinking levels
cmd+ctrl+l (macOS)
ctrl+alt+l (Windows/Linux)
Add the editor selection to Pi
ctrl+alt+n New chat
ctrl+alt+h Chat history
ctrl+alt+. Stop the current run
ctrl+alt+, Settings

cmd+/ is scoped to !editorTextFocus so VS Code's Toggle Line Comment keeps working while you are in a file. Anywhere else — the chat, the sidebar, the panel — it opens the model list.

Every command is contributed to VS Code, so all of it is rebindable: run Pi Coding Agent: Keyboard Shortcuts (or open keybindings.json) and bind whatever you like — including chords, e.g.

{ "key": "ctrl+p m", "command": "piChat.openModelPicker" },
{ "key": "ctrl+p n", "command": "piChat.newSession" }

To take cmd+/ everywhere, including inside a file (commenting then moves to cmd+shift+7):

{ "key": "cmd+/", "command": "-piChat.openModelPicker", "when": "!editorTextFocus" },
{ "key": "cmd+/", "command": "piChat.openModelPicker" }

Inside the chat

ctrl+j / ctrl+k move through every list — slash commands, file mentions, the model picker, thinking levels, and chat history — alongside the arrow keys, matching vim-style keymaps that rebind list navigation globally. With no palette open they step through your prompt history.

Key Behaviour
enter Send
shift+enter Newline
cmd/ctrl+enter Send even with a palette open
↑ / ctrl+k Previous prompt
↓ / ctrl+j Next prompt
tab Accept the highlighted suggestion
esc Close a palette, or stop the run
backspace On an empty input, remove the last file chip

Editor integration

When code is selected, a native Add to Pi action appears immediately above it. Click it—or use the editor context menu, title bar action, or cmd+ctrl+l (ctrl+alt+l on Windows/Linux)—to attach a pill referencing the file, language, and line range to your next message. Press it again over another selection to stack on a second pill; each shows up again in the sent message so you can see exactly what Pi was pointed at.


Commands

Command Notes
Open Reveal the chat
Focus Chat Input Reveal and focus the composer
New Session / Abort / Compact Conversation
Add to Pi Editor selection → prompt
Open Model List / Select Model Inline picker / native quick pick
Open Thinking Levels / Cycle Thinking Level
Cycle Model Forward / Backward
Chat History
Rename / Export / Clone / Fork Current Session
Open Settings / Login to Provider / Logout of Provider
Select Workspace Folder Multi-root workspaces
Refresh Models and Commands / Restart Pi
Keyboard Shortcuts Opens keybindings filtered to piChat.

Select Model opens a native quick pick showing each model's context window, input price, and image support, with a check on the current one.


Settings

Setting Description
piChat.piPath Path to pi (empty = auto: PATH, then ~/.bun/bin/pi)
piChat.extraArgs Extra CLI args after --mode rpc
piChat.streamingBehavior steer or followUp when sending mid-stream
piChat.injectEditorContext Prepend active file/selection after the first prompt
piChat.toolOutputCapKb Cap tool output forwarded to the UI (default 64)

Settings and accounts page

Open Pi Coding Agent: Open Settings, press ctrl+alt+,, or use the gear in the chat header. The page manages:

  • every piChat.* extension setting at user or workspace scope;
  • common Pi defaults, delivery, transport, compaction, retry, display, and telemetry settings;
  • global (~/.pi/agent/settings.json) and project (.pi/settings.json) model scopes;
  • provider login and logout, including API-key, browser OAuth, and device-code flows.

Authentication runs through the installed Pi runtime, so credentials stay in Pi's normal auth store and provider-specific flows remain authoritative. The webview never persists secret prompt values. Sign-out removes credentials stored in Pi; credentials supplied by environment variables or external configuration are identified separately and must be removed at their source.

Scoped models are saved as Pi enabledModels. Pi uses them for model cycling after the RPC process restarts; the normal model picker still allows direct access to all available models.


Development

npm install
npm run typecheck
npm run test:unit       # unit + RPC integration tests
npm run test:e2e        # browser test against the webview preview harness
npm run lint
npm run build
npm run package         # → pi-agent-vscode-<version>.vsix

Press F5 ("Run Pi Coding Agent Extension") for an Extension Development Host, then open the Pi activity-bar icon.

For UI work without an editor host:

npx vite --config webview/vite.config.ts

webview/preview.html mounts the webview against a mock state covering every tool card variant, streaming, todos, and the context meter.

Architecture

Pi SDK (in the extension host) ──events──▶ EventReducer        src/pi/eventReducer.ts
                                                  │ UiPatch[]
                                                  ▼
                                           PiSessionManager       src/pi/sessionManager.ts
                                                  │ batched patches (~16ms)
                                                  ▼
                                          PiChatViewProvider      src/bridge/viewProvider.ts
                                                  │ postMessage
                                                  ▼
                                               React webview       webview/src
  • src/pi/sdkClient.ts dynamically loads the SDK beside the configured Pi executable, creates an in-process AgentSessionRuntime, and bridges extension dialogs to the webview without JSONL RPC.
  • src/pi/eventReducer.ts translates Pi SDK events into UI patches. It keeps one bubble per turn, tracks prose segments so text lands in the right place relative to tool cards, and resolves tool identity when events omit ids.
  • src/bridge/uiState.ts owns the merge policy and is shared by host and webview, so both apply patches identically. Streaming deltas own a prose segment while it streams — providers that send the finished text before replaying it as deltas cannot double the reply.
  • src/bridge/protocol.ts is the single source of truth for both message directions.
  • webview/src renders; it holds no agent logic of its own.

Tests live in test/ and cover the reducer, the bridge, tool display rules, ANSI rendering, composer text rules, and the file index.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft