How it works
localexec is a stdio MCP server.
The life of a task
- Pre-flight. Before the model is called, the task is refused if a named check does not exist, the project is not a git repository, tracked files outside
files_to_edithave uncommitted changes, thetask_idis already in use, the spec or file list is empty, or a path is ignored or outsideallowed_paths. - Prompt. It reads the files, adds context files, your
conventionsand, for large files, only the requested symbols. The size is held to the prompt budget. - Generation. One request goes to Ollama. The reply must be edit blocks, or
BLOCKED:followed by a question. - Parsing and applying. Blocks are parsed strictly and applied in memory. Every problem found is reported at once, and if any block fails, nothing is written. Parse and match failures are sent back to the model for repair.
- Writing. Files are written atomically, after checking that none changed on disk while the model worked. A failed multi-file write is rolled back, including new directories.
- Checks. The
standardscheck runs first when enabled, then your checks. A failure is trimmed and sent to the model for the next attempt, up tomax_attempts. - Result. The server writes the manifest, patch and log, and returns the compact verdict.
Before touching any file, a task writes a provisional manifest with status: running, so revert_task can undo a run that was interrupted.
The edit format
The model edits files with search-and-replace blocks:
FILE: calc.py
<<<<<<< SEARCH
return a - b
=======
return a + b
>>>>>>> REPLACENew or small files can be written whole:
FILE: shop/slug.py
<<<<<<< FULL
...the complete file...
>>>>>>> END- Edits only apply to files listed in
files_to_edit. - A SEARCH block must match exactly one place. It is matched exactly first, then ignoring trailing whitespace, then ignoring indentation, in which case the replacement is re-indented to the file's style.
- Placeholder lines such as "rest unchanged" are rejected. FULL output that is empty or far shorter than the existing file is rejected, and FULL is refused for files shown only as excerpts.
- Anything else in the reply is a parse error.
What is written to your project
Everything lives in <project>/.localexec/, which gitignores itself.
| File | Contents |
|---|---|
<id>.json | The task manifest: status, attempts, files and a summary. |
<id>.patch | The full unified diff. |
<id>.log | What was sent and received, and check output. |
<id>.orig/ | Backups of the files before the task, used by revert_task. |
run_checks.log | Output of the last run_checks call. |
Source layout
Each package under src/localexec/ groups one kind of problem, with at most ten source files per directory. The tests mirror it.
| Package | Concern |
|---|---|
server.py, core.py | The MCP entry point and the orchestration of tasks. |
settings/ | Config, path safety, task ids and the shared standards defaults. |
fsio/ | Safe text reads and atomic writes. |
editing/ | parse reads edit blocks, match locates text, apply edits in memory and writes atomically. |
tasks/ | The implement-and-retry loop, pre-flight, prompts, the Ollama client and backend status. |
history/ | Manifests, revert_task, list_tasks and show_patch. |
gates/ | The configured checks and the built-in standards check. |
vcs/ | Git queries. |
explore/, symbols/, structure/ | outline and find, symbol ranges and directory limits. |
Dependencies point one way. settings, fsio and vcs are leaves. editing, symbols, gates and explore build on them. history builds on vcs, and tasks builds on everything below it.