Sidebar panel that tracks flutter run (and similar) processes — wherever they're running
from, including ones started by an AI coding agent — and lets you drive Hot Reload / Hot
Restart / Quit and watch live logs without touching the terminal.

Two flutter run sessions tracked side by side — one launched on a Pixel 9 (Android), one on
an iPhone simulator — each independently controllable, with live logs streaming underneath.
The panel picked up both without either being started in a terminal it created: here, Claude
Code (right panel) launched the iOS session as a backgrounded task, and the pid-scan path
found it and started tailing flutter logs automatically.
What it detects
Two independent detection paths, combined into one list:
- Terminal-tracked. Any command run in a VS Code integrated terminal whose command line
matches one of
flutterProcessPanel.matchPatterns(default^flutter run\b), via the built-in Terminal Shell Integration API (onDidStartTerminalShellExecution/onDidEndTerminalShellExecution/onDidCloseTerminal). Works whether you typed the command yourself or an agent did, as long as shell integration is active for that terminal (VS Code default for bash/zsh/fish/pwsh). - Process-scanned (pid-only). Anything with no VS Code terminal at all — e.g. an AI
agent's backgrounded task — fires no VS Code event we can hook. Instead, a
psscan runs on activation, onFlutter Process Manager: Refresh Processes, and every 4s while watching is active (macOS/Linux only), looking for anyflutter_tools.snapshot ... runprocess not already reachable from a tracked terminal. Only the top-level process is picked (a nested child matching the same pattern — e.g. a build/install helper — is skipped, so a signal never lands on the wrong process).
Controlling a process
- Terminal-tracked rows: buttons are driven by
flutterProcessPanel.keymap(default Hot Reloadr/ Hot RestartR/ Quitq), sent viaterminal.sendText. A free-text box sends anything else (e.g.pfor widget inspector,oto switch platform) followed by Enter. - Process-scanned rows: there's no terminal to type into, so Reload/Restart/Quit are
sent as OS signals (
SIGUSR1/SIGUSR2/SIGINT— the mechanism Flutter's own tooling documents for non-interactive control) directly to the process.- Exception: if the command line contains
--machine, the process is already being driven by another tool over its own stdin-based protocol (VS Code's own Dart/Flutter debugger, or any other IDE integration). Sending it an extra raw signal on top of that has crashed the session in testing — two independent controllers racing on the same restart path. For--machinerows only Quit is offered; use the owning tool's own reload/restart controls instead.
- Exception: if the command line contains
Click a terminal-tracked row's header to focus/reveal its terminal (not available for process-scanned rows — there's no terminal to reveal).
Logs
- Terminal-tracked rows: captured live via
TerminalShellExecution.read(). - Process-scanned rows: only for mobile-ish targets (anything except
chrome/edge/macos/windows/linux/web-server, i.e. Android/iOS simulators/devices) — a separateflutter logs -d <device>process is spawned (from the tracked process's resolved cwd) and streamed in. Desktop/web pid-only rows show no logs; there's no equivalent tailer command for those targets. - Both paths cap retention at 1000 lines (matching VS Code's own
terminal.integrated.scrollbackdefault), trimming from the oldest line once exceeded. - Logs render in a read-only Monaco editor (syntax: plaintext, word wrap, VS Code's own find widget for in-log search) — auto-scrolls to the bottom only if you were already there; scroll up to read history and it won't yank you back down.
- Click Show Logs to open a row's log viewer, Hide Logs to close it.
Search
The top search box highlights matches, it doesn't filter — non-matching rows stay visible (dimmed), matching rows get a highlighted border. Searches command text, device, cwd, terminal name, and PID. Hidden automatically when the current tab has nothing to search.
Tabs, pause, and cleanup
- Active Processes / History tabs split running vs. exited entries.
- Refresh Processes (toolbar) re-runs the pid-scan immediately instead of waiting for the next 4s poll.
- Clear Dead Processes (toolbar) drops all exited entries from the list at once.
- Pause Watching / Resume Watching (toolbar, context-sensitive) stops/starts both detection paths — new terminal commands are ignored and the pid-scan poll stops while paused; resuming immediately re-scans.
- Exited processes stay listed (grayed out, with exit code) until dismissed or cleared, or until you switch to the History tab.
Limitation (read this)
- Process-scanned detection needs
psand is macOS/Linux only — not supported on Windows (no POSIX signals either), so pid-only rows simply won't appear there. Terminal-tracked detection is unaffected (shell integration works fine on Windows). - Process-scanned rows have no free-text/arbitrary-keystroke box (signals only cover reload/restart/quit) and no live exit-code (exit is inferred next scan when the PID disappears).
- If a row isn't behaving as expected, check
Flutter Process Manager: Show Log Output— spawn errors, stderr, and exit codes for theflutter logschild processes are logged there.
Configuration
flutterProcessPanel.matchPatterns— array of regexes tested against the full command line typed into a terminal. Default["^flutter run\\b"]. Add more to track other invocations or entirely different tools, e.g."^flutter run.*-d chrome"or"^npm run dev".flutterProcessPanel.keymap— object mapping button label to the keystroke sent (no newline) for terminal-tracked rows. Default:
Edit to add/remove buttons or retarget a different CLI's interactive shortcuts.{ "Hot Reload": "r", "Hot Restart": "R", "Quit": "q" }
Run in dev
npm install
npm run compile
Then press F5 in VS Code (with this folder open) to launch an Extension Development Host
with the panel installed. npm install pulls in monaco-editor, used for the log viewer —
required before the panel will show logs correctly in dev.
Manual test checklist
- Run
flutter runin an integrated terminal → row appears under Active Processes with command text, terminal name, and a "running" status pill. - Click Hot Reload / Hot Restart / Quit → terminal receives the mapped keystroke; Quit exits the process and the row moves to History with an exit code.
- Type something in the free-text box and hit Enter/Send while running → sent followed by a newline.
- Click the row header → its terminal is revealed/focused.
- Click Show Logs → Monaco viewer opens, live-updates, stays pinned to bottom unless you scroll up.
- Search box: type a substring of a running command → matching row highlighted, non-matching rows dimmed (not hidden).
- Start a
flutter runoutside any VS Code terminal (e.g. via an AI agent's backgrounded task) → appears as a pid-only row within ~4s (or immediately via Refresh Processes), with Reload/Restart/Quit via signals. - Same, targeting an Android/iOS device → Show Logs works via
flutter logs. - Same, targeting a desktop/Chrome target → no Show Logs button.
- Clear Dead Processes → all exited rows disappear at once.
- Pause Watching → new
flutter runcommands aren't tracked; Resume Watching picks them up again immediately. - Close a terminal-tracked terminal directly → its row disappears.
- Two simultaneous
flutter runinstances (different terminals or devices) → both tracked as separate rows, act independently.