Workspace Tools
The workspace's own files as tools any agent in the workbench can call — read, list, search, write, edit and open — plus a command left running in a shell of its own.
workspace-tools registers ten tools: seven over the files the reader is looking at, and three over a
command that isn't meant to end. It draws nothing — what it adds shows up wherever tools are offered,
in a conversation's tool picker, in whatever your chat participant calls, and in the browser's own
agent if you also mount webmcp.
import { Workbench, FileSystem } from "codelet/workbench";
import { workspaceTools } from "codelet/extensions/workspace-tools";
import { chat } from "codelet/extensions/chat";
import { piAgent } from "codelet/extensions/pi-agent";
new Workbench({
parent: document.getElementById("app")!,
fs: new FileSystem({ "/index.ts": "export const hello = 1;\n" }),
extensions: [chat, piAgent(), workspaceTools()],
});#The tools
| Name | What it does |
|---|---|
workspace_read | A file, as the reader sees it — unsaved edits included — with numbered lines and a window into a long one. |
workspace_list | Every file and directory, from the root or from a path, to a depth. |
workspace_search | Case-insensitive text across the workspace, narrowed by a glob. The same search the Search view runs. |
workspace_write | A whole file, created or replaced. |
workspace_edit | Exact text replaced in place, refusing anything that names more than one spot unless told otherwise. |
workspace_open | Shows a file to the reader, at a line. Changes nothing. |
workspace_active | What the reader is looking at: the file, the caret, the selection, the open tabs. |
#A command that doesn't end
A shell tool waits for the line it runs — pi's bash does — so a dev server or a watcher would take
the conversation's one shell away for every call after it. These three are the way round that:
| Name | What it does |
|---|---|
bash_background | Runs a command in a shell of its own and answers with an id, plus whatever it printed in a second. |
bash_output | What it has printed since the last read, and whether it's still running. filter is a regex. |
bash_kill | Stops it and closes its tab. |
The command keeps running after the answer that started it, in a terminal tab the reader can watch and stop — a dev server they go on using is worth more than one that dies with the answer. Nothing else ends it: closing the tab does, and so does stopping this extension.
It needs a shell, which is another extension's: mount bash (and
terminal to see the tab). A page with two shells names the one it means:
workspaceTools({ shell: "bash.shell" });#What a write does
A write goes into the same buffer the editor types into, so the change lands in the tab the reader is in — their cursor stays where it was, and the squiggles and decorations move with the text. Then it's saved, so the file holds it.
The one exception is a file that already has unsaved changes in it: the write lands, the tab keeps its dot, and saving is left to the reader. Committing someone's half-finished edit isn't something a tool call asked for.
Note
workspace_write, workspace_edit and bash_background ask before they run. Turning that off is the
chat.tools.autoApprove setting, or the Chat: Toggle Tool Confirmations command — the same
switch pi-agent's own tools read.
#Alongside pi
pi-agent brings its own read, edit, write and bash inside its loop.
Mounting both is fine and common: pi keeps its four, and everything else — another participant, a
.vsix, a browser agent — gets these. Mount this one if you want the workbench's files reachable by
whatever is answering, rather than only by pi.
The three background tools used to be pi-agent's, which is why pi's prompt talks about them: it
writes that section only where they're registered, so a page mounting pi alone tells the model not to
start a server rather than pointing it at a tool that isn't there.
#What it doesn't do
- No delete or rename. Those are gestures the reader has in the tree, and a page has no bin to take a deleted directory out of.
- No waiting on a command. A command that finishes belongs to whatever loop is answering —
pi's
bashdoes that. What's here is the case a waiting tool can't cover, and it needsbashmounted either way.