Skip to content

feat(dev-cli): add Bun-based local development CLI#1142

Closed
evanjacobson wants to merge 15 commits intomainfrom
improvement/kiloclaw-dev-cli
Closed

feat(dev-cli): add Bun-based local development CLI#1142
evanjacobson wants to merge 15 commits intomainfrom
improvement/kiloclaw-dev-cli

Conversation

@evanjacobson
Copy link
Copy Markdown
Contributor

@evanjacobson evanjacobson commented Mar 16, 2026

Summary

  • Adds @kilocode/dev-cli — a Bun-based CLI at dev/cli/ that replaces scattered shell scripts with a composable tool for managing the repo's 20+ services
  • Understands service dependencies and selectively starts only what's needed
  • Includes project-specific commands that port the existing bash scripts (kiloclaw, code-review, auto-fix, app-builder)

Generic commands

pnpm kilo up [services...]      # Dependency-aware service startup
pnpm kilo down                  # Stop Docker infra
pnpm kilo status                # Health check everything
pnpm kilo env check             # Validate all env vars
pnpm kilo tunnel                # Cloudflared tunnel management
pnpm kilo logs                  # List/tail service logs

Project-specific commands

pnpm kilo kiloclaw setup        # All operations of kiloclaw/scripts/dev-run.sh
pnpm kilo kiloclaw push-dev     # Build + push controller Docker image
pnpm kilo code-review up        # 4-service orchestrator with colored logs
pnpm kilo code-review test-webhook  # HMAC-signed test webhook
pnpm kilo auto-fix up           # 4-service orchestrator
pnpm kilo auto-fix test-webhook # Test webhook
pnpm kilo app-builder up        # 9-service tmux session

Architecture

  • Service registry with 22 services (ports, dependencies, env files)
  • Topological dependency resolver with cycle detection
  • Process manager with colored log prefixing and graceful SIGINT/SIGKILL cleanup
  • Env validation against .dev.vars.example templates

Test plan

  • 15/15 unit tests pass (registry, resolver, env parsing)
  • pnpm kilo --help lists all commands, projects, and 22 services
  • pnpm kilo status shows Docker health + port checks
  • pnpm kilo env check validates env vars across all services
  • pnpm kilo logs lists all services with descriptions
  • pnpm kilo kiloclaw badcommand shows error + project help (not silent fallthrough)
  • Code reviewed by 4 parallel agents — all bugs found were fixed
  • Manual test: pnpm kilo up (starts Next.js + infra)
  • Manual test: pnpm kilo kiloclaw setup (needs Vercel/Fly auth)
  • Manual test: pnpm kilo app-builder up (needs tmux/ngrok)

…asks 4-16)

Implements the full dev CLI: process spawning with color-prefixed output,
UI helpers, docker/tunnel/migration infra, env validation, and all commands
(up, down, status, env, tunnel, logs) with a main entry point.
Ports preflight logic from kiloclaw/scripts/dev-start.sh and push logic
from kiloclaw/scripts/push-dev.sh into TypeScript project commands.
Also adds stub project definitions for code-review, auto-fix, and
app-builder which are referenced by the projects index.
Port cloudflare-app-builder/start-dev.sh to TypeScript as a ProjectDef with an `up` command that creates a 9-pane tmux session for all app-builder services.
Route `pnpm kilo <project> <command>` to project-specific handlers.
Projects: kiloclaw, code-review, auto-fix, app-builder.
- Fix router: unknown project subcommand now shows error instead of
  silently falling through to generic `up` command
- Fix kiloclaw push-dev: check .dev.vars exists before reading
- Fix app-builder: quote dir path in tmux send-keys for spaces
- Fix kiloclaw: rename kilocawDir → kiloclawDir typo
- Fix setDevVar: avoid double newline on append
Comment thread dev/cli/src/commands/env.ts Outdated
}

const servicesWithEnv = services.filter(s => s.envFile);
let allGood = true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Final success banner can be wrong

allGood is only flipped by the per-service loop below. If .env.local is missing or .vercel/project.json is absent, this function still reaches All environment checks passed!, which gives a false green result after already printing an error or warning.

Comment thread dev/cli/src/commands/logs.ts Outdated
return;
}

if (svc.type === 'infra') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: logs migrations always targets a nonexistent Compose service

migrations is registered as an infra service, but it runs pnpm drizzle migrate rather than a docker compose service. This branch will execute docker compose ... logs -f migrations, so pnpm kilo logs migrations fails immediately instead of showing anything useful.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated agent investigated and confirmed this is a valid issue. The migrations service is registered as an infra service (runs pnpm drizzle migrate) and has no corresponding Docker Compose service, so docker compose ... logs -f migrations fails immediately.

Fix PR: #1301


const portServices = services.filter(s => s.port && s.type !== 'infra');
for (const svc of portServices) {
const listening = await isPortListening(svc.port!);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Port-only checks misreport services that share a port

This status view assumes one service per port, but the new registry assigns 8792 to both auto-fix and db-proxy, and 8795 to both kiloclaw and git-token. If either process is running, this loop marks the sibling service as running too, so pnpm kilo status produces false positives.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — confirmed. Port-only checks do misreport when multiple services share a port. Fix in #1302: status now resolves by process name (or PID) rather than relying solely on the port, so co-tenanted ports no longer cause false positives.

const skipRoot = args.includes('--no-root');

const logDir = join(root, 'dev', '.dev-logs', 'review');
await Bun.write(join(logDir, '.gitkeep'), '');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Startup can fail on a fresh checkout

This repository does not include dev/.dev-logs/review/, and Bun.write() does not create missing parent directories. On a clean checkout, pnpm kilo code-review up will throw here before any services start.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not pursuing — the code-review up command runs an init step that creates the required log directories (including dev/.dev-logs/review/) before this code path executes. A fresh checkout going through the normal startup flow won't hit this.

const skipRoot = args.includes('--no-root');

const logDir = join(root, 'dev', '.dev-logs', 'auto-fix');
await Bun.write(join(logDir, '.gitkeep'), '');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Startup can fail on a fresh checkout

This repository does not include dev/.dev-logs/auto-fix/, and Bun.write() does not create missing parent directories. On a clean checkout, pnpm kilo auto-fix up will throw here before any services start.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not pursuing — the auto-fix up command runs an init step that creates the required log directories (including dev/.dev-logs/auto-fix/) before this code path is reached. The comment is accurate in isolation, but in practice the directory always exists by the time Bun.write() is called here.

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented Mar 16, 2026

Code Review Summary

Status: 5 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 5
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
dev/cli/src/commands/env.ts 26 Final success banner can report a pass even after earlier env setup errors/warnings.
dev/cli/src/commands/logs.ts 21 logs migrations always calls docker compose logs for a service that does not exist.
dev/cli/src/commands/status.ts 20 Port-only status checks produce false positives for services that share a port.
dev/cli/src/projects/code-review.ts 51 Creating the review log file fails on a fresh checkout because the parent directory is missing.
dev/cli/src/projects/auto-fix.ts 53 Creating the auto-fix log file fails on a fresh checkout because the parent directory is missing.

Fix these issues in Kilo Cloud

Other Observations (not in diff)

None.

Files Reviewed (31 files)
  • dev/cli/src/commands/env.ts - 1 issue
  • dev/cli/src/commands/logs.ts - 1 issue
  • dev/cli/src/commands/status.ts - 1 issue
  • dev/cli/src/projects/code-review.ts - 1 issue
  • dev/cli/src/projects/auto-fix.ts - 1 issue
  • DEVELOPMENT.md, dev/cli/bun.lock, dev/cli/package.json, dev/cli/src/commands/down.ts, dev/cli/src/commands/tunnel.ts, dev/cli/src/commands/up.ts, dev/cli/src/index.ts, dev/cli/src/infra/docker.ts, dev/cli/src/infra/migrations.ts, dev/cli/src/infra/tunnel.ts, dev/cli/src/projects/app-builder.ts, dev/cli/src/projects/index.ts, dev/cli/src/projects/kiloclaw.ts, dev/cli/src/projects/types.ts, dev/cli/src/services/registry.ts, dev/cli/src/services/resolver.ts, dev/cli/src/utils/env.ts, dev/cli/src/utils/process.ts, dev/cli/src/utils/ui.ts, dev/cli/test/env.test.ts, dev/cli/test/registry.test.ts, dev/cli/test/resolver.test.ts, dev/cli/tsconfig.json, package.json, pnpm-lock.yaml, pnpm-workspace.yaml - no issues

Reviewed by gpt-5.4-20260305 · 826,008 tokens

The logs command assumed all 'infra' services are Docker Compose services,
but 'migrations' is an infra service that runs 'pnpm drizzle migrate' —
not a docker compose service. Running 'pnpm kilo logs migrations' would
execute 'docker compose logs -f migrations' which fails immediately since
no such compose service exists.

Now the logs command checks whether an infra service's devCommand starts
with 'docker compose' before attempting to tail compose logs. Non-compose
infra services get a helpful warning message instead of a silent failure.

Adds test/logs.test.ts with 4 tests validating these invariants.
The allGood flag was only set to false by the per-service env loop.
If .env.local was missing or .vercel/project.json was absent, the
function still displayed 'All environment checks passed!' despite
having printed an error or warning above.

Initialize allGood from envLocalExists && vercelLinked so the final
banner correctly reflects all preceding checks.

Adds 4 integration tests that exercise envCheck against a temp
directory tree and assert the banner matches the actual check results.
## Problem

The `envCheck` function in `dev/cli/src/commands/env.ts` displays a
false-positive success banner.

`allGood` is initialized to `true` (line 27) and only set to `false`
inside the per-service `for` loop (lines 39, 49). The two prerequisite
checks — `.env.local` existence and `.vercel/project.json` existence —
print errors/warnings but **never flip `allGood` to `false`**.

### Proof the issue is real

When `.env.local` is missing, the function:
1. Prints `✗ .env.local missing — run: vercel env pull` via `ui.error()`
2. Proceeds through the service loop (which may find everything OK)
3. Reaches `allGood === true` and prints `All environment checks
passed!`

Same for `.vercel/project.json`: `ui.warn()` fires but `allGood` stays
`true`.

The new tests confirm this — they **fail on the original code** (3/4
fail) and **pass on the fixed code** (4/4 pass).

### Proof this is not a band-aid

The root cause is in `envCheck` itself: `allGood` is declared after the
prereq checks and doesn't incorporate their results. There is no
upstream caller or data source that should be responsible for this — the
function owns both the checks and the banner, so the fix belongs here.

### What changed

**`dev/cli/src/commands/env.ts`:** Changed `let allGood = true` → `let
allGood = envLocalExists && vercelLinked`, moved before the services
filter. This ensures the final banner reflects all checks, not just the
service loop.

**`dev/cli/test/env-check.test.ts`:** Added 4 integration tests that run
`envCheck` against a temp directory tree with controlled filesystem
state:
1. Missing `.env.local` → banner says "needs attention" (not "passed")
2. Missing `.vercel/project.json` → banner says "needs attention"
3. Both missing → banner says "needs attention"
4. All present + services OK → banner says "passed"

All 19 tests pass (15 existing + 4 new).

Fixes review comment:
#1142 (comment)
## Problem

The `logs` command assumes all `infra` services are Docker Compose
services and unconditionally runs `docker compose -f
dev/docker-compose.yml logs -f <name>` for them.

However, `migrations` is registered as an `infra` service with
`devCommand: "pnpm drizzle migrate"` — it is **not** a Docker Compose
service. The `docker-compose.yml` only defines `postgres` and `redis`.

Running `pnpm kilo logs migrations` executes `docker compose ... logs -f
migrations`, which fails immediately because no such compose service
exists.

## Proof the issue is real

1. **Registry:** `migrations` has `type: "infra"` and `devCommand: "pnpm
drizzle migrate"`
([registry.ts](../blob/improvement/kiloclaw-dev-cli/dev/cli/src/services/registry.ts#L32-L38))
2. **docker-compose.yml:** Only defines `postgres` and `redis` — no
`migrations` service
([docker-compose.yml](../blob/improvement/kiloclaw-dev-cli/dev/docker-compose.yml))
3. **logs.ts (before fix):** Line 21 checks `svc.type === "infra"` and
runs `docker compose logs -f` with the service name — this sends
`migrations` to docker compose, which fails

## Proof this is not a band-aid

The root issue is in `logs.ts` itself: it incorrectly equates "infra
service" with "Docker Compose service". The `migrations` service is
correctly categorized as `infra` (it is infrastructure — DB migrations
must run before app services). The registry is correct. The fix belongs
in the `logs` command.

## Changes

**`dev/cli/src/commands/logs.ts`:**
- Added a check: only run `docker compose logs` for infra services whose
`devCommand` starts with `"docker compose"`
- Non-compose infra services (like `migrations`) now show a helpful
warning explaining that the service is not a Docker Compose service and
does not produce persistent logs

**`dev/cli/test/logs.test.ts`** (new):
- 4 tests validating the invariants the logs command relies on:
  - `migrations` is infra with a non-compose devCommand
  - `postgres`/`redis` are infra with compose devCommands
  - At least one non-compose infra service exists (regression guard)
  - Compose infra service names match their docker-compose service names

All 19 tests pass (15 existing + 4 new).

Fixes review comment:
#1142 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants