Find a file
2026-09-13 13:46:28 +03:00
bin Initial commit 2026-09-13 13:46:28 +03:00
examples Initial commit 2026-09-13 13:46:28 +03:00
public Initial commit 2026-09-13 13:46:28 +03:00
src Initial commit 2026-09-13 13:46:28 +03:00
test Initial commit 2026-09-13 13:46:28 +03:00
.gitignore Initial commit 2026-09-13 13:46:28 +03:00
package-lock.json Initial commit 2026-09-13 13:46:28 +03:00
package.json Initial commit 2026-09-13 13:46:28 +03:00
README.md Initial commit 2026-09-13 13:46:28 +03:00

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

# 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_finish event
  • context sizetokens.total of the last step (how full the window is)
  • topicprogress.topic
  • % completeprogress.percent, or the ratio of completed goal stages
  • next stageprogress.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 sessionID from the first event and passes --session <id> 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:

# 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)