feat: add native kandev launcher (#1391)

This commit is contained in:
Carlos Florêncio
2026-06-20 19:40:15 +01:00
committed by GitHub
parent 2b93545a4d
commit da45983c4c
99 changed files with 5240 additions and 2044 deletions
+155
View File
@@ -0,0 +1,155 @@
---
status: draft
created: 2026-06-15
owner: tbd
---
# Native Kandev CLI
## Why
Users start Kandev from the terminal through `kandev`, but the current launcher is a Node.js/TypeScript program even when Kandev is installed from Homebrew or run from a local production build. This adds a runtime dependency and extra moving parts to the most basic startup path. Users should keep the same `kandev` command while the launcher becomes a native executable.
## What
- `kandev` remains the public command for Homebrew, npm/npx, global npm installs, service units, and local development.
- Homebrew and release bundle installs provide a native `bin/kandev` executable that can launch Kandev without executing the TypeScript CLI bundle.
- The native `kandev` executable supports the public launcher commands users need after the web runtime merge: default run, `run`, `start`, `service`, `--help`, `--version`, `--port`, `--backend-port`, `--verbose`, `--debug`, and `--headless`.
- Native `dev` mode is deferred until it can be ported with parity; the native launcher does not advertise or accept `dev`/`--dev`.
- The native launcher starts the backend as a supervised child process by re-executing the same `bin/kandev` binary in a hidden backend mode.
- The hidden backend mode is not a public command and is not shown in normal help output.
- Backend restarts restart only the backend child process; the launcher/supervisor remains alive unless the shutdown policy requires the whole app to exit.
- npm/npx continues to expose the `kandev` command through the `kandev` npm package. The npm package may use a minimal JavaScript shim, but that shim only resolves the platform runtime package and execs its native `bin/kandev`.
- `make start` launches through the native `apps/backend/bin/kandev start` path after building local artifacts.
- Existing service installs continue to be managed by `kandev service ...`; newly generated service units execute the public `kandev` launcher path.
- Startup output continues to show the URL users should open, the MCP URL, database path when applicable, and log level when applicable.
- Production `run` and `start` do not execute a Node.js web runtime; the Go backend serves the embedded Vite SPA assets.
## API surface
### Public CLI
```text
kandev [--port <port>] [--verbose] [--debug] [--headless]
kandev run [--port <port>] [--verbose] [--debug] [--headless]
kandev start [--port <port>] [--verbose] [--debug] [--headless]
kandev service install|uninstall|start|stop|restart|status|logs|config [--system]
kandev --version
kandev --help
```
Port flags and environment variables:
- `--port` and `--backend-port` select the public backend port.
- `KANDEV_PORT` and `KANDEV_BACKEND_PORT` provide backend port defaults.
- There is no production web port flag or environment variable. The Go backend serves the embedded SPA on the backend port.
- `--runtime-version` is not supported by the native launcher and is rejected as a usage error.
Logging/debug flags:
- `--verbose` shows backend info logs.
- `--debug` shows debug logs and enables current debug environment behavior.
- `--headless` skips browser opening and prints the ready URL.
### Hidden backend mode
The native launcher has a private backend mode:
```text
kandev __backend [backend flags]
```
This mode starts the backend server and is invoked only by the launcher/supervisor. It is intentionally hidden from public help output. Existing backend diagnostic flags may remain available in this mode for tests and direct diagnostics.
### Runtime bundle
Release bundles expose this layout:
```text
kandev/
├── bin/kandev
├── bin/agentctl
└── bin/agentctl-linux-amd64
```
`bin/kandev` is both the public launcher and the hidden backend-mode executable.
### Supervisor manifest
When backend restart supervision is enabled, the launch manifest records the same executable plus hidden backend argv:
```json
{
"version": 1,
"backend_executable": "/absolute/path/to/bin/kandev",
"argv": ["__backend"],
"cwd": "/absolute/path/to/bin",
"env": {
"KANDEV_SERVER_PORT": "38429",
"KANDEV_AGENT_STANDALONE_PORT": "39429",
"KANDEV_RESTART_ADAPTER": "supervisor"
},
"home_dir": "/absolute/path/to/home",
"port": 38429,
"mode": "run",
"created_at": "timestamp"
}
```
## State machine
Launcher process lifecycle:
- `idle`: command parsed but no children started.
- `backend-starting`: launcher has spawned `kandev __backend` and is waiting for backend health.
- `ready`: backend is healthy and serving API, WebSocket, and SPA routes; browser may be opened unless headless.
- `restarting-backend`: supervisor received a restart request and is replacing only the backend child.
- `shutting-down`: launcher is terminating child processes after signal, service stop, or child exit policy.
- `failed`: startup failed and the launcher exits non-zero after surfacing the actionable error.
Transitions:
- `idle` -> `backend-starting`: user runs `kandev`, `kandev run`, `kandev start`, or service unit starts.
- `backend-starting` -> `failed`: backend exits or health timeout expires.
- `backend-starting` -> `ready`: backend health endpoint reports ready.
- `ready` -> `restarting-backend`: backend restart adapter requests restart.
- `restarting-backend` -> `ready`: replacement backend becomes healthy.
- any active state -> `shutting-down`: signal, service stop, or non-restartable child exit.
## Failure modes
- If a required backend artifact is missing in `start` mode, `kandev start` exits non-zero with a message that tells the user to run `make build`.
- If the runtime bundle is missing required files in `run` mode, `kandev run` exits non-zero and names the missing artifact.
- If the backend does not become healthy before timeout, the launcher exits non-zero and includes captured backend output when running in quiet mode.
- If the backend child exits unexpectedly after startup, the launcher either restarts it through the supervisor path when the exit is restart-controlled or shuts down the app with the backend exit code.
- If npm/npx optional runtime resolution fails, the JavaScript shim exits non-zero with an actionable message explaining that the platform runtime package is missing.
## Persistence guarantees
- Service installation metadata remains durable across restarts and upgrades.
- Supervisor manifest/control files remain under the configured Kandev home directory and are overwritten on each launch with the current executable path and backend argv.
- The launcher does not persist child process state beyond the existing supervisor manifest. After a launcher restart, it starts a fresh backend child.
- User data, database files, task worktrees, and backend-managed persistence remain owned by existing backend persistence behavior; the launcher migration does not change their retention.
## Scenarios
- **GIVEN** Kandev is installed from Homebrew, **WHEN** the user runs `kandev --help`, **THEN** the help output describes the public launcher commands and does not show `__backend`.
- **GIVEN** Kandev is installed from Homebrew, **WHEN** the user runs `kandev --version`, **THEN** the command prints the installed Kandev version without executing the Node CLI bundle.
- **GIVEN** a valid release bundle, **WHEN** the user runs `kandev --headless`, **THEN** the native launcher starts `kandev __backend`, waits for the backend to serve API and SPA routes, and prints the backend URL.
- **GIVEN** a local checkout with built backend and web artifacts, **WHEN** the user runs `make start`, **THEN** the Makefile launches through `apps/backend/bin/kandev start` and does not invoke `pnpm -C cli dev -- start`.
- **GIVEN** the backend has requested a restart through the restart adapter, **WHEN** the launcher receives the restart request, **THEN** only the `kandev __backend` child process is replaced.
- **GIVEN** the user presses Ctrl-C while Kandev is running, **WHEN** the launcher handles the signal, **THEN** it terminates the backend child process before exiting.
- **GIVEN** a new service install on Linux or macOS, **WHEN** the user runs `kandev service install`, **THEN** the generated service unit executes the public `kandev` launcher path.
- **GIVEN** a user runs `npx kandev@latest --help`, **WHEN** npm has installed the platform runtime package, **THEN** the npm shim execs the runtime package's native `bin/kandev` and the user sees the same public help output.
## Out of scope
- Making `npx` itself Node-free.
- Introducing `kandevctl` as a user-facing command.
- Requiring a separate shipped backend binary such as `kandev-backend`.
- Changing task, workflow, agent, or integration behavior after the backend has started.
## Open questions
- Should hidden backend mode be selected by the `__backend` argv token or by `KANDEV_INTERNAL_ROLE=backend`? Recommendation: `__backend`.
- Should `--runtime-version <tag>` be ported in a later native launcher implementation or remain deprecated?
- Should one release ship both the old Node CLI bundle and the native `bin/kandev` for compatibility while Homebrew/npm packaging switches over?