A reproducible, local-first environment for running coding agents against a shared inference service.
The project separates two responsibilities:
- inference host — runs the model runtime and model weights
- agent client — runs coding-agent harnesses such as Codex, Pi, and Goose
A single machine may have both roles.
- Reproducible setup on macOS and Linux.
- One shared local inference endpoint for multiple machines.
- Interchangeable coding-agent harnesses.
- Declarative model configuration.
- No secrets or personal infrastructure details in Git.
- A single health-check command.
- Reproducible evaluation of model/harness combinations.
- Ability to replace the inference host without rebuilding the client environment.
┌── Codex
├── Pi
Agent client ┼── Goose
└── other harnesses
│
▼
inference endpoint
│
▼
model runtime
│
▼
model
The initial reference environment is:
Mac mini / Apple Silicon
│
└── Ollama
│
└── local coding model
MacBook / Linux laptop
│
└── coding-agent harness
│
└── remote inference endpoint
The architecture intentionally does not depend on the Mac mini.
A future backend may be:
Linux
└── NVIDIA GPU
└── Ollama / vLLM / another OpenAI-compatible runtime
without changing the agent-client workflow.
Responsible for:
- model downloads
- model runtime
- inference API
- runtime health checks
Responsible for:
- Codex
- Pi
- Goose
- agent configuration
- connection to an inference endpoint
Clone the repository:
git clone <https://github.com/akrisanov/local-agent-stack.git>
cd local-agent-stackConfigure a machine as an inference host:
make bootstrap ROLE=inference-host
make doctorConfigure a client:
make bootstrap ROLE=agent-client
make doctorPublic defaults live in .env.example.
Machine-specific configuration lives outside the repository in ~/.config/local-agent-stack/env.
The repository excludes secrets and personal infrastructure details, such as:
- API keys
- OAuth tokens
- Tailscale auth keys
- SSH credentials
- machine-specific private addresses
- work infrastructure endpoints
Desired models are declared in models/models.yaml and managed through the repository tooling.
The current reference model is qwen3.6:35b-mlx, served by Ollama on the inference host.
The project currently supports:
- Codex CLI
- Pi
- Goose
Each harness has repository-managed installation and local-inference launch scripts:
make codex-install
make codex-local
make pi-install
make pi-local
make goose-install
make goose-localQwen Code and OpenCode may be added later for comparison.
scripts/run-plan starts Pi for a plan that is ready for implementation. It uses the
public plan inspect --json contract and does not depend on a checkout of the plans
source repository.
The runner is used directly from this checkout. Install its Python dependency, install
Pi, and install the independent plan CLI using its distribution instructions:
uv sync
make pi-installPrerequisites:
uv,plan, andpiare available throughPATH;PLANS_HOMEpoints to the private plan data directory;REPOS_HOMEpoints to the directory under which repository checkouts are stored;$PLANS_HOME/repos.yamlmaps logical plan repository names to paths relative toREPOS_HOME.
For example, this private configuration:
# $PLANS_HOME/repos.yaml
repositories:
local-agent-stack:
path: local-agent-stack
service-api:
path: work/service-apiresolves local-agent-stack to $REPOS_HOME/local-agent-stack and service-api to
$REPOS_HOME/work/service-api. Keep paths relative: machine-specific absolute paths
must not be placed in plan files or repos.yaml.
Run a new plan in the next state with:
export PLANS_HOME="$HOME/path/to/plan-data"
export REPOS_HOME="$HOME/Projects"
uv run scripts/run-plan <plan-id> --harness piThe same command retries a plan that is already in the open state. A retry starts a
new Pi session from the current open plan; it does not resume the previous Pi session or
perform another lifecycle transition.
To select an explicit provider and model (for example, via OpenRouter), including when retrying an open plan:
uv run scripts/run-plan <plan-id> --harness pi \
--provider openrouter \
--model openai/gpt-5.6-solFor a next plan, the runner completes all preflight checks before transitioning it to
open. The selected next plan must be tracked by the Git repository containing
PLANS_HOME, and its contents must match the version in HEAD; commit the
implementation-ready plan before running it. Unrelated uncommitted planning files do
not block execution, and a local commit is sufficient—no push is required. The runner
then re-inspects the plan and starts Pi in the resolved checkout with the open plan path
and implementation instructions. For an open plan, it uses the existing open plan
path and starts Pi without a transition. Plans in other states are rejected.
The runner never stages, commits, pushes, or marks a plan as done. If Pi fails, its
nonzero status is returned and the plan remains open, so the command can be run again
to retry it.
Run the deterministic runner tests (which use stub plan and pi executables) with:
make testThe Python toolchain uses Ruff for linting and formatting and ty for type checking:
make lint
make format-check
make typecheckThe repository includes a controlled evaluation suite for comparing model/harness combinations.
Run an evaluation with:
make eval HARNESS=pi TASK=bug-diagnosisThe current tasks are:
repo-inspection— inspect the repository, reconstruct its architecture and request paths, and propose concrete improvements without modifying filesbug-diagnosis— inspect a failing codebase, identify the root cause, and explain the minimal fix without modifying filesminimal-fix— diagnose the failure, implement the smallest correct change, and verify it with testsmulti-file-bug— trace a failure across multiple modules, identify the root cause, implement the smallest correct fix, and verify the full test suitefeature-implementation— implement a small cross-file feature, update the relevant tests, and verify the complete test suite
Evaluation fixtures live in evals/fixtures/, task definitions in evals/tasks/,
generated runs in evals/runs/, manual reviews in evals/reviews/, and consolidated results in evals/results/.
Each evaluation produces objective run metadata locally, including wall time, test results, changed files, and diff size. A separate committed review records the subjective verdict, recovery behavior, requirement coverage, final-answer completion, and notes.
Create a review template after a run with:
make review HARNESS=goose TASK=minimal-fixPreview the consolidated report for the current model with:
make reportThe current baseline evaluates qwen3.6:35b-mlx with Codex, Pi, and Goose.
Goose has a complete structured baseline across all five tasks;
Pi and Codex are being migrated to the same evaluation pipeline
so harness comparisons use identical run metadata and review criteria.
© 2026, Andrey Krisanov