/local-models
Best used: on any host (Anchor checkout or a scaffolded project) when choosing or wiring a lean local model executor for this machine. Re-run per clone — multi-machine safe. See Skills overview.
Evaluate this machine for cutting-edge lean local models (Qwen3, Gemma 3, Mistral Small, R1 distills, …), recommend what fits, and show clickable install links plus a short procedure for the detected OS (including WSL2).
Why
Spec sheets do not answer “what can this box run?” This skill probes hardware, runs scripts/fit_device.py --probe, and turns the result into an operator-facing report with HTTPS links (official docs, Hugging Face weights, WSL/CUDA/Ollama/llama.cpp).
Usage
| Invocation | Behavior |
|---|---|
/local-models | Probe + fit + install guidance |
/local-models --list | Catalog only |
/local-models --memory 16 --backend cuda | Override probe |
What you get
- Machine profile — guest OS + WSL?; on WSL, host RAM/CPU/GPU via
powershell.exe - Executor placement — prefer Windows bare-metal model server when under WSL
- Reachable API — guest-proven
base_url(loopback → default gateway →resolv.confnameserver); never register dead WSLlocalhostwhen the host API only answers on the gateway IP - Best lean fit (sized to host budget when known) + smaller options
- Links — model quick starts + weight repos
- Install path — host Ollama for Windows / llama.cpp first; in-WSL only as fallback; re-probe after reboot (gateway IPs churn); optional mirrored networking
- Optional draft — after the report, the agent asks only whether to create a plan (yes/no); it always writes under
./.plans/drafts/with an auto slug (local-executor-<model>); install work is under## Prerequisites, not main Steps - Routing — reconfiguration respects the operator’s model-priority list first; lightweight locals only do lightweight work; if the host can run heavy local models, those locals are preferred for heavy inference when they appear appropriately in (or are inserted consistently with) that priority order
WSL + Aider: bare aider needs .env
Aider loads model config from .aider.conf.yml but reads OLLAMA_API_BASE
from a repo-root .env, loaded before Aider's own model-info probe. On
WSL2, if that variable is unset (or points at loopback), a bare aider
invocation fails:
OllamaError: Error getting model info for qwen3:4b. Set Ollama API Base via
`OLLAMA_API_BASE` environment variable. Error: [Errno 111] Connection refused
because the Windows-side Ollama doesn't answer on the WSL guest's own
localhost. scripts/write_aider_env.py reuses this skill's own
reachable-base_url discovery (above) and writes it into a gitignored
project .env (plus a placeholder OPENAI_API_KEY=ollama if none is set —
litellm expects some value even though Ollama ignores it):
python scripts/write_aider_env.py --root <project> # write/refresh .env
python scripts/write_aider_env.py --root <project> --dry-run
Idempotent — re-running after wsl --shutdown or a gateway-IP change updates
only the OLLAMA_API_BASE line, leaving the rest of .env untouched. Also
ensures .gitignore covers .env/.env.* so the machine-local value is
never committed. Never installs or configures Aider itself — only wires the
endpoint an already-configured Aider project needs to reach it.
Confirmed-stack autowire
In a scaffolded project (not the Anchor checkout itself), /local-models
checks for a confirmed stack after the report — reachable and the
API actually serves the Default-for-wire model, not just "responds":
python scripts/local_models_autowire.py --detect # or .anchor/scripts/…
If confirmed, the skill asks "Wire this project?". On yes:
python scripts/local_models_autowire.py --project <project> --write
Writes, all idempotent and machine-local only: a var/endpoints.local.yaml
overlay (Default-for-wire model, never Max fit), the gitignored .env (via
write_aider_env.py, above), an idempotent conventions note (only if the
project already has a conventions file), and — only when aider is on
$PATH — .aider.conf.yml plus a copied portable launcher
(scripts/aider-ollama.sh, from hardware/personal-devices/configs/).
Detection never writes anything; only an explicit "yes" does. Never installs
Ollama or pulls model weights — detected-existing stacks only. Autowire when
already serving is faster than a full declare/converge pass for projects
that just need "use what's already up."
CLI (no agent)
python scripts/fit_device.py --probe
python scripts/fit_device.py --memory 48 --backend metal
python scripts/fit_device.py --list
Install paths
Dual-use: Anchor checkout and scaffolded into projects with
--platform claude and/or grok (same base paths as /work):
| Platform | Path |
|---|---|
| Grok Build | .grok/skills/local-models/SKILL.md |
| Claude Code | .claude/commands/local-models.md |
Queen-bee plans, drone executes
A local model this skill just wired up is a drone, not a planner:
- Queen-bee (a cloud/frontier session, or a human) writes plans into
.plans/drafts/and promotes them to.plans/bugs/or.plans/features/. - Drone (the local model, via
/workin an agentic harness like Aider, or headless viawork_once.py/fleet_watch.py) only claims and executes an already-ready plan — it never drafts, promotes, reviews, or merges.
If the local model's backlog is empty, that's the correct idle state — a
drone does not invent work. See /work and
Fleet workers for the full contract; the
Aider platform page shows the same split for an
interactive local-model harness.
Drone recipe: this machine, unattended
Once /local-models has confirmed a reachable local endpoint (Ollama,
llama.cpp, …), point work_once.py — the same headless picker
Fleet workers documents generically — at a
machine-local registry rather than the shared scripts/endpoints.yaml.
This follows /local-models' own multi-machine hard rule: a laptop's
base_url (an Ollama gateway IP, a localhost port) is host-specific and
must never be committed as project-wide fleet truth — prefer a gitignored
overlay; a shared registry entry is only for LAN/API hosts every clone can
actually reach.
# var/endpoints.local.yaml (gitignored) — one entry, this host's reachable URL
cat > var/endpoints.local.yaml <<'YAML'
endpoints:
- name: laptop-swarm
tier: swarm
base_url: http://<reachable-ollama-url>/v1 # from /local-models' probe
model: qwen3:4b
quirks: {think_toggle: qwen3, strip_think: true, min_completion_tokens: 256}
YAML
# Inspect, then claim one fit-appropriate ready plan (never merges):
python scripts/work_once.py --list --endpoint laptop-swarm --registry var/endpoints.local.yaml
python scripts/work_once.py --once --endpoint laptop-swarm --registry var/endpoints.local.yaml --agent-id laptop-swarm-1
For a reboot-persistent poller instead of a one-shot claim, hand the same
registry/endpoint name to /fleet-watch — the
--worker spec takes a registry= key so the generated systemd unit points
at your machine-local file, not the shared scripts/endpoints.yaml default:
python scripts/fleet_watch.py --project . --emit systemd \
--worker tier=small,agent=laptop-swarm-1,interval=10m,endpoint=laptop-swarm,registry=var/endpoints.local.yaml
See Fleet workers for the full systemd/cron
recipes; this page only adds the machine-local-registry step specific to a
drone running on the box /local-models just probed.