Files
kandev/docs/plans/native-kandev-cli/legacy-plan.md
T
2026-08-24 11:09:03 +00:00

11 KiB

spec, created, status
spec created status
docs/specs/cli/requirements/native-kandev-cli.md 2026-06-15 draft

Implementation Plan: Native Kandev CLI

Overview

Refactor the current backend command into a callable backend app, then make apps/backend/cmd/kandev a unified native executable that dispatches public launcher commands by default and hidden backend mode via __backend. Port the existing TypeScript launcher behavior into Go in phases, starting with start so make start stops depending on pnpm/tsx, then run, restart supervision, services, dev, release packaging, and finally the npm shim.


Backend

Backend App Extraction

Files:

  • apps/backend/cmd/kandev/main.go
  • apps/backend/internal/backendapp/app.go (new)

Changes:

  • Move the current server startup logic from cmd/kandev/main.go into internal/backendapp.
  • Expose func Run(args []string) int or equivalent.
  • Use a private flag.FlagSet inside backend mode rather than package-global flag.CommandLine, so launcher parsing and backend parsing cannot conflict.
  • Preserve current backend flags: -port, -log-level, -help, -version.
  • Keep build metadata (Version, Commit, BuildTime) available to both launcher and backend mode.

Reason:

  • A single bin/kandev binary needs to run either launcher code or backend server code without building a second backend artifact.

Unified Entrypoint

Files:

  • apps/backend/cmd/kandev/main.go

Changes:

  • Replace direct backend startup with dispatch:
func main() {
    os.Exit(run(os.Args[1:]))
}

func run(args []string) int {
    if len(args) > 0 && args[0] == "__backend" {
        return backendapp.Run(args[1:])
    }
    return launcher.Run(args)
}
  • Keep __backend hidden from public help.
  • Add tests for dispatch if run can be tested without launching the server.

Reason:

  • The public executable remains kandev, while the launcher can supervise a restartable backend child by spawning itself.

Launcher Package

Files:

  • apps/backend/internal/launcher/cli
  • apps/backend/internal/launcher/ports
  • apps/backend/internal/launcher/paths
  • apps/backend/internal/launcher/env
  • apps/backend/internal/launcher/bundle
  • apps/backend/internal/launcher/health
  • apps/backend/internal/launcher/process
  • apps/backend/internal/launcher/supervisor
  • apps/backend/internal/launcher/service

Changes:

  • Port current behavior from apps/cli/src/args.ts, ports.ts, constants.ts, shared.ts, health.ts, web.ts, process.ts, and supervisor/*.
  • Keep packages narrow to satisfy backend lint limits.
  • Use standard library APIs first:
    • net.Listen("tcp", "127.0.0.1:0") for free ports.
    • net/http for health polling.
    • os/exec for child processes.
    • os/signal and platform-specific process group handling for shutdown.

Reason:

  • This replaces the Node launcher logic while preserving behavior.

start Mode

Files:

  • apps/backend/internal/launcher/start
  • Makefile

Changes:

  • Detect repo root using current findRepoRoot semantics.
  • Resolve local backend by using os.Executable() and spawning <self> __backend.
  • Start backend and wait for health; the backend serves the embedded Vite SPA.
  • Update make start to invoke apps/backend/bin/kandev start.

Reason:

  • This is the quickest user-visible reduction in Node launcher dependency for local production startup.

run Mode

Files:

  • apps/backend/internal/launcher/run
  • apps/backend/internal/launcher/bundle
  • .github/workflows/release.yml

Changes:

  • Validate release bundle layout with one bin/kandev executable.
  • Resolve runtime via KANDEV_BUNDLE_DIR and explicit bundle context.
  • Spawn backend using <bundle>/bin/kandev __backend.
  • Preserve quiet backend log buffering and verbose/debug/headless behavior.
  • Decide separately whether to port --runtime-version; if deferred, return a clear unsupported/deprecated error.

Reason:

  • This replaces the installed runtime launch path used by Homebrew/manual bundles.

Restart Supervisor

Files:

  • apps/backend/internal/launcher/supervisor
  • existing backend restart adapter call sites, if they assume manifest shape

Changes:

  • Port manifest/control/socket behavior from apps/cli/src/supervisor/*.
  • Manifest records:
    • backend_executable: absolute path to bin/kandev
    • argv: ["__backend"]
    • backend env allowlist
    • mode, port, cwd, home dir
  • Restart control restarts only the backend child process.

Reason:

  • Preserves ADR 0019 while avoiding a separate backend binary.

Service Management

Files:

  • apps/backend/internal/launcher/service
  • apps/backend/internal/launcher/service/systemd
  • apps/backend/internal/launcher/service/launchd
  • Makefile service targets

Changes:

  • Port service argument parsing, help, config output, metadata, systemd unit rendering, launchd plist rendering, install/uninstall/status/logs/restart.
  • Generated units execute public kandev.
  • Service launch path starts backend through hidden child mode.
  • Preserve stale-service detection using installed metadata.

Reason:

  • Services are part of the public launcher surface and must stop depending on the Node CLI.

Dev Mode

Files:

  • apps/backend/internal/launcher/dev
  • Makefile

Changes:

  • Port repo-root detection, dev env, production DB backup guard, and task-workspace detection.
  • Prefer spawning <self> __backend with dev env if it preserves current behavior.
  • Keep pnpm --filter @kandev/web dev for the web dev server.
  • Preserve Windows winjob wrapping if still needed.
  • Switch make dev only after dev parity is proven.

Reason:

  • Dev mode is user-facing for contributors but still intentionally depends on pnpm/Node for the web dev server.

Frontend

No web UI changes are required by this spec.


Packaging

Release Bundles

Files:

  • .github/workflows/release.yml
  • scripts/release/package-bundle.sh
  • release packaging scripts for runtime npm packages

Changes:

  • Package one primary native executable:
kandev/
├── bin/kandev
├── bin/agentctl
├── bin/agentctl-linux-amd64
├── bin/agentctl-darwin-arm64
└── bin/agentctl-darwin-amd64
  • Stop requiring dist/kandev/cli for Homebrew; platform bundles contain native bin/ artifacts with web assets embedded in bin/kandev.
  • Add smoke tests for bin/kandev --help, bin/kandev --version, bin/kandev run --headless, and bin/kandev service --help.

npm/npx Shim

Files:

  • apps/cli/bin/cli.js
  • apps/cli/package.json
  • possibly a small apps/cli/src shim test if TypeScript remains

Changes:

  • Replace full launcher implementation with a small JS shim.
  • Shim maps platform/arch to @kdlbs/runtime-*.
  • Shim resolves the runtime package and execs bin/kandev(.exe) with inherited stdio.
  • Remove runtime dependencies from the published npm package; keep old TS launcher dependencies as dev-only test/build dependencies until the legacy source is deleted.

Docs

Files:

  • apps/cli/README_internal.md
  • apps/cli/README.md
  • docs/specs/INDEX.md
  • root/scoped AGENTS.md if package layout guidance changes

Changes:

  • Document that kandev is native for Homebrew/release bundles.
  • Document that npm/npx still uses Node as the package dispatch mechanism.
  • Remove stale references to Homebrew requiring dist/cli.bundle.js.

Tests

  • What: public launcher help hides internal backend mode. File: apps/backend/internal/launcher/cli/*_test.go How: table-driven parser/help test.

  • What: kandev __backend dispatches to backend mode. File: apps/backend/cmd/kandev/*_test.go or apps/backend/internal/backendapp/*_test.go How: unit test around dispatchable function; avoid starting the full server where possible.

  • What: port/env precedence matches current CLI behavior. File: apps/backend/internal/launcher/env/*_test.go, apps/backend/internal/launcher/ports/*_test.go How: table-driven unit tests for flags and env vars.

  • What: start runs the native backend with embedded or filesystem Vite assets. File: apps/backend/internal/launcher/start/*_test.go How: temp directory fixtures for repo roots, apps/web/dist, and embedded-asset fallback.

  • What: bundle validation accepts the single-binary layout. File: apps/backend/internal/launcher/bundle/*_test.go How: temp bundle fixtures with missing-artifact cases.

  • What: health failures surface captured backend output. File: apps/backend/internal/launcher/health/*_test.go or run/*_test.go How: httptest and short-lived helper process.

  • What: supervisor manifest restarts same binary with __backend. File: apps/backend/internal/launcher/supervisor/*_test.go How: golden manifest test and restart child test with helper executable.

  • What: service units execute public kandev. File: apps/backend/internal/launcher/service/systemd/*_test.go, apps/backend/internal/launcher/service/launchd/*_test.go How: golden file tests for rendered unit/plist.

  • What: npm shim resolves runtime package and execs native bin/kandev. File: apps/cli/* How: fixture node_modules/@kdlbs/runtime-* package and mocked child process spawn.


E2E Tests

No browser E2E test is required because the feature has no web UI surface.

Add release/local smoke coverage instead:

  • Scenario: GIVEN a local production build, WHEN make start runs, THEN it uses native apps/backend/bin/kandev start. File: release smoke script or backend integration test. What to verify: process starts and health endpoints become ready.

  • Scenario: GIVEN a bundle fixture, WHEN bin/kandev run --headless runs, THEN backend and web become ready. File: release smoke script. What to verify: health endpoint and ready output.


Implementation Waves

Wave 1: Backend extraction and dispatch

  • Refactor backend app into callable package.
  • Add hidden __backend dispatch.
  • Add launcher skeleton and parser/help tests.

Wave 2: Shared launcher primitives and start

  • Port env, ports, paths, health, browser/process helpers.
  • Implement start.
  • Switch make start.

Wave 3: Installed runtime run

  • Port bundle validation and run launch path.
  • Add same-binary backend spawning.
  • Add bundle smoke tests.

Wave 4: Supervisor and services

  • Port restart supervisor.
  • Port systemd/launchd service management.
  • Add golden tests.

Wave 5: Dev mode

  • Port dev.
  • Switch make dev only after parity is confirmed.

Wave 6: Packaging cutover

  • Update release bundle shape.
  • Convert npm package to thin shim.
  • Remove old Node launcher bundle from Homebrew path.
  • Update docs.

Wave 7: Cleanup and verification

  • Delete obsolete TS launcher modules.
  • Run format, tests, lint, and release smoke tests.

Open Questions

  • Use __backend argv token or KANDEV_INTERNAL_ROLE=backend env for hidden backend mode? Recommendation: __backend.
  • Preserve --runtime-version <tag> in the first Go port, or defer/deprecate it?
  • Ship old Node CLI fallback for one release while Homebrew/npm packaging switches over?