Architecture: DevTools CLI
The DevTools CLI is a small, dependency-free TypeScript-style
implementation living in extension/panel.{html,js,css}. It mounts
inside the DevTools panel registered by extension/devtools.js.
For how to use it, see the DevTools CLI page. This page is for contributors curious about how it’s built.
| File | Role |
|---|---|
extension/devtools.html | Tiny shell that loads devtools.js |
extension/devtools.js | Calls chrome.devtools.panels.create('Browy', icon, 'panel.html') |
extension/panel.html | Toolbar + log + autocomplete listbox + textarea |
extension/panel.css | The retro green-on-black aesthetic |
extension/panel.js | All behavior: REPL, slash commands, port glue |
The REPL loop
Section titled “The REPL loop”panel.js keeps a single <div id="log"> of “rows” (banner / dim /
ok / err / model / tool / output). The textarea below is the prompt.
On Enter:
- If the input starts with
/, dispatch to the slash-command registry (COMMANDSarray near line 200 ofpanel.js). - Otherwise, send
{type: 'user', text, sessionId}over the port to the native host, append a row, and streamdeltachunks into a single growing assistant row.
On Esc mid-flight, the panel posts {type: 'cancel'} to the host
and shows a dim “cancelled” row.
Slash-command registry
Section titled “Slash-command registry”Each command is a {name, help, run} triple:
const COMMANDS = [ { name: '/help', help: 'Show available commands', run: cmdHelp }, { name: '/clear', help: 'Clear the scrollback (Ctrl+L)', run: () => clearLog() }, { name: '/model', help: 'Show or pick the active model', run: cmdModel }, { name: '/login', help: 'Sign in to GitHub Copilot', run: cmdLogin },];/? → /help and /models → /model are aliases handled at
dispatch time. New commands plug in by appending one entry.
Autocomplete
Section titled “Autocomplete”The listbox #ac shows matching COMMANDS entries as you type a
slash. Arrow keys move the selection, Tab / Enter accepts.
Implemented as a simple substring match on name plus its first-line
help.
Model picker
Section titled “Model picker”/model with no argument fetches the model list from the host
({type: 'list_models'} → host calls Copilot SDK listModels()) and
renders each as a clickable row. Clicking sends /model <id> for you.
The toolbar’s #model badge updates on model_changed.
History
Section titled “History”Up / down arrows when the input is empty browse a stack of past prompts. Stored in-memory only. DevTools panels are recreated on every page reload, so history is per-session.
Per-target binding
Section titled “Per-target binding”Because each DevTools panel is bound to a single inspected target,
the panel’s sessionId is namespaced with the inspected URL. Open
DevTools on three tabs → three independent agent sessions.
This is intentional: it makes the DevTools CLI a per-page tool, in the same way the inspector is per-page.
Why a separate panel.js?
Section titled “Why a separate panel.js?”We could have rendered the REPL in the side panel UI and called it “DevTools mode”. We didn’t, because:
- DevTools panels live alongside the inspector, network, and console, giving power users context they want next to the agent.
- The DevTools panel UI primitives (toolbar buttons, theming, host
shortcuts like
Ctrl+Lto clear) are subtly different from a regular side panel. Keeping them separate avoidsif (devtools)forks throughout the side-panel code. - It lets the two surfaces evolve independently: the side panel can stay friendly and chat-shaped; the DevTools CLI can grow more power-user features (REPL co-coding, attach to running session) without complicating the side panel.
Coming soon
Section titled “Coming soon”These items are tracked on the GitHub project board:
- REPL co-coding: explicit handoff syntax (
:edit,:continue) for pair-programming with the agent on a single file. - Attach to running session: debug view of an active side-panel agent.