One repo, four things:
- A Claude Code plugin —
.claude-plugin/plugin.json, seven skills (/dev-loop,/goal,/research,/review,/ship,/triage,/websearch), five agents (orchestrator,lane-workerin an isolated worktree,auditor,triage,researcher), and enforcement hooks. - A portable skill set (Agent Skills open standard) plus thin command shims for Antigravity, Gemini CLI, Codex, Copilot, Cursor and OpenCode — and a skills-only install for agentskills.io-compatible gateways, which expose
/dev-loopfrom the skill itself — installed byskills/dev-loop/scripts/install.sh/.ps1. - An MCP server (
skills/dev-loop/scripts/devloop_mcp.py, registered by.mcp.json) so any MCP-capable host drives the orchestrator:validate_lanes,run_lanes,gate,report,tasks_next,task_set,ledger,scaffold,probe. - Harness-neutral glue in
skills/dev-loop/scripts/:adapters.py(9 lane harnesses, gates, probe, ledger),devloop.sh/DevLoop.ps1(worktree lane orchestrators),artifacts.py(AGENTS.md, GOALS, ROADMAP, MADR ADRs, tasks.jsonl → TASKS.md, DoD, checklists, CHANGELOG, ledger),goal.py,research.py,review.py,ship.py,triage.py,devloop_worker.py(OpenAI-compatible worker),contracts.py(cross-lane interface exchange),verify_harness.py; docs inreferences/, schemas/templates inassets/(agentskills.io layout).
# Claude Code (plugin — recommended)
claude plugin marketplace add /path/to/dev-loop # or a git URL
claude plugin install dev-loop@dev-loop-marketplace # /dev-loop:goal etc., and bare /dev-loop /goal /research /review /ship /triage /websearch while unambiguous
# takes effect at the NEXT session start; in a running session run /reload-plugins
# local dev: claude --plugin-dir /path/to/dev-loop
# Every OTHER harness (Claude Code is covered by the plugin above - do not register it twice), user scope, plus the canonical artifacts
sh skills/dev-loop/scripts/install.sh --harness antigravity,gemini,codex,cursor,copilot,opencode,hermes --user --scaffold
pwsh skills/dev-loop/scripts/install.ps1 -Harness antigravity,gemini,codex,cursor,copilot,opencode,hermes -User -Scaffold
# re-sync one harness after editing the repo (these installs are COPIES and go stale):
sh skills/dev-loop/scripts/install.sh --harness antigravity --userSpark has no install path: it takes a skill only as an uploaded SKILL.md or a .zip with SKILL.md at its
root, plain text only. skills/dev-loop-web/ is the web-only variant of the loop (no shell, no checkout: fetch the
task's contract at a pinned commit, prove bytes by git blob SHA, check both ways, end on the contract's verdict line),
written in the portable Agent Skills subset and holding no project values. Package it, then upload
dist/dev-loop-web.zip on Spark's Skills page:
sh skills/dev-loop/scripts/install.sh --harness gemini-spark # -> dist/dev-loop-web.zip (or --out PATH/dev-loop-web.zip)
python3 skills/dev-loop/scripts/skill_package.py check dist/dev-loop-web.zip # the same gate validate.sh runsThe package is gated before it lands (portable keys only, description ≤ 1024, body < 500 lines, plain-text files
only, no .pyc, name = folder); controls: tests/test_dev_loop_web_package.py. Nothing in it has run on a live
Spark yet; its capability preflight (SKILL.md §2) is what the first run settles.
Required Claude Code settings: "worktree": {"baseRef": "head"}; version ≥ 2.1.219. Run python3 skills/dev-loop/scripts/adapters.py probe after installing or upgrading any harness CLI.
A directory in ~/.claude/skills/ or ~/.gemini/config/skills/ proves nothing: a stale copy looks exactly like a fresh one.
# Claude Code - enabled, and pointing at THIS tree. A directory-source marketplace loads IN PLACE,
# so the repo is the installed copy and cannot go stale; the inventory below is read live from it.
claude plugin list --json | python3 -c 'import json,sys; p=[x for x in json.load(sys.stdin) if x["id"]=="dev-loop@dev-loop-marketplace"]; assert p and p[0]["enabled"], p or "NOT INSTALLED"; print("enabled, version", p[0]["version"])'
python3 -c 'import json,os; s=json.load(open(os.path.expanduser("~/.claude/settings.json"))); print(s["extraKnownMarketplaces"]["dev-loop-marketplace"]["source"]["path"])' # must print this repo
claude plugin details dev-loop@dev-loop-marketplace # must read: Skills (7), Agents (5), Hooks (7), MCP servers (1)
# Antigravity - the install IS a copy, so rebuild a reference copy with the same installer and diff.
# (SKILL.md is frontmatter-stripped on the way in, so a plain `diff -r` against skills/ can never pass.)
REF=$(mktemp -d); HOME="$REF" sh skills/dev-loop/scripts/install.sh --harness antigravity --user >/dev/null 2>&1
diff -r -x __pycache__ "$REF/.gemini/config/skills" "$HOME/.gemini/config/skills" \
&& diff -r "$REF/.gemini/config/workflows" "$HOME/.gemini/config/workflows" \
&& echo "AGY install CURRENT"; rm -rf "$REF"sh skills/dev-loop/scripts/validate.sh — agentskills.io validator on every skill (sub-skills as the six-key copies other harnesses receive), plugin/hook/agent shapes, schemas, syntax, and claude plugin validate --strict when the CLI is present. Details and sources: skills/dev-loop/references/conformance.md.
/dev-loop:init— first command in a fresh environment (a rebuilt cloud container, a new devcontainer, a bare Fedora box): finds or clones the four MiOS repos side by side, installs the MiOS package set (dnfon Fedora; the identical Fedora userspace projected from MiOS's devcontainer anywhere else, podman first), provisionsagy, fetches the Global MiOS System Prompt (usr/share/mios/ai/system.md) and adopts it, then orients on the MiOS ledger and proposes the next task. Wrapsscripts/env/mios-init.sh(--plan,--no-packages,--prompt-only); idempotent./dev-loop <objective>— one task through the loop./dev-loop lanes:<lanes.json>— parallel lanes in any mix of harnesses (schema:assets/lane-schema.json, example:assets/lanes.example.json)./goal dev <objective>— define stopping conditions, decompose into tasks, iterate untilgoal.py evalpasses./research …→/dev-loop …→/review→/ship <branch>;/triage <failing cmd>before touching code.
Any harness can be the host; any harness can run a lane (references/harness-adapters.md). State lives on disk (.devloop/), never in the context window (references/artifacts.md).
The environment layer ships inside the skill (skills/dev-loop/scripts/env/, docs in
references/environment.md), so every dev-loop install carries it. It detects the host's
package manager (dnf5/dnf/microdnf, else apt-get) rather than assuming one — idempotent, and
location-independent.
Devcontainer: .devcontainer/Containerfile is a byte-identical mirror of MiOS's one
development image (Fedora 44; tests/test_devcontainer_mirror.py gates it), built from this
repo's root, and devcontainer.json runs MiOS's lifecycle, so it is identical to every other
MiOS dev environment. It bakes
agy, the keyring stack and Node + Claude Code for claude-code lanes, and re-arms the keyring
on every start — after a container restart no new login is needed.
A hosted cloud session runs on a fixed Ubuntu VM that cannot be replaced; MiOS's Fedora dev image runs inside it and every session starts already provisioned. Create the environment once (claude.ai/code, the cloud icon above the message box, Add cloud environment), paste the script below into Setup script, add the variables, and tick it as the default. The platform runs the script, snapshots the disk, and every later session starts from that snapshot.
Setup script. It clones the dev-loop plugin to /opt/dev-loop and hands off to its cloud-fedora-setup.sh. All the logic lives in that clone, so this text never needs editing, and it always exits 0 because a failing setup script fails every session:
#!/bin/bash
# MiOS Fedora dev environment + the dev-loop plugin (/dev-loop:*) in every session.
export FEDORA_DEVCONTAINER_REPO=https://github.com/mios-dev/MiOS
export FEDORA_DEVCONTAINER_FILE=.devcontainer/Containerfile
# dev-loop plugin checkout; loaded by CLAUDE_CODE_PLUGIN_DIRS=/opt/dev-loop
if [ -d /opt/dev-loop/.git ]; then
git -C /opt/dev-loop pull -q --ff-only || true
else
git clone -q --depth 1 https://github.com/mios-dev/-dev-loop /opt/dev-loop || true
fi
[ -f /opt/dev-loop/skills/dev-loop/scripts/env/cloud-fedora-setup.sh ] &&
bash /opt/dev-loop/skills/dev-loop/scripts/env/cloud-fedora-setup.sh
exit 0Environment variables, set in the same dialog:
| Variable | Value | What it does |
|---|---|---|
FEDORA_DEVCONTAINER_REPO |
https://github.com/mios-dev/MiOS |
Projection mode: the session's Fedora userspace is this repo's devcontainer, built unedited, so it is the one MiOS dev image. Unset, the script builds a generic Fedora image instead. |
FEDORA_DEVCONTAINER_FILE |
.devcontainer/Containerfile |
The Containerfile inside that repo. This is the default; setting it keeps the environment a complete record. |
CLAUDE_CODE_PLUGIN_DIRS |
/opt/dev-loop |
Loads the dev-loop plugin in every session, whatever repo it opens: the /dev-loop:* commands, hooks and agents. A cloud session loads plugins no other way. |
Optional, same place:
| Variable | Default | What it does |
|---|---|---|
FEDORA_RUNTIME |
auto |
auto, podman or docker. MiOS is Podman-native: auto takes podman when it is installed and Docker only where it is the sole runtime, which is this cloud VM. An explicit runtime that is missing logs an error and builds nothing. |
FEDORA_SETUP_BUDGET_S |
0 (never) |
Defers the devcontainer lifecycle prebuild (miosd, the root overlay, /opt/mios/bin) once this many seconds of the setup have elapsed, so a slow run stays inside the platform's roughly 5-minute snapshot budget. A deferred prebuild is applied later, inside a session, with bash /opt/dev-loop-fedora/cloud-fedora-setup.sh --lifecycle. A full run measured 452 s; start with 240 if the environment cache stops building. |
FEDORA_DEVCONTAINER_REF |
the repo's default branch | Branch or tag of the repo to clone. |
FEDORA_EXEC_USER |
root |
The user container commands run as. mios-dev matches the devcontainer's remoteUser. |
FEDORA_PROVISION_HOST |
1 |
0 skips provisioning the VM itself (agy, keyring, headless grants, dev-loop skill), which otherwise runs first. |
FEDORA_REBUILD |
0 |
1 forces an image rebuild even when one is cached. |
What every session then has: mios-dev <cmd> and fedora <cmd> run inside the Fedora image at the same path as on the host; agy, claude, gemini and copilot on the host and in the image; the /dev-loop:* commands. Sign in to agy once per container: bash /opt/dev-loop/skills/dev-loop/scripts/env/agy-login.sh prints the URL, then the same command with --code '<code>' finishes. The canonical text, the measurements behind every default and the --wrapper-only path for a dialog with no Setup script field are in skills/dev-loop/references/environment.md. The repo's own SessionStart hook (.claude/hooks/session-start.sh) is a fallback for a session that has no environment configured: it installs the wrapper only, and the image builds on first use.
First-run login — two commands, from any of these environments (Antigravity has no non-interactive auth; the driver walks agy's entire first-run TUI and proves the result with a live headless probe):
bash skills/dev-loop/scripts/env/agy-login.sh # 1. prints the Google auth URL
bash skills/dev-loop/scripts/env/agy-login.sh --code '<code>' # 2. finishes onboarding + verifiesDefaults chosen for you (flags to override): Google's "share Interactions data" checkbox OFF
(--telemetry to opt in — that consent belongs to the human), workspace trust YES
(--no-trust). Already signed in? Either command detects it and skips straight to the
verification probe. Containers are ephemeral: a brand-new container needs the login once. The
keyring is empty-password (credential recoverable by anyone with container access) — acceptable
for a single-user ephemeral session only.
AGENTS.md (the constitution — law in every harness) makes Antigravity the L0 manager of all
dev-loop sub-agents in this repo. It dispatches any mix of lanes, including multiple
concurrent Antigravity lanes (separate agy -p processes) and multiple concurrent Claude
Code lanes (claude -p), each in its own git worktree with its own two-sided gate:
python3 skills/dev-loop/scripts/adapters.py probe # flags drift monthly — check first
sh skills/dev-loop/scripts/agy_host.sh my-lanes.json # interactive manager
sh skills/dev-loop/scripts/agy_host.sh my-lanes.json --headless # unattended manager (JSON out)The manager is native-first: Antigravity's own multi-agent machinery (subagents via
invoke_subagent with workspace: branch, native workflows) runs its own lanes by default;
the reference orchestrator is how Claude Code and other harnesses join the loop. Harness-native
loop commands inside lanes are allowed and never replace the gates.
Model defaults (operator policy, 2026-09): Claude Code lanes run --model opus --effort xhigh
(the alias tracks the newest Opus release; the manager assigns lower tiers — sonnet/haiku,
lower effort — to light lanes); Antigravity lanes default to gemini-3.8-flash-high; the
headless manager runs gemini-3.1-pro-high (AGY_HOST_MODEL / AGY_HOST_EFFORT override;
per-lane worker.model / worker.effort win). Verified live: the full AGY-managed mixed-lane
e2e (tests/e2e-mixed-lanes/) passes end to end under scoped agy permission rules. Upstream
pattern survey, with patterns copied and credited at pinned commits (nothing vendored):
skills/dev-loop/references/upstream-patterns.md.
Mixed-lane example: skills/dev-loop/assets/lanes.agy-manager.example.json (2 AGY lanes +
2 Claude Code lanes + an AGY auditor). The manager runs every merge gate itself; lanes never
commit, and a lane whose negative control passes is never merged.
/review now runs the full SCOPE oversight model (Staged Code Oversight with Proportional
Escalation, after Greiler's staged-review work): Stage 1 agent review (severity × dimension
findings; secret scan covers keyword assignments AND bare token formats — AKIA/ghp_/sk-/AIza/
xox), Stage 2 steering-developer ownership with Agent-Dev Loop sizing (≤ 600 lines / ≤ 20
files), Stage 3 proportional peer escalation (Understanding Need × Change Risk × Established
Assurance → escalation tier), recorded in a Living Oversight Record
(OVERSIGHT_RECORD.md + .devloop/scope_review_*.json). The goal/ship/research/triage engines
took the same lineage's refinements, and scripts/git_lock.py plus a stale-index.lock
sweep in the adapters' git retry protect multi-lane git contention.