How it works

localexec is a stdio MCP server.

The life of a task

  1. 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_edit have uncommitted changes, the task_id is already in use, the spec or file list is empty, or a path is ignored or outside allowed_paths.
  2. Prompt. It reads the files, adds context files, your conventions and, for large files, only the requested symbols. The size is held to the prompt budget.
  3. Generation. One request goes to Ollama. The reply must be edit blocks, or BLOCKED: followed by a question.
  4. 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.
  5. 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.
  6. Checks. The standards check runs first when enabled, then your checks. A failure is trimmed and sent to the model for the next attempt, up to max_attempts.
  7. 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
>>>>>>> REPLACE

New 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.

FileContents
<id>.jsonThe task manifest: status, attempts, files and a summary.
<id>.patchThe full unified diff.
<id>.logWhat was sent and received, and check output.
<id>.orig/Backups of the files before the task, used by revert_task.
run_checks.logOutput 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.

PackageConcern
server.py, core.pyThe 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.