feat(dev-cli): add Bun-based local development CLI#1142
feat(dev-cli): add Bun-based local development CLI#1142evanjacobson wants to merge 15 commits intomainfrom
Conversation
…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
| } | ||
|
|
||
| const servicesWithEnv = services.filter(s => s.envFile); | ||
| let allGood = true; |
There was a problem hiding this comment.
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.
| return; | ||
| } | ||
|
|
||
| if (svc.type === 'infra') { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'), ''); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'), ''); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Code Review SummaryStatus: 5 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Fix these issues in Kilo Cloud Other Observations (not in diff)None. Files Reviewed (31 files)
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)
Summary
@kilocode/dev-cli— a Bun-based CLI atdev/cli/that replaces scattered shell scripts with a composable tool for managing the repo's 20+ servicesGeneric commands
Project-specific commands
Architecture
.dev.vars.exampletemplatesTest plan
pnpm kilo --helplists all commands, projects, and 22 servicespnpm kilo statusshows Docker health + port checkspnpm kilo env checkvalidates env vars across all servicespnpm kilo logslists all services with descriptionspnpm kilo kiloclaw badcommandshows error + project help (not silent fallthrough)pnpm kilo up(starts Next.js + infra)pnpm kilo kiloclaw setup(needs Vercel/Fly auth)pnpm kilo app-builder up(needs tmux/ngrok)