{"id":51172098,"url":"https://github.com/yusukeshib/looop","last_synced_at":"2026-06-27T01:09:50.625Z","repository":{"id":365065148,"uuid":"1269181247","full_name":"yusukeshib/looop","owner":"yusukeshib","description":"A tiny, portable, Kubernetes-shaped control loop for your work: senses your world and makes one LLM-driven move per beat toward your goals.","archived":false,"fork":false,"pushed_at":"2026-06-24T13:00:16.000Z","size":871,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T14:24:32.748Z","etag":null,"topics":["ai-agents","automation","bash","cli","control-loop","kubernetes","llm","reconciliation"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yusukeshib.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T11:59:40.000Z","updated_at":"2026-06-24T12:58:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/yusukeshib/looop","commit_stats":null,"previous_names":["yusukeshib/loop","yusukeshib/looop"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/yusukeshib/looop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukeshib%2Flooop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukeshib%2Flooop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukeshib%2Flooop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukeshib%2Flooop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yusukeshib","download_url":"https://codeload.github.com/yusukeshib/looop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukeshib%2Flooop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34798183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","automation","bash","cli","control-loop","kubernetes","llm","reconciliation"],"created_at":"2026-06-27T01:09:46.700Z","updated_at":"2026-06-27T01:09:50.607Z","avatar_url":"https://github.com/yusukeshib.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# looop\n\nA tiny, portable control plane for agent-driven work. One self-contained binary —\nno database, no server.\n\n## What it does\n\n`looop` watches the things you care about (GitHub, Linear, Grafana, …) and runs a\nfleet of worker agents. Every beat it senses the world and, if something changed,\nmakes the single most important move — including spawning workers. You don't drive\nit; you steer it by editing goals and the PLAYBOOK. Irreversible actions (merges,\ndeploys, deletes) always wait for your explicit yes.\n\n## Architecture\n\nEach beat the pulse runs three steps:\n\n1. **SENSE** — run every `sensors/*.sh`, refreshing `snapshots/`. Unchanged world\n   → stop here, no LLM call.\n2. **DECIDE** — on change, hand PLAYBOOK + goals + readings + asks to the LLM,\n   which returns **one** typed move.\n3. **ACT** — execute it: write a goal/sensor/PLAYBOOK, run one reversible command,\n   or spawn a worker. One move per beat; a daily budget caps spend.\n\nState lives entirely in files, so the loop is **level-triggered**: it re-senses\nevery beat and a crashed pulse just re-reads its files on restart. When a worker\nneeds a human decision it blocks on `looop _ ask`; you reply with `looop _ answer`\n— a durable mailbox that needs no tmux or stdin.\n\nEverything is plain files in the data dir:\n\n| File / dir         | Role                                                    |\n| ------------------ | ------------------------------------------------------- |\n| `PLAYBOOK.md`      | your judgment, priorities, guardrails                   |\n| `goals/*.md`       | desired state — one declarative spec per thing you push |\n| `sensors/*.sh`     | observers — each prints **one JSON object**             |\n| `journal.md`       | action log — one line per move                          |\n| `asks/` `answers/` | the worker ↔ human mailbox                              |\n\n## Install\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/yusukeshib/looop/main/install.sh | bash\n# or\ncargo install looop\n```\n\n**Runtime dep:** an LLM runner — the only hard requirement. `claude` is the\ndefault; `codex`, `opencode`, and `pi` are also supported. Run `looop init` to\npick one (see below).\nWorkers run in parallel, so each isolates its own workspace (a `git worktree`, or\n`box` if available) to avoid clobbering another worker's files; this is a worker\nconvention, not a dependency of looop itself.\n\n## Usage\n\n```sh\nlooop init          # interactive setup: edit the agent commands (tick_command/worker_command)\nlooop up            # start the autonomous pulse (detached)\nlooop watch         # live log + running-session selector\nlooop down          # stop the pulse and all workers\n```\n\n`looop init` lets you edit the two command strings of the wiring\n(`tick_command` / `worker_command`), each prefilled with the current value (or the\nbuilt-in **claude** default on first run). It is **required before `looop up`** —\nthe pulse refuses to start until the wiring exists, so the agent CLI driving every\ntick and worker is an explicit choice rather than a silent default. See\n[Configuration](#configuration) for ready-made wirings to paste in.\n\n### First run\n\nlooop is steered by an agent, not by you typing commands. The first-run flow:\n\n1. **`looop init`** — accept the claude default, or paste a different runner's\n   wiring (see [Configuration](#configuration)). Required before the pulse starts.\n2. **Start a concierge.** Launch an agent and ask it to drive looop for you:\n   ```sh\n   claude   # or pi / codex / opencode — then say:\n   # \"be my looop concierge: run `looop up`, then relay the setup goal and\n   #  interview me to write my goals + sensors + PLAYBOOK\"\n   ```\n   The concierge runs `looop up` (starting the autonomous pulse) and speaks plain\n   language while driving the `looop _ …` contract for you — relaying pending\n   asks, helping edit goals, answering on your behalf.\n3. **The first tick opens the `setup` goal.** A fresh data dir is seeded with a\n   starter PLAYBOOK + a `setup` goal whose top priority is exactly this: looop\n   runs headless (it can't interview anyone), so on the first changed beat it\n   journals an invitation that your concierge surfaces, then the concierge\n   interviews you and writes your real goals/sensors/PLAYBOOK. Once customized,\n   archive the `setup` goal and looop runs from there.\n\nYou can also skip the concierge entirely: run `looop up` yourself and steer by\nhand (edit goals/PLAYBOOK, or use the `looop _ …` verbs). See `looop help` for the\nfull command reference and design manual.\n\n## Configuration\n\nThe config (`$LOOOP_CONFIG`, default `~/.config/looop/config.json`) is just **two\nshell commands** — looop is glue and knows nothing about any specific runner:\n\n| Key              | Role                                                                              |\n| ---------------- | --------------------------------------------------------------------------------- |\n| `tick_command`   | run ONE disposable decision. The tick prompt arrives on **stdin**; must run unattended (no permission prompts — the detached pulse can't answer them) and emit a structured event stream looop can render. |\n| `worker_command` | launch a worker agent. `{{prompt_file}}` is substituted with the worker's prompt file path. |\n\n(Re-attaching to a worker is handled in-process by looop, so there is no `resume`\ncommand to configure.) `looop init` just lets you edit these two strings. The\nbuilt-in default is `claude`; paste one of the wirings below (or your own) to\nswitch runner.\n\n**claude** (default)\n\n```json\n{\n  \"tick_command\": \"claude -p --output-format stream-json --verbose --dangerously-skip-permissions --model sonnet\",\n  \"worker_command\": \"claude --dangerously-skip-permissions --model opus \\\"$(cat {{prompt_file}})\\\"\"\n}\n```\n\n**codex**\n\n```json\n{\n  \"tick_command\": \"codex exec --json --dangerously-bypass-approvals-and-sandbox\",\n  \"worker_command\": \"codex --dangerously-bypass-approvals-and-sandbox \\\"$(cat {{prompt_file}})\\\"\"\n}\n```\n\n**opencode** (best-effort — verify against your installed version)\n\n```json\n{\n  \"tick_command\": \"opencode run\",\n  \"worker_command\": \"opencode \\\"$(cat {{prompt_file}})\\\"\"\n}\n```\n\n**pi**\n\n```json\n{\n  \"tick_command\": \"pi -p --mode json -ne --model claude-sonnet-4-5 --thinking low 'Execute the looop tick instructions provided on stdin.'\",\n  \"worker_command\": \"pi --model claude-opus-4-8 --thinking medium @{{prompt_file}}\"\n}\n```\n\nModel ids above are examples. For claude, `sonnet`/`opus` are aliases that always\nresolve to the latest of each; pin a specific version (e.g.\n`--model claude-opus-4-1`) if you need reproducibility.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyusukeshib%2Flooop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyusukeshib%2Flooop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyusukeshib%2Flooop/lists"}