Normally, a model uses tools one call at a time: call a tool, wait for the result, decide what to do next, call another. That’s fine for a single lookup, but slow and wasteful when a task needs ten reads or a hundred API calls.
Codemode is a different approach. Instead of calling tools one by one, the model writes a short JavaScript script that calls them, and only the script’s output goes back to the model.
What a script looks like
Here’s the kind of script a model might write to read every lesson file at once and report which is longest:
const ls = await tools.bash({ command: "ls src/content/lessons" });
const files = ls.output.split("\n").filter(Boolean);
const counts = await Promise.all(
files.map(async (f) => {
const text = await tools.read({ path: `src/content/lessons/${f}` });
return [f, text.split(/\s+/).length];
}),
);
return counts.sort((a, b) => b[1] - a[1]).slice(0, 3);
Every Pi tool is available as tools.<name>(). Scripts run in a sandbox with no file system or network access of their own, so they can only do what Pi’s tools can do. The calls are real, though: they show up in the transcript like any other tool call.
Why it helps
- Fewer round trips. Ten tool calls in one script is one step for the model, not ten.
- Parallel work.
Promise.allruns the calls at the same time. - Less context. A script can filter a huge result down to the few lines that matter before the model ever sees it.
You’ve already used codemode. In the MCP lesson, the weather lookup went through a codemode script: it called searchTools() to find the right weather tool, then called it. That’s the default way MCP tools reach the model.
Turn it on for every session
Pi turns codemode on automatically when an MCP server needs it. To keep it on all the time, add it to defaultTools in ~/.pi/agent/settings.json:
{
"defaultTools": ["+codemode"]
}
The + adds codemode and keeps read, bash, edit, and write. Run /reload to pick it up.
For a single run, --tools replaces the whole selection, so list every tool:
pi --tools read,bash,edit,write,codemode
Try it
After /reload, start Pi in any folder with a handful of files and ask:
Use one codemode script to read every file in this folder in parallel and tell me which one is longest.
Look for a single codemode call in the transcript, with the individual reads nested inside it.
More than tool calls
Scripts have a few extras worth knowing about:
store(key, value)andload(key)keep small values, like IDs or a cursor, between codemode calls in the same session.models.generateImages()generates images with an image model your providers give you access to, such as one on OpenRouter.models.classify()answers typed questions (pick a label, give a score, yes or no) with fast classifier models.
For the full script API and limits, see Pi’s codemode documentation.