looper · git:20260817.608d562 · 2026-08-17 · sha256 4981d9f3aae9ca61
looper git:20260817.608d562B
Immutable. This exact content is served forever at /api/v1/blob/4981d9f3aae9ca61.
--- name: looper description: Use when installing, bootstrapping, configuring, starting, verifying, operating, or troubleshooting Looper, looperd, the looper CLI, ~/.looper config, or runtime paths; when setting up Looper with opencode, claude-code, codex, cursor-cli, Grok Build, Pi, or Oh My Pi (omp); when registering repos or configuring planner/reviewer/fixer/worker loops; or when diagnosing status, logs, osascript, git, gh, LOOPER_TOKEN, writable path, or daemon startup issues. --- # Looper Use this skill when an agent needs to install, configure, start, check, operate, or troubleshoot Looper (`looper` CLI, `looperd` daemon, or files under `~/.looper`). It also covers the full webhook-mode lifecycle — turning it on, installing or validating the `gh webhook` extension, confirming forwarders are healthy, diagnosing a degraded runtime, clearing stale GitHub CLI hooks with `looper webhook cleanup`, and judging when a daemon restart is actually needed. `looperd` also watches its selected config source while running. Curated hot-safe policy changes—including `agent.vendor`—apply to claims made after publication without restarting the daemon; active runs keep the configuration snapshot they started with. The scheduler remains available when the daemon starts without a vendor, so configuring one later can activate an already prepared model/params profile. Leaving one configured vendor—by switching or clearing it—requires empty `agent.params` and, when a model is explicit, a paired model change or unset; cross-vendor continuations keep checkpoint/worktree state but start a fresh native session. Invalid edits and changes to process-owned settings are rejected as a whole while the daemon keeps its last-known-good snapshot. Use the Configuration page at `/dashboard/config` for supported field-level edits and reload diagnostics, and read [`references/config.md`](references/config.md) before deciding that a restart is required. Dashboard writes use the revision bound to the published values returned by the config read, then repeat an identity/mode/byte check immediately before atomic rename. This detects external generations present before the final check, including one not yet accepted by the watcher. Portable filesystems leave a tiny final-check-to-rename race, so avoid simultaneous manual and dashboard writes. Without token authentication, config PATCH requires a direct loopback peer and Host authority and rejects proxy-forwarding headers; proxied access requires `local-token` authentication. Dashboard serialization preserves the selected format and unknown top-level extension sections, but may normalize comments and lexical ordering; it also rejects symlinked config paths. Use a targeted manual edit of the symlink target when those constraints matter. **When NOT to use this skill:** developing on the Looper codebase itself (Go sources at `cmd/`, `internal/`, `pkg/`). For that, follow `AGENTS.md` and standard Go tooling. ## Looper in one paragraph Looper is a local daemon (`looperd`) that polls GitHub and runs four agent loops in their own git worktrees. Each loop is gated by GitHub labels: | Role | Default discovery | Hands off via | | --- | --- | --- | | 🧭 **Planner** | Open issues with `looper:plan`, assigned to current user | Opens spec PR labeled `looper:spec-reviewing` | | 🔍 **Reviewer** | PRs where current user is review-requested, plus `looper:spec-reviewing` follow-up | A clean review on a `looper:spec-reviewing` PR promotes it to `looper:spec-ready` | | 🔧 **Fixer** | Open non-draft PRs authored by current user with actionable threads | Pushes fixes; reviewer re-runs | | 🚢 **Worker** | Open issues with `looper:worker-ready` (assigned), or PRs labeled `looper:spec-ready` | Implements on the same PR until checks pass | All trigger fields combine with logical AND; empty label lists mean "no label constraint." Triggers are customizable per role and per project — see [`references/config.md`](references/config.md) for the full schema, validation rules, and override examples. Manual loop starts always work, even when `roles.<role>.autoDiscovery=false`: ```bash looper plan --project <id> --issue <num> looper review <owner>/<repo>#<pr> [--loop] looper work --project <id> --issue <num> looper loop start --type fixer --pr <owner>/<repo>#<pr> ``` ## One-shot install and configuration Use this when the user wants Looper installed, configured, and running end-to-end. Confirm each destructive step before running it (config writes, daemon start, project add). ### Step 0 — Preflight (read-only) Looper currently supports macOS (`darwin-arm64`) and Linux (`linux-amd64`). Stop and ask the user how to proceed if the host is not supported: ```bash case "$(uname -s)-$(uname -m)" in Darwin-arm64|Darwin-aarch64) ;; Linux-x86_64|Linux-amd64) ;; *) echo "Looper supports macOS arm64 and Linux x64 only; stop and confirm with the user before continuing." >&2 ;; esac ``` On Linux, use detached foreground-mode daemon management; launchd supervision is macOS-only. ```bash case "$(uname -s)" in Darwin) ;; *) echo "Skip macOS launchd assumptions on this host." >&2 ;; esac ``` Then check required tools: ```bash command -v git command -v gh gh auth status command -v osascript # required if osascript notifications stay enabled ``` If `git` or `gh` are missing, ask the user before installing them. On macOS with Homebrew: ```bash brew install git gh ``` If `gh auth status` is not authenticated, ask the user to run `gh auth login`. For deeper preflight detail, see [`references/daemon.md`](references/daemon.md). ### Step 1 — Detect available agent vendors Auto-detect installed agent CLIs in parallel before asking: | `agent.vendor` | Detect with | | --- | --- | | `claude-code` | `command -v claude` | | `codex` | `command -v codex` | | `opencode` | `command -v opencode` | | `cursor-cli` | `command -v agent` | | `grok-build` | `command -v grok` | | `pi` | `command -v pi` | | `omp` | `command -v omp` | Use the `question` tool to let the user pick one. List detected vendors first, marked `(installed)`, with undetected ones appended as `(not installed — needs setup)`. If multiple are installed, do not impose an opinionated default — present them in detection order and let the user choose. If none are installed, ask the user which one they want to install before continuing; do not proceed to bootstrap with a vendor whose CLI is missing. After the user picks a vendor, verify it is authenticated (run the vendor's own status command, e.g. `claude --version` followed by a quick auth check, or `agent status`). If the vendor CLI exits with an auth error, surface it and ask the user to log in via the vendor's own flow before continuing. Looper inherits the vendor's own authentication (e.g. `claude login`, `agent login`, or env vars in the user's shell). For xAI Grok Build (`agent.vendor = "grok-build"`, executable `grok`), use `grok login --device-auth` or provide `XAI_API_KEY` in the daemon environment. For Pi (`agent.vendor = "pi"`, executable `pi`) and Oh My Pi (`agent.vendor = "omp"`, executable `omp`), use each vendor's own login/config. Prefer those vendor/daemon authentication mechanisms over storing credentials in Looper configuration. If the user explicitly manages a value through `agent.env`, treat it as a local secret: the dashboard/API returns only its key, never its value, and it must not be committed or copied into examples. Grok Build fresh runs default to `--always-approve` and `--sandbox off` so the agent can update Git metadata outside a linked worktree. Configured arguments override defaults: operators can select a stricter sandbox when the repository layout permits it; `--permission-mode` may prompt or fail unattended runs; non-`plain` output can prevent direct completion-marker parsing; and `-p`/`--single` replaces Looper's generated task prompt. Daemon native resume and interactive `looper resume` takeover are unsupported for Grok Build; retries use a fresh checkpoint prompt, and Looper never uses ambient `--continue`. Pi fresh runs default to `-p <prompt> --approve` (project-local `.pi` trusted for the run). Oh My Pi (`omp`) fresh runs default to `-p <prompt> --cwd <worktree> --auto-approve`. Configured print/approve/cwd/approval-mode flags override those defaults. Daemon native resume and interactive `looper resume` takeover are unsupported for `pi` and `omp`; retries use a fresh checkpoint prompt. ### Step 2 — Pick the first project to watch Use the `question` tool with exactly these three options: 1. **Use the current directory** — "Register the repo at the current working directory (must be a git checkout)." 2. **Enter a project path** — "Provide an absolute path to a local git repository on disk." 3. **Skip for now** — "Bootstrap Looper without a project; add one later with `looper project add`." Resolution rules per choice — bind the user-provided path to a shell variable (e.g. `REPO=...`) so it does not collide with `$PATH`: - **Current directory**: `REPO="$(git -C "$PWD" rev-parse --show-toplevel)"`. If `git` errors, the directory is not a git repo — fall back to asking for an explicit path. - **Project path**: validate the path is absolute and contains a `.git` entry: `test -d "$REPO/.git" || test -f "$REPO/.git"` (the file form supports git worktrees). Reject relative paths and ask again. - **Skip for now**: continue to Step 3 with no `--project-path` flag. Save the resolved absolute path (if any) for Step 4. See [`references/cli.md`](references/cli.md) for `looper project add` semantics. ### Step 3 — Install the `looper` CLI ```bash curl -fsSL https://raw.githubusercontent.com/nexu-io/looper/main/scripts/install.sh | sh looper --version ``` If `looper --version` fails, do not guess a new install location. The installer controls placement; the typical fix is a `PATH` problem in the user's shell. Determine where `install.sh` placed the binary, then ask the user whether to add that directory to their shell's `PATH` (e.g. by editing `~/.zshrc`). ### Step 4 — Bootstrap config, daemon, and first project `looper bootstrap` writes the active Looper config file (usually `~/.looper/config.toml`; some existing installs still use `~/.looper/config.json`), installs the managed daemon to `~/.looper/bin/looperd`, optionally registers a project, and starts `looperd`. **If a Looper config file already exists (commonly `~/.looper/config.toml` or legacy `~/.looper/config.json`), do NOT pass `--yes`.** Inspect first with `looper config show`, then triage by what is missing or wrong: | Existing-config state | Action | | --- | --- | | Config exists, daemon healthy, no projects yet | Run `looper project add` for the chosen path; skip bootstrap | | Config exists with wrong/missing `agent.vendor` | Targeted edit after confirmation; leaving a configured vendor (switch or clear) requires empty `agent.params` and a paired explicit model change/unset | | Config exists, daemon unhealthy | Triage with `looper daemon status` and `looper daemon logs --startup` first; do not re-bootstrap blindly | | Config exists and is correct | Skip bootstrap; go to Step 5 verification | When the config does not yet exist and you have the user's selections from Steps 1–2: ```bash # With a project path from Step 2 looper bootstrap --yes \ --project-path "$REPO" \ --agent-vendor "<selected-vendor>" # Skipped project in Step 2 looper bootstrap --yes \ --agent-vendor "<selected-vendor>" ``` If the user prefers to drive bootstrap themselves: `looper bootstrap` (interactive). See [`references/cli.md`](references/cli.md) for every supported flag. #### Plane task-source + Feishu HITL variant Use this when the issues live in a [Plane](https://plane.so) project (not GitHub) but the code + PRs stay on GitHub. Looper reads work-items from Plane and opens PRs on the GitHub `repo`. `--provider plane` generates a **fresh** config, so only use it when no config exists yet. ```bash looper bootstrap --yes \ --provider plane \ --project-path "$REPO" \ --code-repo <owner>/<repo> \ --plane-workspace <workspace-slug> \ --plane-project <plane-project-uuid> \ --trigger-label looper:plan \ --feishu-webhook-env LOOPER_FEISHU_WEBHOOK_URL \ --agent-vendor "<selected-vendor>" ``` `--code-repo` may be omitted if `$REPO` has a `github.com` origin (it is auto-detected). Two env vars must be exported in the daemon's shell before Step 5 (never store them in the config): ```bash export PLANE_API_KEY="<plane-api-key>" # matches --plane-token-env (default PLANE_API_KEY) export LOOPER_FEISHU_WEBHOOK_URL="<feishu-bot-webhook-url>" # matches --feishu-webhook-env ``` Full flag reference, config shape, and follow-ups: [`references/plane.md`](references/plane.md) and [`docs/plane-provider.md`](../../docs/plane-provider.md). ### Step 5 — Verify the install Run all of these and report the results. Do not restart the daemon if status is healthy. ```bash looper status looper daemon status looper daemon logs --startup looper config show looper project list ``` The bundled diagnostic helper is read-only and safe to run; invoke it via its absolute skill path (it is not on `PATH`): ```bash bash <skill-bundle>/scripts/check.sh ``` Replace `<skill-bundle>` with the actual install location of this skill (commonly under `~/.claude/skills/looper/` or wherever the skill installer placed it). If the path is unknown, skip the helper and rely on the `looper`/`gh` checks above. A healthy install shows: - `looper status` reports daemon running and config valid. - `looper daemon status` shows a PID, recent start time, no `last error`. - `looper config show` lists the expected `agent.vendor` and projects. - `looper project list` lists every repo the user expects. - `gh auth status` is authenticated for those repos. If `server.authMode` is `local-token`, the user needs to export the token in their shell before running CLI commands: ```bash export LOOPER_TOKEN="<value-of-server.localToken>" ``` For daemon log layout and supervised vs detached mode, see [`references/daemon.md`](references/daemon.md). ### Step 6 — Add additional projects (optional) ```bash looper project add /absolute/path/to/repo --id <stable-id> --repo <owner>/<repo> ``` Always prefer absolute paths and confirm the GitHub slug (`owner/repo`) before running. ### Step 7 — First loop (smoke test) Suggest a smoke test only after explicit confirmation, since this triggers automation against the user's GitHub repo. **Pick a non-production repo and a low-risk issue the user is happy to plan.** Do not run smoke tests against critical production workflows. ```bash looper plan --project <id> --issue <num> looper ps looper logs <id> --follow ``` ### Common install failures | Symptom | Likely cause | Fix | | --- | --- | --- | | `tools.gitPath` or `tools.ghPath` could not be resolved | `looperd` cannot find binaries in its env | Set explicit `tools.gitPath` / `tools.ghPath` in config | | `tools.osascriptPath is required when osascript notifications are enabled` | macOS notifications enabled but `osascript` not resolvable | Set `tools.osascriptPath`, or disable `notifications.osascript.enabled` after confirming | | `authMode=local-token requires server.localToken` | Token mode without token | Add `server.localToken` and export `LOOPER_TOKEN` for the CLI | | `agent.vendor` missing | No agent configured | Set `agent.vendor` to a supported vendor whose CLI is installed locally | | Runtime path not writable | `~/.looper/`, `logs/`, `backups/`, or worktree root not writable | Fix ownership/permissions, do not delete data without explicit confirmation | | Daemon binary missing | `~/.looper/bin/looperd` not installed | `looper daemon install --force`, then `looper daemon start` | | `looper --version` not found | Installer placed binary outside `PATH` | Identify install dir, ask user to add it to shell `PATH` | ## References For deeper detail, consult these bundled docs before acting: - [`references/cli.md`](references/cli.md) — installed `looper` CLI commands, install/uninstall scripts, `looper bootstrap`, `looper project add`, daemon lifecycle, loop inspection. - [`references/config.md`](references/config.md) — full Looper config shape, every field, validation rules, env var overrides, CLI flag overrides, role trigger customization, reviewer event mapping. - [`references/daemon.md`](references/daemon.md) — `looperd` startup, supervised vs detached mode, launchd integration, log locations, startup-failure triage. - [`references/plane.md`](references/plane.md) — Plane task-source provider + Feishu HITL: extra bootstrap flags, the two env vars, generated config shape, and a discovery verify step. - [`scripts/check.sh`](scripts/check.sh) — read-only local diagnostic. Verifies `git`, `gh`, `gh auth status`, optional `osascript`, `looper --version`, config presence, and `~/.looper` writability. Invoke via absolute skill path. When in doubt, prefer read-only checks first: ```bash looper status looper daemon status --json looper daemon logs --startup looper config show looper webhook status ``` When webhook mode is degraded, inspect stale GitHub CLI forwarder hooks before restarting the daemon: ```bash looper webhook cleanup owner/repo ``` Only run deletion after the dry run shows stale `cli` hooks and the user confirms: ```bash looper webhook cleanup owner/repo --confirm ``` ## Safety rules - Do not overwrite or rewrite the Looper config file (`~/.looper/config.toml` on new installs; `~/.looper/config.json` on some existing installs) without explicit user confirmation. Prefer targeted edits. - Do not restart `looperd` merely to apply a hot-safe config change. Check `/dashboard/config` or daemon logs for the reload result; restart only for an explicitly reported restart-bound change or a separate lifecycle reason. - Do not assume the dashboard is a lossless text editor. It preserves format and unknown top-level sections, but may normalize comments, quoting, and key/table order; it refuses to replace a symlinked config path. - Do not delete runtime artifacts (`~/.looper/looper.sqlite`, `backups/`, `logs/`, `worktrees/`) unless the user explicitly asks and understands the impact. - Starting or restarting `looperd` can launch background automation against configured GitHub repositories — confirm intent before doing so. - Do not toggle `daemon.mode` (foreground ↔ launchd) without confirming; supervised mode persists across login/reboot. - Reviewer defaults are intentionally action-taking (`clean=APPROVE`, `blocking=REQUEST_CHANGES`) while `enableSelfReview` stays off. Do not broaden reviewer authority further without explicit user opt-in (for example enabling self-review or auto-merge, or relaxing review-event guardrails). - Do not overwrite or delete existing `looper:*` labels in user repos without confirmation; they may have local customizations. - Never print secrets from config or environment. Redact tokens and API keys as `***` in summaries. - Prefer `looper daemon status`, `looper daemon logs`, and the read-only checks above before making changes. ## Common mistakes - `cat ~/.looper/config.*` as a first move: use `looper config show` instead and redact secrets. - Restarting after every config edit: hot-safe policy reloads automatically for claims made after publication. Check the Configuration page or logs first; restart can re-trigger automation. - Assuming every valid edit was applied: a restart-bound field in the same candidate rejects the entire reload, and the last-known-good snapshot remains active until the file is corrected or the daemon is restarted with that config. - Submitting a stale Configuration page repeatedly: the patch revision intentionally conflicts after another editor changes the file; wait for a safe edit to publish and refresh, or resolve the displayed reload diagnostics in the file before retrying. - Disabling `notifications.osascript.enabled` silently: confirm the change or set an explicit `tools.osascriptPath`. - Rewriting the whole config for one fix: make targeted edits and preserve existing settings. - Treating a missing `~/.looper/` directory as permission to create or delete data: explain impact and ask first. - Running smoke tests against production repos: pick low-risk issues only.