Skip to main content

One-time setup

muyan-pilot setup is the one-time, config-driven initialization entry for a new machine or a new task-pool repository. It verifies or reinstalls the editable CLI install, verifies the local prerequisites, aligns the platform labels, installs the systemd user units, checks the checkout, and reports the optional model proxy — in one command, with a stable machine-readable result.
Run it once per machine (and once per new task-pool repository). It is idempotent: re-running it never creates duplicate labels, units or timers, and never touches business Issues or business labels.

What it does, in order

Setup is fail-fast: a core prerequisite failure stops the run before any later mutation, with the concrete reason on stderr and a non-zero exit code.
  1. Commandsgit, gh, python3, uv and the installed muyan-pilot CLI on the PATH (you install these yourself — setup only checks them; a missing one fails fast with the actionable install guidance, e.g. required command missing: uv (not on PATH) — install uv first: curl -LsSf https://astral.sh/uv/install.sh | sh), and a reachable systemctl --user user bus (a container or headless session without a user bus fails with the bus error).
  2. CLI editable install (Issue #152) — the official local deployment is the EDITABLE uv tool install: the tool env imports muyan_pilot from the deployment checkout, so the ExecStartPre checkout sync is picked up by the next CLI process automatically. When the running process already imports from the configured repo_dir the step verifies it (no uv call, cli=verified); otherwise it runs the exact force editable reinstall (uv tool install --force --reinstall --editable --python /usr/bin/python3 <repo_dir>, cli=installed). A failing install fails fast — no unit install, no half-initialized state. The step never touches a running Runner process (the new source is loaded by the next CLI start).
  3. Authgh auth status (logged in with a usable token).
  4. Per target repository (every configured source_repos entry by default; exactly one with --repo OWNER/REPO):
    • the repo exists and the viewer has write permission (gh repo viewviewerPermission must be WRITE, MAINTAIN or ADMIN);
    • the eight platform labels are aligned declaratively from the repo-managed labels.toml (repo root) — the single source of truth for label name, color and description: a missing label is created, a drifted label is updated, nothing is deleted, and business labels (bug, enhancement, …) are never touched.
  5. Systemd units — the repo templates (systemd/[email protected], systemd/[email protected]) are installed idempotently into the user unit directory (the same install install-units performs: copy, migrate the pre-#149 non-templated units away once, daemon-reload, enable the two timer instances [email protected] / [email protected] — the service is never started, stopped or restarted), then each timer instance’s enabled/active state and next trigger time are reported.
  6. Checkout + git transport — check of the configured repo_dir: the origin remote’s transport (Issue #114): git data operations (fetch, push — including .github/workflows/*.yml) must go over SSH ([email protected]:owner/repo.git), so an existing HTTPS origin is migrated here with the plain git remote set-url origin [email protected]:owner/repo.git (setup is the human-authorized migration path — the Runner itself never rewrites a remote), the SSH URL must match the first configured source repo — a remote pointing at a DIFFERENT repo is never migrated (the rewrite would re-target the checkout at another repository) and fails with setup_failed reason=... origin remote repo mismatch ... — and git ls-remote <ssh-url> must exit 0 (SSH reachable and authenticated — a failure is setup_failed reason=... ssh_unreachable ..., no HTTPS fallback). Then the read-only parts: current branch, clean worktree (a dirty checkout fails: the timer’s ExecStartPre fast-forward refuses a dirty worktree, so the Runner could never start), and base freshness (local HEAD vs freshly fetched origin/<base_branch> — reported, not a failure: the timer fast-forwards a clean checkout at each start).
  7. Optional model proxy — the local-llm-kv-cache proxy health endpoint (http://127.0.0.1:18082/health) is checked and reported as optional: its absence or unhealthiness is a warning in the output and never blocks the core GitHub/Pilot setup.
Setup never creates business Issues, never claims a task, never starts Pi, and never modifies a protected branch.

Options

Output

The default output is stable key=value lines (one per concern; values containing spaces are quoted), so agents and scripts can parse it:
cli=verified means the running CLI already imports muyan_pilot from the deployment checkout (the editable install is in place); cli=installed means the force editable reinstall ran. labels=8/8 means all eight platform labels match labels.toml; there is one timer= line per timer instance and its next is that instance’s next trigger time (- when it has none); optional_proxy is healthy, unhealthy or unavailable and never changes the exit code. migrated=true means setup rewrote an HTTPS origin to the SSH URL (a re-run then reports migrated=false); ssh_reachable is the git ls-remote probe result. --json prints the equivalent JSON document:

Success and failure examples

Success (exit code 0):
(The optional proxy is down here — note optional_proxy=unhealthy — but the core setup still succeeds with exit code 0.) Failures (non-zero exit code, setup_failed reason=... on stderr):
A missing prerequisite command (e.g. uv), a wrong repo, missing labels that cannot be created (permission), a dirty checkout, an unreachable SSH (no HTTPS fallback) or a missing systemd user bus all stop the setup with the concrete reason — nothing is half-initialized without a clear signal.

Idempotency

Re-running setup on an already-initialized machine is a no-op for the external state: labels that already match are not rewritten, the unit templates are re-copied from the repo (drift repair), the timer stays enabled, and no Issues or business data are created or modified. The reported hashes and states are stable across runs. The HTTPS→SSH migration is one-shot: after the first run the origin remote is SSH and re-runs only re-verify it (migrated=false).