# kilocode-loop Goal-driven autonomous loop runner for the **Kilo CLI** (`kilo run`), with a live Express dashboard, per-iteration reports (tokens / cost / context / topic / % / next stage), streamed section headings for every action, terminal controls and human-in-the-loop between sessions. It is a self-contained mini-app: one Node process spawns `kilo run --format json` for every iteration, parses the event stream, and turns the agent's own `progress.json` into the operator report. ``` ┌─ loop ──────────────────────────────────────────────────────────────────┐ │ for i in 1..N: │ │ ┌─ section: Iteration i/N — ───────────────────────────────┐ │ │ │ ▸ Read src/foo.vue (tool → section heading) │ │ │ │ ▸ Edit src/foo.vue │ │ │ │ ▸ Run pnpm typecheck │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ report: tokens · cost · context · topic · % · next stage │ │ human-in-the-loop: ask the operator if the agent needs a decision │ └─────────────────────────────────────────────────────────────────────────┘ ``` ## Requirements - Node.js ≥ 20 (developed on Node 24) - The Kilo CLI on `PATH` (`kilo` / `kilocode`), authenticated - A git repository to work in (the agent never commits) ```bash npm install ``` ## Quick start ### Installed as a submodule (recommended) The launcher resolves the git root of the current directory, installs `node_modules` on first use, and picks up `/.kilocode-loop/goal.json` when no goal is passed: ```bash # from anywhere inside the repo (or a submodule — it scopes to that repo) tools/kilocode-loop/kilo-loop # uses .kilocode-loop/goal.json, 5 iterations tools/kilocode-loop/kilo-loop -n 3 # override the iteration count tools/kilocode-loop/kilo-loop --goal plans/another-goal.md tools/kilocode-loop/kilo-loop --dry-run # offline rehearsal # same command from anywhere, once the launcher is on PATH kilo-loop --goal plans/another-goal.md -n 5 ``` Per-repo defaults live in `/.kilocode-loop/config.json` (agent, iterations, session mode, HITL, port, context threshold). The goal lives in `/.kilocode-loop/goal.json` — edit it, or point `--goal` somewhere else. ### Standalone ```bash npm install # Real run: 5 iterations of the code-design agent against ./your-repo node bin/kilocode-loop.mjs \ --goal /path/to/your-repo/.kilocode-loop/goal.json \ -n 5 \ --project /path/to/your-repo \ --agent code-design \ --port 7999 # Offline rehearsal: no API calls, exercises the loop, reports, dashboard and HITL node bin/kilocode-loop.mjs --dry-run \ --goal examples/goal.example.json -n 5 --project /tmp/demo --port 7999 ``` Then open the dashboard at . ## Controls | Where | Key / button | Effect | | --- | --- | --- | | Terminal | `X` | Abort the loop **after** the current session finishes | | Terminal | `P` | Pause / resume before the next iteration | | Terminal | `Q` | Stop **now** (kills the current `kilo` session) | | Terminal | `?` | Print shortcut help | | Terminal | typing + `Enter` | Answer a pending question (number keys pick an option) | | Dashboard | Pause / Abort after session / Stop now | Same controls from the browser | | Dashboard | question modal | Answer the pending questions; answers go into the next session | ## How a report is produced Each iteration the runner writes a prompt that embeds a **contract**: at the end of the session the agent must write `/.kilocode-loop/progress.json`: ```json { "iteration": 2, "topic": "Rework SettingsByokSection", "stageId": "stage-2", "stageStatus": "in_progress", "percent": 40, "nextStage": "Verify layout and dark theme", "summary": "Replaced the card grid with a q-select + config panel. Typecheck passes.", "filesChanged": ["src/components/settings/SettingsByokSection.vue"], "needsInput": false, "questions": [] } ``` From that + the CLI event stream the runner derives: - **iteration number** — loop counter - **tokens / cost** — summed from every `step_finish` event - **context size** — `tokens.total` of the last step (how full the window is) - **topic** — `progress.topic` - **% complete** — `progress.percent`, or the ratio of completed goal stages - **next stage** — `progress.nextStage`, or the first not-done stage - **files changed** — git delta since the iteration started, plus `filesChanged` If the agent forgets the contract, the runner falls back to the goal stages and scrapes question-looking lines out of the agent's text. ### Reports on disk ``` /.kilocode-loop/ progress.json # the agent's machine-readable report answers.json # operator answers from HITL runs// state.json # full run state (for the dashboard) events.jsonl # every log line report.md # end-of-run summary iterations/ 01-prompt.md # exact prompt sent 01-report.json 01-report.md ``` ## Goal files `--goal-text "..."` — a single implicit stage. `--goal file.json`: ```json { "title": "Stabilise the workflows module", "description": "…", "stages": [{ "id": "stage-1", "title": "Remove legacy libs", "details": "…" }] } ``` `--goal file.md` — the first `#` heading is the title, each `- [ ]` / `- [x]` item is a stage: ```markdown # Stabilise the workflows module - [ ] Remove legacy libs - [x] Rewire the builder ``` ## CLI reference ``` Goal --goal .json goal or .md checklist --goal-text "" inline goal -n, --iterations loop count (default 5) Execution -C, --project repository the agent works in (default cwd) -a, --agent Kilo agent (default code-design) -m, --model model override (provider/model) --variant reasoning variant (e.g. high, max) --thinking capture reasoning parts --auto / --no-auto auto-approve permissions (default: auto) --session-mode continue (one growing session) | fresh --hitl always | on-question | off --max-iteration-minutes --context-warn-tokens --prompt-extra extra instructions for every iteration Dashboard -p, --port default 7999 --host default 127.0.0.1 --keep-open keep the dashboard after the loop Utility --dry-run simulate the agent (no API calls) --resume reuse a previous run's session + stage progress --run-id --quiet suppress the live console stream --no-color ``` Config can also live in `/.kilocode-loop/config.json` (CLI wins). ## Dashboard API | Method | Path | Purpose | | --- | --- | --- | | `GET` | `/api/state` | Current run snapshot | | `GET` | `/api/events` | SSE stream (`state` + `log` messages) | | `GET` | `/api/runs` | List saved runs | | `GET` | `/api/runs/:id` | Load a saved run (with its log) | | `GET` | `/api/runs/:id/report/:iter` | Iteration report (markdown) | | `POST` | `/api/control` | `{ "action": "pause"\|"resume"\|"abort-after-current"\|"stop" }` | | `POST` | `/api/answer` | `{ "answers": [{ "question": "…", "answer": "…" }] }` | ## How it drives the Kilo CLI `kilo run --format json` emits one JSON object per line: ```jsonc {"type":"step_start","timestamp":…,"sessionID":"…","part":{…}} {"type":"tool_use","part":{"tool":"edit","state":{"status":"completed","input":{…}}}} {"type":"text","part":{"type":"text","text":"…"}} {"type":"step_finish","part":{"cost":0.0021,"tokens":{"total":14411,"input":14025,"output":2,"reasoning":0,"cache":{"read":384,"write":0}}}} {"type":"error","error":…} ``` - The runner captures `sessionID` from the first event and passes `--session ` on later iterations, so `--session-mode continue` keeps one growing context (and `--session-mode fresh` starts a new one each time). - Unattended runs need `--auto`: without it, non-interactive permission requests are auto-rejected by the CLI ("pass --auto for autonomous use"). `--auto` is on by default; deny destructive permissions in the project `kilo.json` if needed. - `--thinking` is required for `reasoning` events to be emitted. ## Safety - The runner never commits or pushes; the agent is instructed not to either. - `--auto` approves permissions the project does not explicitly deny — review `kilo.json` / `.kilo` permissions before unattended runs. - `--dry-run` performs no API calls and no file changes (beyond its own `progress.json`). ## Adding it to the main project as a submodule This repo is standalone and has its own history. To pull it into another repo: ```bash # 1. create an empty repo on Forgejo (e.g. w4c-agent/kilocode-loop), then: cd /path/to/main-repo git submodule add ssh://git@localhost:13022/w4c-agent/kilocode-loop.git tools/kilocode-loop git commit -m "chore: add kilocode-loop submodule" ``` Because it is a submodule, `node_modules/` stays local and is never committed. ## Layout ``` bin/kilocode-loop.mjs entry point (wires config → orchestrator → dashboard) src/config.mjs CLI/config parsing src/orchestrator.mjs the loop, controls, HITL, report assembly src/kilocode.mjs `kilo run` driver + JSONL event parser src/goal.mjs goal files, agent contract, progress/percent/questions src/state.mjs run state, persistence, log stream src/reports.mjs console/markdown reports, formatting src/console.mjs live console renderer src/terminal.mjs TTY controls src/server.mjs Express + SSE dashboard API src/simulator.mjs --dry-run agent public/ dashboard UI examples/ sample goals test/ node:test unit tests (npm test) ```