Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .skills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Repo Agent Skills

Skills under `.skills/` are auto-picked-up by agents working in this repo.
Each is a focused, current how-to; deeper reference lives in `docs/`.

| Skill | Use when |
|---|---|
| [deploy-macos](deploy-macos/SKILL.md) | Install/launch mesh-llm on a macOS node (release install or dev-build bundle, codesign/quarantine, verify serving) |
| [deploy-linux-gpu](deploy-linux-gpu/SKILL.md) | Install/launch mesh-llm on a remote Linux GPU node (Vast.ai/RunPod/self-managed CUDA, supervisor/systemd, verify serving) |
| [deploy-windows](deploy-windows/SKILL.md) | Install/launch mesh-llm on Windows (install.ps1 via `irm \| iex`, flavor selection CUDA/ROCm/Vulkan/CPU, contrib helper scripts, PowerShell gotchas) |
| [mesh-join](mesh-join/SKILL.md) | Create/join/publish meshes: invite tokens, `--auto`, named meshes, client-only nodes, NAT/bind issues, multi-node verification |
| [connect-agents](connect-agents/SKILL.md) | Point Goose/Claude Code/OpenCode/Pi or any OpenAI client at a running mesh; tool-call validation; blackboard |

Ground rules baked into all of these:

- The bundle/release is a **single `mesh-llm` binary** with the embedded staged
runtime. No `rpc-server`, no `llama-server`, no `.dylib` set.
- mesh-llm downloads models itself — pass `--model <ref>`, never pre-download.
- `--headless` only hides the web UI; it is not a backgrounding mechanism.
- Prefer `mesh-llm stop` over `pkill`.

Related docs: `docs/USAGE.md` (install/service/storage), `docs/CLI.md`
(commands and model refs), `docs/MESHES.md` (mesh workflows),
`docs/AGENTS.md` (agent clients), `docs/SKIPPY_SPLITS.md` (big-model splits).

Maintainer-facing skills (skippy internals, patch queues, benchmarks, lab) live
in `.agents/skills/`; plugin-shipped skills install via `mesh-llm skills install`.
97 changes: 97 additions & 0 deletions .skills/connect-agents/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
name: connect-agents
description: Use this skill when connecting agent tools or OpenAI clients to mesh-llm — launching or configuring Goose, Claude Code, OpenCode, Pi, curl, or any OpenAI-compatible client against a local or remote mesh, picking a model, or validating tool-call reliability.
metadata:
short-description: Connect agents and OpenAI clients to mesh-llm
---

# connect-agents

Use this when pointing an agent harness or any OpenAI client at a running
mesh-llm node. Full reference: `docs/AGENTS.md`.

## Mental model

- Every node serves an OpenAI-compatible API at `http://<host>:9337/v1`.
- `GET /v1/models` lists everything reachable (local + mesh peers); requests
route by the `model` field.
- Special model ids: `auto` lets the mesh pick; `mesh` engages the
mixture-of-agents path. Otherwise use an exact id from `/v1/models`.
- For coding agents, pick a tool-capable model. If `--model` is omitted, the
built-in launchers pick the strongest tool-capable model available.

## Built-in launchers (preferred)

mesh-llm launches the major agent CLIs with config injected for you:

```bash
mesh-llm goose [--model <id>] # writes ~/.config/goose/custom_providers/mesh.json
mesh-llm claude [--model <id>]
mesh-llm opencode [--model <id>] [--host <h>] # injects OPENCODE_CONFIG_CONTENT (no file edits)
mesh-llm pi [--model <id>] [--host <h>] # writes ~/.pi/agent/models.json
```

- `goose`/`claude` reuse a local mesh on the chosen `--port`.
- `opencode`/`pi` target `--host` (default `127.0.0.1:9337`) and auto-start a
local client only for loopback targets; the auto-started node is cleaned up
when the harness exits.
- `mesh-llm pi --write` / `mesh-llm opencode --write` update config without
launching (use `--host` for remote meshes).
- Agent launch commands also install available plugin skills for that agent
(`mesh-llm skills install` does it standalone).

## Manual config (any OpenAI client)

Base URL `http://<host>:9337/v1`, any non-empty API key:

```bash
export GOOSE_PROVIDER=openai GOOSE_MODEL="<id-from-v1-models>"
export OPENAI_HOST="http://127.0.0.1:9337" OPENAI_API_KEY="mesh"
```

```bash
curl -s http://localhost:9337/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"auto","messages":[{"role":"user","content":"hello"}]}'
```

Exact manual provider JSON for OpenCode and Pi is in `docs/AGENTS.md`.

## Validating agent behavior

Direct API contract probe (tool-call forcing, streaming reconstruction):

```bash
scripts/qa-agent-tool-call-reliability.py \
--base-url http://127.0.0.1:9337/v1 --models auto,mesh --attempts 3 \
--output target/agent-tool-call-reliability/results.jsonl
```

Broader harness (models, chat, streaming, plus optional Goose/OpenCode/Pi
smokes): `scripts/qa-nightly-stability.py` — see `docs/AGENTS.md`. Use
`--print-plan` on either script for a side-effect-free preview.

## Blackboard (cross-mesh agent coordination)

Agents can share status/questions across the mesh via the blackboard plugin —
even from a client-only node:

```bash
mesh-llm plugins install blackboard
mesh-llm blackboard "STATUS: [org/repo branch:main] refactoring billing module"
mesh-llm blackboard --search "QUESTION"
```

MCP access: the management endpoint `http://127.0.0.1:3131/mcp` exposes
`blackboard_post`, `blackboard_search`, `blackboard_feed`. Posts are visible to
every peer — never post secrets, credentials, private paths, or customer data.

## Gotchas

- Use a base URL ending in `/v1`; prefer chat-completions over the Responses
API unless the client documents Responses support.
- Model ids must match `/v1/models` exactly (they can contain spaces — quote
them).
- An empty `/v1/models` usually means the model is still loading or no mesh was
joined yet — check `/api/status` on `:3131` (see `mesh-join`).
- The response `"model"` field tells you which node/model actually answered.
23 changes: 17 additions & 6 deletions .skills/deploy-linux-gpu/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: deploy-linux-gpu
description: Use this skill when deploying, installing, launching, or serving mesh-llm on a remote Linux GPU node (rented GPUs like Vast.ai or RunPod, or a self-managed CUDA server), including installing the CUDA build, choosing a model and running
description: Use this skill when deploying, installing, launching, or serving mesh-llm on a remote Linux GPU node (rented GPUs like Vast.ai or RunPod, or a self-managed CUDA server), including installing the CUDA build, choosing a model, keeping it alive under a supervisor, and verifying it serves.
metadata:
short-description: Deploy mesh-llm on a remote Linux GPU node
---
Expand All @@ -11,12 +11,19 @@ Use this when standing up mesh-llm on a remote Linux GPU box (rented GPU like
Vast.ai / RunPod, or your own server) to serve a specific model and join the
mesh.

This is the Linux/CUDA counterpart to the macOS `deploy` skill. It does NOT use
This is the Linux/CUDA counterpart to the `deploy-macos` skill. It does NOT use
the old `llama-server`/`rpc-server` lane — the current binary embeds the staged
runtime. There are no `.dylib`/`codesign`/quarantine steps on Linux.

note --auto flag tells it to join the public mesh. serve command with --model tells it to run a specific model.
Example here are for solo serving, don't read this in isolation without other docs and skills.
Examples here are for solo serving — don't read this in isolation:

- `deploy-macos` / `deploy-windows` — other platforms
- `mesh-join` — creating/joining private and public meshes (tokens, NAT, multi-node)
- `connect-agents` — pointing Goose/Claude Code/OpenCode/Pi at a running mesh
- `docs/USAGE.md` — install details, service mode, model storage
- `docs/CLI.md` — full command and model-ref reference
- `docs/SKIPPY_SPLITS.md` — splitting big models across nodes

## The one rule that matters most

Expand Down Expand Up @@ -109,9 +116,13 @@ supervisorctl reread && supervisorctl update && supervisorctl start mesh-llm
supervisorctl status mesh-llm
```

If there is no supervisor, run it under `systemd --user`, `tmux new -d`, or as a
foreground process in a held SSH session for first-run debugging (allocate a TTY
with `ssh -tt host 'bash -lc "..."'`).
If there is no supervisor, the installer can set up a `systemd --user` service
for you (`curl -fsSL .../install.sh | sh -s -- --service` installs
`~/.config/systemd/user/mesh-llm.service`; startup models go in
`~/.mesh-llm/config.toml`, and `sudo loginctl enable-linger "$USER"` makes it
survive reboot before login). Otherwise use `tmux new -d`, or a foreground
process in a held SSH session for first-run debugging (allocate a TTY with
`ssh -tt host 'bash -lc "..."'`).

## Verify it's actually serving

Expand Down
164 changes: 164 additions & 0 deletions .skills/deploy-macos/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
name: deploy-macos
description: Use this skill when deploying, installing, launching, or serving mesh-llm on a macOS machine (local or remote over SSH), including installing a release, shipping a dev build bundle, codesign/quarantine fixes, choosing a model, and verifying it serves.
metadata:
short-description: Deploy mesh-llm on a macOS node
---

# deploy-macos

Use this when standing up mesh-llm on a macOS machine — either installing a
release or shipping a locally built dev binary to a remote Mac for testing.

This is the macOS counterpart to `deploy-linux-gpu`. The current binary embeds
the staged llama.cpp runtime: the bundle is a **single `mesh-llm` binary**.
There is no `rpc-server`, no `llama-server`, and no `.dylib` set anymore — if
you see instructions mentioning those, they are outdated.

Related skills/docs:

- `deploy-linux-gpu` — remote Linux/CUDA nodes
- `deploy-windows` — Windows nodes
- `mesh-join` — creating/joining private and public meshes (tokens, NAT, multi-node)
- `connect-agents` — pointing Goose/Claude Code/OpenCode/Pi at a running mesh
- `docs/USAGE.md` — install details, service mode, model storage
- `docs/CLI.md` — full command and model-ref reference

## The one rule that matters most

**mesh-llm resolves and downloads the model itself.** Pass `--model <ref>` and
it fetches the GGUF into the standard Hugging Face cache on first use. Do NOT
pre-download with `hf`/`huggingface-cli`, do NOT scp GGUFs around. (Only
`--gguf` takes a local file path you manage yourself.)

## Install path A: official release (most cases)

```bash
curl -fsSL https://raw.githubusercontent.com/Mesh-LLM/mesh-llm/main/install.sh | bash
```

The binary lands at `~/.local/bin/mesh-llm` (may not be on a non-interactive
SSH `PATH` — use the full path or `bash -lc`). Metal is the macOS backend; the
installer picks it automatically.

To install as a per-user background service (launchd agent) in the same step:

```bash
curl -fsSL https://raw.githubusercontent.com/Mesh-LLM/mesh-llm/main/install.sh | bash -s -- --service
```

Service files: `~/Library/LaunchAgents/com.mesh-llm.mesh-llm.plist`, shared env
in `~/.config/mesh-llm/service.env`, startup models in `~/.mesh-llm/config.toml`.

## Install path B: dev build to a remote Mac

Build and bundle locally (from the repo):

```bash
just release-build # serious testing must use the release binary
just bundle # /tmp/mesh-llm-bundle.tar.gz (single mesh-llm binary)
```

Ship and unpack:

```bash
scp -P <SSH_PORT> /tmp/mesh-llm-bundle.tar.gz user@host:
ssh -p <SSH_PORT> user@host 'mkdir -p ~/bin && tar xzf mesh-llm-bundle.tar.gz -C ~/bin --strip-components=1'
```

### Fix macOS quarantine — ALWAYS after scp

Files transferred via scp get provenance/quarantine xattrs that make macOS
SIGKILL the binary on launch (exit 137). After every scp:

```bash
codesign -s - ~/bin/mesh-llm
xattr -cr ~/bin/
```

Verify: `xattr ~/bin/mesh-llm` should print nothing. Note codesign changes the
file hash — don't compare local vs remote hashes after signing.

Verify the version on the remote matches what you built:

```bash
~/bin/mesh-llm --version
```

## Launch

Serve a model and join the public mesh:

```bash
mesh-llm serve --model unsloth/Qwen3.6-27B-GGUF:UD-Q4_K_XL --auto
```

- `--auto` discovers and joins the community mesh; local serving and mesh
joining happen together.
- Without `--auto` (and without `--join`/`--discover`) you create a private
mesh and an invite token is emitted — see the `mesh-join` skill.
- `--model` accepts catalog names, `repo:QUANT`, `repo/file.gguf`, or a full HF
URL. `--gguf /path/file.gguf` serves a local file directly.
- API on `:9337`, management console on `:3131` (override with `--port` /
`--console`).

Notes / gotchas:

- **Do NOT use `--headless` to "go quiet"** — it only disables the embedded web
UI and does nothing for backgrounding. For machine-readable output use
`--log-format json`.
- **Model load takes time.** Poll `/v1/models` until your model appears before
concluding anything is broken.
- For background test runs from an agent:
`bash -c 'nohup mesh-llm serve --model <ref> --auto > /tmp/mesh.log 2>&1 & disown'`.
For persistence across reboots, prefer the `--service` install.

## Verify it's actually serving

```bash
# Ports bound
lsof -nP -iTCP:9337 -iTCP:3131 -sTCP:LISTEN

# Models (union of local + mesh peers)
curl -s http://localhost:9337/v1/models | python3 -m json.tool

# Status / peers
curl -s http://localhost:3131/api/status | python3 -m json.tool

# Inference — the returned "model" field tells you which node/model answered
curl -s http://localhost:9337/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"auto","messages":[{"role":"user","content":"hi"}],"max_tokens":16}'
```

To force your local model specifically, pass its exact id from `/v1/models`
instead of `auto`.

## Logs and state

- `~/.mesh-llm/runtime/<pid>/logs/skippy-native.log` — embedded llama.cpp/skippy
native logs. Check here first if a model fails to load.
- `~/.mesh-llm/key` — persistent node identity.
- `~/.mesh-llm/config.toml` — startup models and defaults for bare `mesh-llm serve`.
- HF cache (`~/.cache/huggingface/...`) — downloaded GGUFs; you generally never
need to touch this.

## Stop / clean up

```bash
mesh-llm stop # scoped stop of tracked instances (preferred)
# emergency only:
pkill -9 -f mesh-llm
```

A clean stop removes the instance runtime dir under `~/.mesh-llm/runtime/`.

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| Exit 137 immediately after scp | macOS quarantine/provenance xattr | `codesign -s - <bin>; xattr -cr <dir>` |
| `mesh-llm: command not found` over SSH | `~/.local/bin` not on non-interactive PATH | Full path or `bash -lc` |
| Empty `/v1/models` | Model still downloading/loading | Wait; watch skippy-native.log |
| "No inference server available" | Election in progress or load failed | Check stderr + skippy-native.log |
| Stale runtime dir after crash | Unclean exit | `rm -rf ~/.mesh-llm/runtime/<stale_pid>/` (auto-GC'd after 1h too) |
Loading
Loading