A local CLI for skills you already have, plus a self-hostable registry to
pull more from. Start with the CLI — it needs no account and makes no
network call for import/diff.
This is a literal, self-contained transcript. Paste every command below into a clean shell — it fabricates two fake "skill" installs, edits one and deletes the other to simulate drift, then diffs. You will get exactly this output; nothing here depends on skills you already have installed.
git clone https://github.com/wisechef-ai/loopskill-api && cd loopskill-api
python3 -m venv .venv && ./.venv/bin/pip install ./cli
alias loopskill=./.venv/bin/loopskill # or add ./.venv/bin to PATH
loopskill --version
# --- fabricate a "machine" with two skills, take a snapshot ---
mkdir -p /tmp/loopskill-demo/home/.claude/skills/agent-reach
mkdir -p /tmp/loopskill-demo/home/.claude/skills/recipes
cat > /tmp/loopskill-demo/home/.claude/skills/agent-reach/SKILL.md <<'EOF'
---
name: agent-reach
description: Give your agent eyes on the internet.
---
Body v1.
EOF
cat > /tmp/loopskill-demo/home/.claude/skills/recipes/SKILL.md <<'EOF'
---
name: recipes
description: Recipe search skill.
---
Body.
EOF
loopskill import --home /tmp/loopskill-demo/home -o /tmp/loopskill-demo/machine-a.lock.json
# --- simulate drift: edit one skill, delete the other ---
cat > /tmp/loopskill-demo/home/.claude/skills/agent-reach/SKILL.md <<'EOF'
---
name: agent-reach
description: Give your agent eyes on the internet.
---
Body v2 — updated tool list.
EOF
rm -rf /tmp/loopskill-demo/home/.claude/skills/recipes
loopskill diff /tmp/loopskill-demo/machine-a.lock.json - --home /tmp/loopskill-demo/homeloopskill 0.2.0
loopskill import: wrote /tmp/loopskill-demo/machine-a.lock.json (2 skill(s) across 1 client(s))
loopskill diff: /tmp/loopskill-demo/machine-a.lock.json vs <live scan>
[claude] DRIFT DETECTED
- only in /tmp/loopskill-demo/machine-a.lock.json: recipes
~ changed: agent-reach
[codex] in sync (0 skill(s))
[cursor] in sync (0 skill(s))
[hermes] in sync (0 skill(s))
DRIFT FOUND
Exit code is 1 — drift found, script- and CI-friendly. Run loopskill diff again with nothing changed and exit code is 0. That's the whole
pitch: two snapshots, one command, drift visible in the time it took to
read this paragraph — on the skills you actually have, not a demo, once
you drop --home.
import and diff make zero network calls — this isn't a promise in
a docstring, it's a structural guarantee: the network-capable code lives
in exactly one module (loopskill.pull) that import/diff never
import, and
cli/tests/test_loopskill_cli.py::test_import_and_diff_make_zero_network_calls
proves it by breaking socket.socket for the duration of those commands.
Full CLI reference, lockfile format, and pull/apply (the two commands
that DO touch the network, opt-in): cli/README.md.
Two things, and the CLI is the one to start with:
cli/— a local, offline-by-construction tool for the skills you already have on disk. No account, no server, no LoopSkill dependency forimport/diff. Point it at any registry that serves the same well-known bundle-index shape forpull/apply, or never call those commands at all.app/— a self-hostable FastAPI registry (this repo) that the CLI'spull/applycan optionally talk to, and that also serves a browsable catalog at loopskill.io. The registry is not the reason to start here — the CLI working on your own machine, before you've made an account, is.
The registry also serves 10 vetted loops (scripts/seed_starter_catalog.py).
POST /api/loops/{slug}/run is synchronous and works anywhere. Putting a loop on
a fleet member so it fires on a schedule is a second path with two constraints
worth knowing before you build on it:
- A loop reports nothing unless its own prompt says to. Telemetry exists
only because the loop's prompt calls
scripts/loopskill-emit-run.sh. Nothing else observes a fire — not the scheduler, not the server. Omit that line and the loop runs forever while every dashboard shows zero. This is the reasonloop_runssat at 1 for a year. - Cron materialization is Hermes-only.
app/loop_apply.pywrites the Hermes scheduler's~/.hermes/cron/jobs.json, and nothing else speaks that format yet. On Codex, Claude or OpenCode hostsscripts/install-loop-apply.shrefuses rather than installing a cron that can never converge. The skill path is cross-vendor; the scheduled-loop path is not.
Both are covered end to end in docs/SELF_HOST.md.
git clone https://github.com/wisechef-ai/loopskill-api
cd loopskill-api && docker compose upZero-config: SQLite, auto-generated dev secrets, a seeded starter catalog. Your dev API key is printed on first boot. Full guide, including the Postgres/production path: docs/SELF_HOST.md.
Then run a loop — the runner is live (no LLM needed for verify-mode):
# the zero-config Docker Compose stack always boots with this dev key
# (override via WR_API_KEY in production) — same value the boot banner prints
curl -X POST localhost:8200/api/loops/hello-world-loop/run \
-H "x-api-key: rec_dev_wiserecipes_local_testing_key"
# → {"passed": true, "confinement": "bounded", "duration_seconds": 0.03, ...}A fresh registry that doesn't just list a loop — it executes the loop's success check under enforced bounds and hands you a verdict.
291 app Python files, 69,729 lines of app code, 107 Alembic migrations,
410 test files, 2 GitHub stars, 0 forks (measured 2026-08-11 via gh repo view wisechef-ai/loopskill-api --json stargazerCount,forkCount).
That ratio is real and it isn't a good one. Issue
#68 asked about
it; the honest answer — including why the codebase grew from a working
recipe-search product's battle-tested auth/Stripe/sandbox stack rather
than from a blank registry, and the concrete cuts committed as a result —
is in
docs/decisions/2026-08-11-bundles0811-p4-issue-68-codebase-size.md.
Every number above is checked against a live filesystem measurement by
tests/test_readme_claims.py on every run — it fails the build if this
paragraph drifts from reality the way #68's original numbers did.
| Method | Path | Description |
|---|---|---|
GET |
/api/healthz |
DB health check |
GET |
/api/skills/search |
Full-text + vector hybrid skill search |
GET |
/api/skills/{slug} |
Skill detail + versions |
GET |
/api/bundles/discover |
Browse public bundles |
GET |
/api/loops |
List loops (with their safety contracts) |
GET |
/api/loops/{slug} |
Loop detail — contract, run count, rating |
POST |
/api/loops |
Publish a loop (validates the contract) |
POST |
/api/loops/{slug}/run |
Run the loop's verification → objective pass/fail |
POST |
/api/loops/{slug}/rate |
Rate a loop 1–5 (social-proof signal) |
GET |
/api/personalities |
List deployable personalities |
MCP-native: agents (Claude Code, Cursor, anything speaking MCP) discover and install over the protocol. There's also a signed-URL tarball path for direct fetch.
FastAPI + SQLAlchemy. The same alembic migration chain runs on SQLite
(self-host) and Postgres (hosted) — no create_all drift; the SQLite boot
replays the real migrations, so what you self-host is what production
runs. Full module layout: AGENTS.md.
APIKeyMiddleware.dispatch()
└─ validate_key(db, x-api-key)
└─ request.state.auth_ctx = AuthContext(scope, user_id, tier, …)
└─ REST routes / MCP tools / runner call authz.can_*() predicates
API keys are rec_-prefixed and passed in the x-api-key header. Most
read endpoints (search, detail, discover) are unauthenticated; install,
publish, run, and rate require a key.
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pre-commit install # ruff, bandit, mypy --strict, actionlint, yamllint
pytest -q # fast run
pytest -n auto --cov=app --cov-fail-under=80 # the CI gate
alembic upgrade head # apply migrations before first start (non-SQLite)
uvicorn app.main:app --reload --port 8201Sandbox (Linux only): the kernel sandbox (
app/sandbox/) needs firejail or bubblewrap. Where neither is functional (macOS, hardened containers), the loop runner falls back to bounded mode — POSIX rlimits + scrubbed env + isolated workspace — so loops still run; the response declares whichconfinementlevel it achieved. Multi-tenant fleet owners setWR_LOOP_RUN_REQUIRE_SANDBOX=trueto refuse bounded-mode execution and require a real kernel sandbox.
Contributor guide for AI agents: AGENTS.md.
The whole registry is the OSS product (MPL-2.0). Self-host it anywhere —
docker compose up is the complete experience, not a teaser, and nothing
phones home. The hosted plan is "don't run it yourself," never a feature
gate. Same posture as n8n / PostHog / Supabase.
MPL-2.0 — see LICENSE. The whole registry is open source; we only charge for hosting it.
- CLI (start here): cli/README.md
- Home: loopskill.io
- Self-host guide: docs/SELF_HOST.md
- Issue #68 answer: docs/decisions/2026-08-11-bundles0811-p4-issue-68-codebase-size.md
- Contributing (AI agents): AGENTS.md