# 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 also enforces its own safety net: **independent verification gates** (a stage is not done until the loop's `--verify` command exits 0), a **destructive-command guard**, per-iteration **git checkpoints with revert**, **budget/stall limits**, **secret redaction**, **notifications** and an optional **reviewer pass**. By default every iteration runs in its **own fresh session** (small context, cheaper, more targeted): the loop carries only a curated handoff — goal, current stage, previous summary, operator answers and a handoff packet the agent prepares. Turn on **Use shared context between iterations** (dashboard checkbox, `--shared-context`, or `S` in the terminal) to keep one growing session instead. 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 and installs `node_modules` on first use. Run it with no `--goal` to open the dashboard launcher and pick a plan in the UI: ```bash # from anywhere inside the repo (or a submodule — it scopes to that repo) tools/kilocode-loop/kilo-loop # open the dashboard launcher (pick a plan in the UI) tools/kilocode-loop/kilo-loop --list-goals # print discovered plans + state, then exit tools/kilocode-loop/kilo-loop --goal .kilocode-loop/goal.json -n 5 # run one directly tools/kilocode-loop/kilo-loop --dry-run --goal examples/goal.example.json # offline rehearsal # same command from anywhere, once the launcher is on PATH kilo-loop 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, default verify). No goal is injected by default — pass `--goal` to run one directly, or pick it in the dashboard. ### 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 . ## Pre-flight confirmation On an interactive terminal a **direct** run (`kilo-loop --goal …`) does not start immediately: it prints a summary of the resolved scenario and waits for confirmation. Launcher mode (no `--goal`) skips this — nothing runs until you press **Start run** in the dashboard. ``` ╭─ About to run ───────────────────────────────────────────────────────╮ │ Goal Workflows module — review, simplification & hardening │ │ Source /home/.../.kilocode-loop/goal.json │ │ Project /home/.../wiz4apps │ │ Agent code-design │ │ Iterations 5 │ │ Context fresh session per iteration (handoff) │ │ HITL on-question │ │ Auto-approve yes │ │ Dashboard http://127.0.0.1:7999 │ │ Mode live │ ╰──────────────────────────────────────────────────────────────────────╯ 1. [ ] Close Phase 1 residuals 2. [ ] Phase 2 — architecture convergence residuals ... Start this run? [Y/n] ``` Enter/`y` starts; anything else cancels without starting anything (no run dir, no dashboard, no sessions). A missing/broken goal file fails here, before anything is created. - `--no-confirm` / `--yes` — skip the prompt and start immediately. - Non-interactive stdin/stdout (pipes, CI, `--dry-run` harness) always starts immediately. - Set `"confirm": false` in `.kilocode-loop/config.json` to make auto-start the default for a repo. ## Controls | Where | Key / button | Effect | | --- | --- | --- | | Terminal | `X` | Abort the loop **after** the current session finishes | | Terminal | `P` | Pause / resume before the next iteration | | Terminal | `S` | Toggle shared context (fresh sessions ⇄ one growing session) | | 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) | | Terminal | `Ctrl+C` (SIGINT) | Stop the run and exit. It works whether or not the dashboard is open; a second `Ctrl+C` force-exits immediately | | Dashboard | Pause / Abort after session / Stop now | Same controls from the browser | | Dashboard | **Use shared context between iterations** | Off (default) = fresh session + handoff per iteration; on = one growing session | | Dashboard | **Revert before #N** | Restore the checkpoint taken before iteration N (confirmation required) | | Dashboard | **Report** | Download `report.md` for the active run | | Dashboard | question modal | Answer the pending questions (option buttons become editable answers); answers go into the next session | ### Top-bar activity indicator The header doubles as a status light, driven purely by CSS animations (JS only toggles a `topbar--` class): - **running / starting** — a glowing blue→green segment sweeps along the bottom edge over a faint travelling sheen, so it is obvious work is in progress; - **waiting-answer** — pulsing purple, to draw attention to a pending question; - **paused** — a static amber line (deliberately not animating); - **error / done-with-errors / stale** — the bar is tinted light red with a pulsing red line; - **aborted / stopped-budget / stalled / stopped-guard** — a light amber tint (warning, not failure); - **done** — a static green line; **idle** — plain header. All movement is disabled under `prefers-reduced-motion: reduce` (the state colour stays). See `public/styles.css` (`topbar--*`) and `topbarState()` in `public/app.js`. ## Fresh sessions vs shared context Default (`sharedContext: false` / checkbox off) — each iteration is an independent `kilo run` with **no session history**. To keep the work coherent, the runner carries forward only: - the goal, its stages and the current stage; - the previous iteration's `progress.json` (topic, summary, next stage, percent); - all operator answers collected so far; - the list of files changed so far this run; - the previous iteration's **handoff packet**. The handoff packet is a bounded markdown file (`.kilocode-loop/handoff.md`, ~120 lines max) that the agent writes at the end of every iteration: current state, decisions & constraints, next steps, prepared data, open questions. The runner embeds it into the next prompt and then deletes the file, so a forgotten packet can never leak into a later iteration. Older packets are archived per iteration in `runs//iterations/-handoff-in.md`. Shared context (`sharedContext: true` / checkbox on) — one growing session is continued across iterations (`--session `), so the whole history is available but the context (and cost) grows every iteration. The handoff sections are then skipped because the session already has the history. The toggle is live: the dashboard checkbox (`POST /api/control { "action": "set-shared-context", "value": true|false }`) and `S` in the terminal apply from the next iteration onward. ## 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 handoff.md # context packet for the next fresh iteration review.json # reviewer verdict (when --review is on) runs// state.json # full run state (for the dashboard) events.jsonl # every log line (secrets redacted) report.md # end-of-run summary checkpoints/ iter-01.json # HEAD + untracked list for iteration 1 iter-01.patch # `git diff HEAD` at the checkpoint iterations/ 01-prompt.md # exact prompt sent 01-review-prompt.md # reviewer prompt (when --review is on) 02-handoff-in.md # handoff packet consumed by iteration 2 01-report.json 01-report.md ``` ## Verification gates, guards, budgets and checkpoints Since v0.2 the loop no longer trusts the agent's own "done". Every safety feature is opt-out, and all of them apply to the *loop*, not the agent's prompt. ### Independent verification (the loop runs the check itself) A stage is not marked `done` until a verify command the loop itself runs exits 0. Set it globally, per stage, or both (the stage wins): ```jsonc { "title": "Ship the parser", "stages": [ { "id": "s1", "title": "Rebuild the parser", "verify": "pnpm test", "acceptance": "no failures" } ] } ``` Markdown goals take directives after `::`: ```markdown - [ ] Rebuild the parser :: verify: pnpm test :: acceptance: no failures ``` When verify fails, the stage is recorded as `blocked`, the failing output is fed back into the next iteration's prompt, and `completedStages` is not advanced. Set `--no-sync-goal` to stop the loop from ticking `[x]` in a markdown goal file. ### Destructive-command guard Two layers: 1. **Pre-execution** — the loop injects deny rules for destructive bash patterns (`git commit`, `git push`, `git reset --hard`, `git clean -f`, `npm|pnpm publish`, history rewrites, raw disk writes, recursive deletes of system/home paths, …) into the child `kilo run` via `KILO_CONFIG_CONTENT`, so `--auto` still denies them. Skipped when the project already sets a scalar `permission.bash`. 2. **Runtime** — every `tool_use` is scanned; a match **aborts that session immediately** (damage control) and, by default, the run **continues with the next iteration**: the still-incomplete stage is retried and the safety note is fed back into the next prompt (`## Loop safety feedback`), so the agent can reach the same result a safer way. In shared-context mode the aborted session is replaced by a fresh one with a synthesized handoff. Use `--guard-stop` to restore the old behaviour (stop the whole run on the first hit, status `stopped-guard`) and `--max-guard-blocks ` (default 3) to cap how many *consecutive* guard-blocked iterations are tolerated before the loop stops. `rm -rf` is **path-aware and `cd`-aware** in both layers: recursive deletes whose targets are all strictly inside a temp directory (`os.tmpdir()`, `/tmp`, `/var/tmp`) are allowed, so routine scratch cleanup no longer blocks a run — including the common agent pattern `cd /tmp && rm -rf `, where the relative target is resolved against the temp cwd. Deleting the temp root itself, a relative path with no known temp cwd, or any non-temp absolute path is still blocked; `cd /repo && rm -rf src` stays blocked. Mixed commands (one temp target plus one unsafe target) are blocked. Pre-execution denies only the catastrophic recursive targets (`~`, `$HOME`, `/home/*`, `/etc*`, `/usr*`, `/opt*`, `/root*`, `/boot*`, `/srv*`, `.git/`), while the runtime scanner remains the catch-all for everything else. ### Checkpoints and revert Before each iteration the loop captures a checkpoint under `runs//checkpoints/`: HEAD, `git diff HEAD` and the untracked-file list. Nothing is committed. **Revert before #N** on the dashboard (or `POST /api/revert { iteration }`) restores tracked files to HEAD, re-applies the checkpoint's own diff and deletes untracked files created afterwards. Revert is destructive and always behind a confirmation; it does not touch submodule contents or committed history. Disable with `--no-checkpoint`. ### Budgets and stall detection - `--max-cost ` / `--max-tokens ` — stop with status `stopped-budget`. - `--max-stale-iterations ` — stop with status `stalled` after N iterations that change no files and advance no stage. - `--max-iteration-minutes ` — per-iteration hard timeout (already existed). ### Auto-recovery from model/network errors If `kilo run` dies because the provider connection is reset, the iteration is retried instead of failing and losing the turn. Detection covers `Connection reset by [peer|server]`, `ECONNRESET`/`ETIMEDOUT`/`EPIPE`, `socket hang up`, `fetch failed`, `premature close`, HTTP `429/5xx`, and `rate limit`/`overloaded`/`timed out`. - Each retry **continues the interrupted session** (`--session `), so work already done in the turn is preserved (in shared mode the run session; in fresh mode the session the failed attempt created). - Exponential backoff with jitter: `--retry-delay` (default 2000 ms), doubling per attempt up to `--retry-max-delay` (default 60000 ms). - `--max-retries ` (default 3); `--no-retry` disables it entirely. - Timeouts (`--max-iteration-minutes`), a command-guard hit, or a user Stop are **not** retried as transient errors. A guard hit instead aborts the session and the loop continues with the next iteration (see `--guard-stop` / `--max-guard-blocks`). - The dashboard marks the iteration `retrying` (`auto-retry ×N`) and the report records the attempt count and the last transient reason. - Optionally notify on retries with `--notify …` (event `model-retry`). ### Resuming a stopped run When a run does not reach `done` (guard, error, stall, budget, abort), the CLI prints the exact command to continue it: ``` Resume this run: kilo-loop --goal --resume ``` `--resume ` reloads that run's saved state: stages it already reported as `done` are skipped, `lastPercent` is carried over, and — when the previous run was interrupted by the guard — the safety note is injected into the first iteration again. `--resume last` (or `latest`) resolves to the most recent saved run, so the everyday recovery command is simply: ```bash kilo-loop --goal .kilo/plans/.md --resume last ``` Saved runs live under `.kilocode-loop/runs//`; `kilo-loop report --last` shows the most recent one without starting anything. ### Secret redaction Every log line, `state.json`, per-iteration report and the run report is scrubbed before it is written: bearer/Basic headers, `sk-`/`ghp_`/`xox`/AWS/JWT tokens, `token=`/`password:` values and URL userinfo. Disable with `--no-redact`. ### Notifications `--notify ` (space/comma separated) sends out-of-band alerts for `question`, `verify-failed`, `guard`, `budget`, `stalled` and `run-done`: | Target | Behaviour | | --- | --- | | `https://hooks.slack.com/...` | Slack incoming webhook (`{ text }`) | | `https://ntfy.sh/` or `ntfy:///` | ntfy POST + `Title` header | | `telegram:///` | Telegram `sendMessage` | | any `https://` URL | generic JSON payload `{ event, title, text, sentAt }` | ### Reviewer pass `--review` runs one extra reviewer session (agent `--review-agent`, default the run agent) after a stage passes verify. The reviewer inspects the working tree and writes `.kilocode-loop/review.json` with `{ verdict: "pass"|"block", … }`; a `block` un-advances the stage and raises the reviewer's questions. Skipped in `--dry-run`. ### Hybrid context In shared-context mode, `--auto-fresh-tokens ` switches to a fresh session (with a synthesized handoff) once the context passes that size, instead of only warning. ## Report subcommand & exit codes ```bash kilo-loop report # most recent run's report.md kilo-loop report --json # redacted run state kilo-loop report --last -C /repo ``` `report` is read-only: it starts nothing and makes no API calls. Exit codes: `0` done, `1` done-with-errors, `2` aborted, `3` budget, `4` stalled, `5` stopped-guard. ## Kilo vs opencode The loop drives a CLI, not a library: it spawns ` run --format json` and parses the line-delimited JSON event stream. opencode is the same lineage as the Kilo CLI (near-identical `run` surface), so the wire format is compatible: | Shared with opencode | | | --- | --- | | `/run --format json` NDJSON events | `tool_use`, `step_finish`, `text`, `reasoning`, `error`; `part.tool`, `part.state.input`, `part.tokens{cache}`, `part.cost`, `sessionID` | | Flags | `--agent`, `--model`, `--variant`, `--thinking`, `--session`, `--continue`, `--title`, `--format json` | Two things differ, and the tool adapts to them automatically: | | kilo | opencode | | --- | --- | --- | | Auto-approve flag | `--auto` | `--dangerously-skip-permissions` | | Inline config env (guard) | `KILO_CONFIG_CONTENT` | `OPENCODE_CONFIG_CONTENT` | | Project config for `permission.bash` | `kilo.json`, `.kilo/kilo.json` | `opencode.json`, `.opencode/opencode.json` | Usage: ```bash # point the loop at opencode (runtime auto-detected from the binary name) KILO_BIN=opencode kilo-loop --goal goal.md -n 5 \ --agent build --model anthropic/claude-sonnet # or be explicit kilo-loop --runtime opencode --agent build ... ``` `--runtime` defaults to `auto`, which detects `opencode` from the binary name. Agent names and model IDs are CLI-specific: set `--agent`/`--model` to something defined in your opencode config (the Kilo default `code-design` is not an opencode agent). Everything the loop itself provides — verification gates, checkpoints, budgets, redaction, notifications, the dashboard and `report` — is runtime-independent. ## 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": "…", "verify": "pnpm test", "acceptance": "test suite is green", "model": "provider/fast-model", "agent": "code", "variant": "high" } ] } ``` Stage fields: `id`, `title`, `details`, `done`, and optional `verify`, `acceptance`, `model`, `agent`, `variant` (per-stage routing overrides the run defaults). `--goal file.md` — the first `#` heading is the title, each `- [ ]` / `- [x]` item is a stage, and `:: verify: … :: acceptance: …` adds directives: ```markdown # Stabilise the workflows module - [ ] Remove legacy libs :: verify: pnpm test - [x] Rewire the builder ``` ## Dashboard launcher (pick a plan in the UI) Run `kilo-loop` **without** `--goal` and the dashboard opens in launcher mode: no run is started until you pick one. ```bash kilo-loop # launcher only kilo-loop --list-goals # print every discovered plan + its state, then exit ``` The launcher lets you: - **pick a plan** from a dropdown — plans are discovered under `.kilocode-loop/goals/**`, the legacy `.kilocode-loop/goal.{json,md}`, and any markdown plan under `plans/` or `.kilo/plans/` that contains `- [ ]` items; - see each plan's **state** (done/total, %, type, last run status/iterations/cost); - **set the verify command** per stage (kept in the goal file; `- [ ] Title :: verify: cmd` for markdown, `stage.verify` for JSON) or once for all plans (stored in `.kilocode-loop/config.json`); - **Mark 100%** all stages done (`- [x]` for markdown, `done: true` for JSON) for a plan that was finished outside the loop, or **Clear done** to reopen it; - **Delete plan** — moves the plan file to the gitignored `.kilocode-loop/trash/-` (recoverable; nothing is committed). The UI asks for confirmation first; - set iterations / agent / HITL / dry-run and **Start run**; - write an optional **custom instruction** (textarea). It is added to the top of every iteration prompt (under `## Operator instructions for this run`, before the goal) and the run header shows `custom instruction` while it is active. It maps to the same `promptExtra` as the CLI `--prompt-extra`; leaving the field empty keeps any instruction coming from the CLI or config. After a run finishes, **New run** returns to the launcher (the plan list is refreshed with the new completion state). `POST /api/start` refuses to start a second run while one is active. ### Reaching the dashboard from a phone (same Wi-Fi) By default the dashboard binds loopback (`127.0.0.1`), so only this machine can open it. `--lan` binds **every interface** (`0.0.0.0`) and the console prints the machine's real LAN address to open on another device: ```bash kilo-loop --lan # or set "lan": true in .kilocode-loop/config.json # Dashboard http://192.168.1.106:7999 (Ctrl+Click to open) ``` Then open that URL on the phone (same Wi-Fi/router). A wildcard bind is never shown as a URL; the tool resolves the primary non-virtual interface (docker/vpn bridges are filtered out). An explicit `--host ` still wins, and `--local` forces loopback back on. > **Security:** the dashboard has no authentication and exposes control > endpoints (start/stop/pause, revert which deletes untracked files). Only use > `--lan` on a trusted network, and prefer `--local` (or `--host 127.0.0.1`) on > shared/public Wi-Fi. ## Scaffolding a plan (`new-plan`) Plans must contain `- [ ]` checkbox stages to be discovered at all. Generate one from the project's template instead of hand-writing the format: ```bash kilo-loop new-plan "My plan title" # writes .kilo/plans/-.md kilo-loop new-plan "My plan title" --print # preview without writing ``` The template is `/.kilo/templates/plan.md` (a sensible default is used if the project has none). Optional flags: `--slug `, `--epoch `, `--dir `, `--project

`, `--print`, `--force`. The scaffolder is also available directly at `tools/kilocode-loop/new-plan.mjs`. ## 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) --shared-context one growing session across iterations (costly) --no-shared-context fresh session per iteration + handoff (default) --session-mode legacy alias: continue = shared, fresh = no shared --hitl always | on-question | off --runtime auto | kilo | opencode (auto-detects from the CLI binary) --max-iteration-minutes --context-warn-tokens --prompt-extra extra instructions for every iteration Verification & safety --verify "" loop-run check; a stage is not done until it exits 0 --verify-timeout-minutes --checkpoint / --no-checkpoint --guard / --no-guard deny destructive bash; a hit aborts the session --guard-stop stop the run on the first guard hit (default: continue) --max-guard-blocks consecutive guard hits tolerated (default 3) --redact / --no-redact --max-cost --max-tokens --max-stale-iterations --retry / --no-retry retry an iteration on a model connection reset (default on) --max-retries retry attempts per iteration (default 3) --retry-delay first backoff delay (default 2000, doubles) --retry-max-delay backoff cap (default 60000) --review / --review-agent --auto-fresh-tokens --sync-goal / --no-sync-goal --force run even when every stage is marked done --list-goals list discovered plans + state, then exit --notify Dashboard -p, --port default 7999 --host default 127.0.0.1 --lan bind 0.0.0.0 so a phone on the same Wi-Fi can open it --local force loopback only (overrides a config "lan": true) --keep-open keep the dashboard after the loop Utility new-plan "" scaffold .kilo/plans/<epoch-ms>-<slug>.md from .kilo/templates/plan.md --dry-run simulate the agent (no API calls) --resume <runId|last> reuse a previous run's session + stage progress --run-id <id> --quiet suppress the live console stream --no-color --no-confirm, --yes start immediately (skip the pre-flight confirmation) ``` Config can also live in `<project>/.kilocode-loop/config.json` (CLI wins). ## Dashboard API | Method | Path | Purpose | | --- | --- | --- | | `GET` | `/api/state` | Current run snapshot (`{status:"idle", idle:true}` when no run is active) | | `GET` | `/api/events` | SSE stream (`state` + `log` messages) | | `GET` | `/api/goals` | Discoverable plans/goals with per-stage completion and last run | | `POST` | `/api/goals/verify` | `{ "file": "…", "stageIndex": N, "verify": "cmd" }` — set a stage verify (`stageIndex` omitted = global, JSON goals only) | | `POST` | `/api/goals/verify-global` | `{ "verify": "cmd" }` — default verify for all runs (`.kilocode-loop/config.json`) | | `POST` | `/api/start` | `{ "file": "…", "iterations": N, "agent": "…", "hitl": "…", "dryRun": bool }` — start a run from the launcher | | `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) | | `GET` | `/api/runs/:id/export` | Full run report (markdown) | | `GET` | `/api/runs/:id/diff/:iter` | Checkpoint patch captured before iteration N | | `GET` | `/api/checkpoints` | Active run's checkpoints (`?run=<id>` for a saved run) | | `GET` | `/api/runs/:id/checkpoints` | Saved run's checkpoints | | `POST` | `/api/control` | `{ "action": "pause"\|"resume"\|"abort-after-current"\|"stop" }` and `{ "action": "set-shared-context", "value": true\|false }` | | `POST` | `/api/revert` | `{ "iteration": N }` — restore the checkpoint before iteration N | | `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 <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. The command guard adds deny rules for destructive bash patterns on top of them. - `--dry-run` performs no API calls and no file changes (beyond its own `progress.json`); it also skips verify commands and the reviewer pass. - Logs, state and reports are redacted before they are persisted; use `--no-redact` only when you trust the target. `--no-guard` and `--no-checkpoint` trade safety for speed. - `report` and `--json` are read-only. ## 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/verify.mjs independent verify command runner src/goals.mjs goal/plan registry, summaries, verify editing src/controller.mjs run lifecycle (start multiple plans from the dashboard) src/guard.mjs destructive-command guard (pre-execution + runtime) src/checkpoint.mjs per-iteration git checkpoints + revert src/redact.mjs secret redaction for persisted logs/state/reports src/notify.mjs webhook/ntfy/telegram notifications src/reportcli.mjs `kilo-loop report` subcommand src/simulator.mjs --dry-run agent public/ dashboard UI examples/ sample goals test/ node:test unit tests (npm test) ```