10 KiB
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 — <stage> ───────────────────────────────┐ │
│ │ ▸ 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)
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 <repo>/.kilocode-loop/goal.json when
no goal is passed:
# 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 <repo>/.kilocode-loop/config.json (agent, iterations,
session mode, HITL, port, context threshold). The goal lives in
<repo>/.kilocode-loop/goal.json — edit it, or point --goal somewhere else.
Standalone
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 http://127.0.0.1:7999.
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 <project>/.kilocode-loop/progress.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_finishevent - context size —
tokens.totalof 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
<project>/.kilocode-loop/
progress.json # the agent's machine-readable report
answers.json # operator answers from HITL
runs/<runId>/
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:
{
"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:
# Stabilise the workflows module
- [ ] Remove legacy libs
- [x] Rewire the builder
CLI reference
Goal
--goal <file> .json goal or .md checklist
--goal-text "<text>" inline goal
-n, --iterations <N> loop count (default 5)
Execution
-C, --project <dir> repository the agent works in (default cwd)
-a, --agent <name> Kilo agent (default code-design)
-m, --model <id> model override (provider/model)
--variant <name> reasoning variant (e.g. high, max)
--thinking capture reasoning parts
--auto / --no-auto auto-approve permissions (default: auto)
--session-mode <mode> continue (one growing session) | fresh
--hitl <mode> always | on-question | off
--max-iteration-minutes <n>
--context-warn-tokens <n>
--prompt-extra <file> extra instructions for every iteration
Dashboard
-p, --port <n> default 7999
--host <host> default 127.0.0.1
--keep-open keep the dashboard after the loop
Utility
--dry-run simulate the agent (no API calls)
--resume <runId> reuse a previous run's session + stage progress
--run-id <id>
--quiet suppress the live console stream
--no-color
Config can also live in <project>/.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:
{"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
sessionIDfrom the first event and passes--session <id>on later iterations, so--session-mode continuekeeps one growing context (and--session-mode freshstarts 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").--autois on by default; deny destructive permissions in the projectkilo.jsonif needed. --thinkingis required forreasoningevents to be emitted.
Safety
- The runner never commits or pushes; the agent is instructed not to either.
--autoapproves permissions the project does not explicitly deny — reviewkilo.json/.kilopermissions before unattended runs.--dry-runperforms no API calls and no file changes (beyond its ownprogress.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:
# 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)