Commit Graph

109 Commits

Author SHA1 Message Date
Carlos Florêncio 1484ae7d28 fix: expose user agent bins to system service (#1292) 2026-06-07 21:13:28 +01:00
github-actions[bot] 2557b69c0e release: 0.56.0 2026-06-06 20:43:33 +00:00
github-actions[bot] d2ab3cd509 release: 0.55.0 2026-06-02 23:11:24 +00:00
Carlos Florêncio 6b98185ac3 fix(web): restore debug UI on make start-debug (#1252) 2026-06-02 21:48:23 +01:00
José Almeida 041a19f060 feat: service-managed UI self-update (#1210)
* feat: add service-managed self-update

* chore: add temporary self-update test scripts

* chore: simplify temporary script markdown fences

* chore: run frontend in temporary self-update scripts

* fix: show self-update job progress after apply

* chore: add real npm self-update test script

* fix: isolate npm self-update prefix

* fix: detect npm service installs from bin shim

* chore: reset service before real npm update test

* chore: expand real npm status diagnostics

* fix: preserve service home during self-update

* fix(cli): tee self-update helper output to a log file

* fix(cli): avoid launchd bootstrap race when reloading the macOS service

* fix(backend): run macOS self-update helper once via transient launchd plist

* fix(web): show self-update progress and lock the apply button until the update lands

* fix(cli): reinstall via the kandev wrapper on Homebrew self-update

* chore: remove temp self-update test scripts

* fix(web): drop unused apply path from useUpdates hook

* fix(backend): snapshot install state once in apply preflight

* docs: align self-update docs with transient LaunchAgent flow

* chore(release): run cli bundle from apps workspace root

* fix(backend): guard against concurrent self-update applies

* fix(backend): check scheme and helper paths in self-update apply

* fix(backend): drop duplicate binary name from npx manual command

* fix(cli): allow --help for service self-update without --intent

* fix(cli): harden self-update dir perms and darwin uid resolution

* fix(web): set required POST fields last in applyUpdate

* docs: use JSON body notation for the apply endpoint

* fix(backend): expire self-update guard so a stuck helper cannot wedge apply

* refactor(cli): collapse redundant writeSync branch in self-update log
2026-06-01 17:49:15 +01:00
github-actions[bot] 7b2d503722 release: 0.54.0 2026-05-31 21:35:11 +00:00
Carlos Florêncio b16b27b3e3 fix(cli): generate brew-upgrade-resilient systemd unit (#1180) 2026-05-31 11:14:01 +01:00
Carlos Florêncio 0966b71f1c fix(cli): add ~/.bun/bin to service PATH (#1175) 2026-05-30 19:17:19 +01:00
github-actions[bot] 1b5454153c release: 0.53.0 2026-05-29 01:41:11 +00:00
Carlos Florêncio f4be0d279c fix: dockview, sprite, auth, and CLI db backup improvements (#1075) 2026-05-28 16:23:17 +01:00
github-actions[bot] 97c5b9dc17 release: 0.52.0 2026-05-25 21:27:20 +00:00
Carlos Florêncio 372779ad32 feat(preview): inspect mode with pin and area annotations (#917) 2026-05-24 11:21:50 +01:00
github-actions[bot] 3702056578 release: 0.51.0 2026-05-22 20:11:28 +00:00
Nicolas Lenepveu 722cd31489 fix(cli): include ~/.local/bin in service unit PATH for user-mode installs (#994)
User-mode systemd units and launchd agents now prepend the loading user's
~/.local/bin to PATH, so user-installed agent CLIs (claude, codex, etc.,
installed via npm user prefix, pipx, fnm/nvm) are discoverable by the
kandev daemon. Without this they appeared as `not_installed` even though
they were on the user's interactive PATH.

System-mode units keep the original system-only PATH.
2026-05-21 23:34:20 +01:00
João Salavisa 8a4ac6c570 fix(cli): include node bin dir in service unit PATH for fnm/nvm/asdf/volta/mise (#1000)
* fix(cli): prepend node bin dir to service unit PATH so npx-based agents resolve

systemd/launchd units hard-coded a system-only PATH. Under per-user node
managers (fnm, nvm, asdf, volta, mise), node lives in a versioned
subdirectory like ~/.local/share/fnm/node-versions/v24.14.0/installation/bin,
so the daemon could exec node (ExecStart resolves the absolute path) but
could not exec npx — every ACP agent built with `npx -y <bridge-pkg>`
(claude, codex, opencode, …) failed at spawn time with "peer disconnected
before response".

`captureLauncher()` already records the node binary path. Its parent dir
is exactly where the matching npm/npx live, so prepend it to the unit's
PATH (with dedup so /usr/local/bin etc. don't appear twice).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(cli): guard pathWithNodeBinDir against non-POSIX nodePath

`path.dirname` returns `'.'` on POSIX when given a path with no `/`
separator (e.g. a Windows-style `C:\…\node.exe`). Prepending `.` to a
service unit's PATH would put CWD ahead of system dirs in the daemon
context — a latent privilege-escalation footgun if any future caller
ever passed a relative or bare-filename nodePath. Production is safe
because `captureLauncher()` uses `process.execPath` (always absolute on
Linux/macOS), so the guard is defensive, not a behavior change.

Adds regression tests for both systemd and launchd, plus a previously
missing launchd system-mode + versioned-bin-dir test.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:35:07 +01:00
github-actions[bot] a00d56412c release: 0.50.0 2026-05-19 22:51:13 +00:00
Carlos Florêncio c62739d3c1 feat(cli): kandev service install for systemd and launchd (#926) 2026-05-17 21:12:20 +01:00
Carlos Florêncio fd433626ae fix(cli): unblock make dev on WSL2 mirrored networking (#924) 2026-05-17 12:29:44 +01:00
Carlos Florêncio 0b2085688b feat(office): autonomous agent management layer (#914) 2026-05-16 20:07:57 +01:00
github-actions[bot] 4a517427ba release: 0.49.0 2026-05-16 14:36:34 +00:00
github-actions[bot] 11f468be3d release: 0.48.0 2026-05-15 20:09:04 +00:00
github-actions[bot] de9bbb210d release: 0.47.0 2026-05-15 19:17:12 +00:00
github-actions[bot] c55c01c62f release: 0.46.0 2026-05-15 16:52:23 +00:00
Carlos Florêncio a5108de926 fix(windows): make conpty Close idempotent and kill orphan agentctl (#900) 2026-05-15 16:04:35 +01:00
github-actions[bot] c72080e133 release: 0.45.0 2026-05-12 18:21:01 +00:00
github-actions[bot] ed056a22b5 release: 0.44.0 2026-05-11 21:59:51 +00:00
github-actions[bot] a86085f2d9 release: 0.43.0 2026-05-11 19:46:27 +00:00
Carlos Florêncio 71ee104178 fix(cli): skip NEXT_PUBLIC_KANDEV_API_PORT in production single-port mode (#872) 2026-05-11 20:32:51 +01:00
github-actions[bot] b0e27c3750 release: 0.42.0 2026-05-11 17:55:16 +00:00
José Almeida d031ba9824 fix(ci): harden release package publishing (#862)
* fix: pin pnpm for Docker release builds

* test: harden release pnpm version guard

* test: improve release guard diagnostics

* test: cover named pnpm setup steps

* fix: make npm release publishing rerunnable
2026-05-11 09:27:39 +01:00
github-actions[bot] deb12d31b2 release: 0.41.0 2026-05-09 11:30:38 +00:00
github-actions[bot] 14e80c8904 release: 0.40.0 2026-05-08 09:16:14 +00:00
github-actions[bot] ec1e4a8d61 release: 0.39.2 2026-05-05 00:30:49 +00:00
José Almeida e8780d54bb fix(release): add repository field for npm provenance, patch CLI perms (#826)
* fix(release): add repository field for npm provenance

The publish-npm step in release run 25350606233 (v0.39.1) failed for all
6 packages with:

  npm error code E422
  npm error 422 Unprocessable Entity
  Error verifying sigstore provenance bundle: Failed to validate
  repository information: package.json: "repository.url" is "",
  expected to match "https://github.com/kdlbs/kandev" from provenance

We added `--provenance` to `npm publish` earlier so the registry attaches
a sigstore attestation linking each tarball to its source repo + workflow
run. The attestation is signed using info from the OIDC token (which
includes the repo). At publish time, npm cross-checks the package.json's
`repository.url` against the OIDC repo claim — if the package.json
doesn't have one, or it doesn't match, the publish is rejected.

Both the runtime template (package-npm-runtime.sh) and the main
apps/cli/package.json were missing `repository`. Adding `repository` +
`homepage` to both. The runtime template gets it inline via the
heredoc; the main package.json gets static fields.

This fix unblocks the next release. v0.39.1 itself can't be republished
(version conflict on npm if it had landed; on GitHub release it already
exists). The next release will be v0.39.2 and will go all the way through.

Side state notes:
- v0.39.1 git tag + GitHub release exist with all 5 platform tarballs.
- Homebrew tap was updated to v0.39.1 — `brew install` works as v0.39.1.
- npm has nothing at v0.39.1 (all 6 publishes 422'd).
- v0.39.2 will replace the npm side; Homebrew formula will move forward;
  v0.39.1 GitHub release stays as a historical artifact.

* fix(release): re-chmod cli.js after artifact download (lost in zip roundtrip)

`brew install kdlbs/kandev/kandev` followed by `kandev` failed with:

  /opt/homebrew/bin/kandev: line 2:
  /opt/homebrew/Cellar/kandev/0.39.1/libexec/cli/bin/cli.js: Permission denied
  cannot execute: Undefined error: 0

Inspected the published v0.39.1 macos-arm64 tarball:

  -rwxr-xr-x  kandev/bin/kandev          (755 — executable, correct)
  -rwxr-xr-x  kandev/bin/agentctl        (755 — executable, correct)
  -rw-r--r--  kandev/cli/bin/cli.js      (644 — NOT executable)

Root cause: `actions/upload-artifact@v4` + `actions/download-artifact@v4`
zip-encode the artifact during upload and extract during download.
**Executable bits do not survive that round-trip.** The Go binaries
(kandev, agentctl) keep their +x because they're built inside the same
build-bundles job (no artifact crossing); cli.js is built in build-cli,
uploaded as an artifact, downloaded in build-bundles, and emerges as 644.
The chmod +x in package-cli.sh runs before upload, so it's effectively
erased.

Fix: re-apply chmod +x to dist/kandev/cli/bin/cli.js right before
tarballing, in the Package bundle step (which runs in build-bundles
after the artifact download).

Could not have been caught by the Tier 1.1 local smoke test we did
before merging — that test cp -R'd the cli/ dir directly without an
artifact roundtrip, so the chmod survived. This bug only manifests in
CI's matrix builds.

Manual recovery for v0.39.1 brew users:
  chmod +x /opt/homebrew/Cellar/kandev/0.39.1/libexec/cli/bin/cli.js

Or wait for v0.39.2 and `brew upgrade kandev`.
2026-05-05 01:27:17 +01:00
github-actions[bot] 252c874c3e release: 0.39.1 2026-05-05 00:11:29 +00:00
José Almeida 379230892b feat: brew install + npm sibling channels via OIDC trusted publishing (#806)
* chore: ignore local secret file

* feat(cli): replace self-update with package-manager-owned runtime resolution

The CLI no longer downloads runtime bundles from GitHub on first run. Instead:

- A new resolveRuntime() in apps/cli/src/runtime.ts resolves the bundle in
  three steps: KANDEV_BUNDLE_DIR env var (Homebrew wrapper, tests) → installed
  @kdlbs/runtime-{platform} npm package → cached download (only when
  --runtime-version is given explicitly).
- apps/cli/src/run.ts is split: launch logic separated from download logic.
  The download path stays for the explicit --runtime-version flag only.
- The --version flag now prints CLI semver (was: runtime tag selector). The
  old behavior moves to --runtime-version, documented as advanced/debug.
- The npm self-update prompt (apps/cli/src/update.ts) is removed entirely.
- apps/cli/package.json declares optionalDependencies for five
  @kdlbs/runtime-{platform} packages and engines.npm: ">=7" (required for
  optional-dep resolution during npx).
- Adds esbuild + a "bundle" script that produces dist/cli.bundle.js — a
  single-file CJS bundle with all Node deps inlined. The Homebrew formula
  uses this; the npm package keeps the unbundled dist/cli.js.

Tests: 130/130 passing. Bundle smoke-tested in a directory with no
node_modules to confirm tree-kill is correctly inlined.

* feat(release): unified vX.Y.Z release flow with brew + npm sibling channels

GitHub release artifacts become the canonical build output. npm and Homebrew
are independent sibling channels — both consume the same GitHub release
tarballs; neither depends on the other.

Workflow changes (.github/workflows/release.yml):

- Trigger is now workflow_dispatch only (maintainer-only, gated by repo write
  access). Inputs: bump dropdown (patch/minor/major) + dry_run checkbox.
- Single job graph orchestrates everything in one run:
  prepare (bump, PR, merge, tag) → build-web/build-cli → build-bundles (5
  platforms) → docker → publish-release → publish-npm + update-homebrew-tap.
- The prepare job inlines version bump, optionalDependencies sync, CHANGELOG
  generation, PR create + squash-merge, and tag push. Replaces what
  release-pr.sh used to do locally.
- Tag-push trigger removed; everything runs from the dispatch flow.

New scripts (called by the workflow):

- package-cli.sh: builds + esbuild-bundles the CLI for inclusion in release
  bundles (cli/bin/cli.js + cli/dist/cli.bundle.js + package.json).
- package-npm-runtime.sh: generates per-platform @kdlbs/runtime-* npm
  packages from release bundles. Sets npm os/cpu fields so npm 7+ skips
  non-matching platforms during npx kandev install.
- publish-npm.sh: verifies GitHub assets exist, downloads them, repackages,
  publishes all 5 @kdlbs/runtime-* packages + main kandev@X.Y.Z.
- update-homebrew-tap.sh: clones kdlbs/homebrew-kandev via SSH using the
  HOMEBREW_TAP_DEPLOY_KEY repo secret, generates Formula/kandev.rb pointing
  at the GitHub release assets, pushes directly to main.

Removed scripts:

- release.sh and release-pr.sh — both are redundant with the new workflow.

Required CI secrets:
- NPM_TOKEN: granular access token with publish access to kandev + @kdlbs/*
- HOMEBREW_TAP_DEPLOY_KEY: SSH private key for kdlbs/homebrew-kandev

* docs: brew install, runtime resolution, and GHA-driven release flow

- README.md: add Homebrew quick start (brew install kdlbs/kandev/kandev),
  update NPX docs to require npm 7+, document the three update paths.
- docs/cli.md: rewrite architecture diagram to show three-step runtime
  resolution. Replace --version <tag> docs with --version (CLI semver) +
  --runtime-version <tag> (advanced/debug). Remove KANDEV_NO_UPDATE_PROMPT.
- apps/cli/README.md: add Homebrew install path; document
  package-manager-owned updates.
- apps/cli/README_internal.md: rewrite to describe the npm package vs
  GitHub bundle artifact-shape distinction and the three-step resolver.
  Document the GHA-driven release UI (Run workflow + bump dropdown).
- AGENTS.md: add Release & Versioning Convention section describing the
  unified single-SemVer flow, the sibling channel model, and the GHA flow.

* fix(release): release-bundle bin/cli.js must require the esbuild bundle

The previous package-cli.sh copied apps/cli/bin/cli.js verbatim into the
release bundle. That file requires `../dist/cli.js` (unbundled), which
only works when node_modules is present alongside it.

In the GitHub release bundle (used by Homebrew and manual installs),
there is no node_modules — `require('tree-kill')` and `require('tar')`
inside dist/cli.js would crash at startup.

Now package-cli.sh writes a release-specific bin/cli.js that requires
`../dist/cli.bundle.js` instead. The bundled file has all Node deps
inlined via esbuild, so it runs standalone in the Cellar.

The npm package keeps its own bin/cli.js (in apps/cli/bin/) requiring
the unbundled dist/cli.js, since npm installs tree-kill etc. normally.

Caught while smoke-testing the release bundle locally with
KANDEV_BUNDLE_DIR — backend + web boot succeeded after this fix.

* feat(release): switch npm publishing to Trusted Publishers (OIDC)

The previous flow required an NPM_TOKEN secret. Local testing with that
token failed with EOTP because the granular access token still required a
2FA OTP on publish despite the account-level setting change. npm now
recommends Trusted Publishers (OIDC) over long-lived tokens.

Workflow change (release.yml):
- publish-npm job declares `permissions: id-token: write` so GitHub Actions
  can mint OIDC tokens.
- Removes NODE_AUTH_TOKEN env var. The npm CLI auto-detects the OIDC
  environment and exchanges the GitHub OIDC token for a short-lived
  npmjs.com publish credential.
- Comment block updated to reflect the new auth model. NPM_TOKEN is no
  longer listed in required secrets (already deleted from repo secrets).

Script change (publish-npm.sh):
- Updates `npm publish` calls to include `--provenance` so each published
  package ships with a signed attestation linking it back to this commit
  and workflow run.
- Header comment explains the OIDC auth model.

Setup required on npmjs.com (per package, before first OIDC publish):
For each of the 6 packages — kandev plus @kdlbs/runtime-{linux-x64,
linux-arm64,darwin-x64,darwin-arm64,win32-x64} — go to the package's
"Settings → Publishing access" and add a Trusted Publisher pointing at
this repo (kdlbs/kandev), workflow filename release.yml, and (optionally)
an environment.

* fix(release): pin optionalDependencies to bootstrap version in committed source

CI was failing because apps/cli/package.json declared optionalDependencies
at 0.16.0 but only 0.0.0-bootstrap exists on npm for those packages. The
lockfile couldn't resolve, and `pnpm install --frozen-lockfile` rejected
the working tree.

Pin optionalDependencies to 0.0.0-bootstrap in the committed source so the
lockfile resolves cleanly. The release workflow now bumps them to the real
version inside publish-npm.sh, just before publishing main kandev — at
which point the matching @kdlbs/runtime-*@VERSION tarballs have already
been published earlier in the same step. This avoids the chicken-and-egg
where the prepare job tried to bump optional deps to a version that didn't
yet exist on npm.

Workflow changes:
- prepare job: only bumps the main package version, not optional deps.
- publish-npm.sh: rewrites optionalDependencies to $VERSION right before
  the main `npm publish`, after the 5 runtime packages have been published.

The published kandev@X.Y.Z still has its optionalDependencies pinned to
matching @kdlbs/runtime-*@X.Y.Z. Only the local source uses the bootstrap
pin for development convenience.

* fix: address PR review comments on release flow + runtime resolution

PR reviewers (Greptile, Claude, CodeRabbit) flagged six concrete issues.
Each is addressed below.

Critical:
- publish-npm.sh: previously exited 0 even when runtime packages failed to
  publish, which would leave kandev@VERSION on the registry pointing at
  @kdlbs/runtime-*@VERSION that never got published. Now: capture full npm
  output on failure, distinguish "already published" (idempotent skip)
  from real failures, abort before publishing main kandev if any runtime
  fails.
- release.yml: build-bundles referenced needs.prepare.outputs.tag without
  declaring prepare in its needs array. Static analysis flagged this.
  Added prepare to the needs list.

Major:
- update-homebrew-tap.sh: the formula's write_env_script only injected
  KANDEV_BUNDLE_DIR. run.ts:174 reads KANDEV_VERSION to display the
  running version in startup logs; without it Homebrew users would see
  "release: (env)" instead of "release: 0.17.0". Pass KANDEV_VERSION:
  version.to_s through the wrapper.
- runtime.ts: the catch {} block silently swallowed validateBundle errors,
  hiding "package installed but corrupt" cases behind the generic
  "no runtime found" message. Split the lookup so MODULE_NOT_FOUND falls
  through, but validation errors propagate.
- run.ts: the --runtime-version code path skipped validateBundle, missing
  the file-existence checks that the npm/env paths get. Added it.

Minor:
- release.yml: workflow-level permissions defaulted to write for everyone.
  Tightened to read-only at the workflow level; jobs that need writes
  (prepare, docker, publish-release) declare them explicitly.
- args.test.ts: added missing tests for --runtime-version with no value
  and --runtime-version followed by another flag, parallel to the
  existing --port tests.

Tests: 132 passing (was 130; +2 new).

* fix: address second-round PR review (cleanup traps, host-key pinning, supply chain)

Latest review (Claude + Greptile) flagged three security/cleanup items
on top of the previous fix commit. All three addressed below.

publish-npm.sh:
- Add `trap 'rm -rf "$WORK_DIR"' EXIT` immediately after `mktemp -d` so
  the tmpdir is cleaned up even when `pnpm build` or `npm publish` fails
  partway through (set -e would otherwise abort before the explicit
  rm at the end).
- Remove redundant `pnpm build` before `npm publish` — the `prepublishOnly`
  script in package.json already runs the build.

update-homebrew-tap.sh:
- Combine `cleanup_ssh` + WORK_DIR cleanup into one EXIT trap so `git clone`
  / `git push` failures don't leak the tmpdir.
- Pin GitHub's published SSH host keys (ed25519, ECDSA, RSA) into a temp
  `known_hosts` file instead of using `accept-new` + `/dev/null`. This
  closes the "trust any host key on first connect" gap. The pinned keys
  come from https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints.

release.yml:
- Replace the curl|tar git-cliff install with `orhun/git-cliff-action@v4`
  (already used in publish-release). The action pins to a digest, so we
  no longer need to verify a tarball checksum manually. Removes a
  supply-chain attack surface where a tampered git-cliff binary could run
  during `prepare` (which has contents:write + pull-requests:write).

* revert: keep curl install for git-cliff, add sha512 verification instead

The previous commit (18b2a5c) replaced our `curl | tar` install with
orhun/git-cliff-action@v4 under the banner "supply chain". On reflection
that swap doesn't actually improve anything:

- @v4 is a moving tag, not a digest pin — same trust as a tagged curl URL.
- Both depend on trusting the same maintainer (orhun).
- The action adds a third-party step we don't audit.

The legitimate concern from the review was "no integrity verification on
the downloaded tarball." Address that directly: pin the asset version,
embed its published sha512, and abort if the download doesn't match.
The hash for v2.6.1's x86_64-linux-gnu tarball came from
https://github.com/orhun/git-cliff/releases/download/v2.6.1/<asset>.sha512.

This restores the original setup with a real integrity check and one
fewer external dependency.

* fix: address remaining PR nits (env restore, sha256 download dir)

runtime.test.ts: drop the redundant `delete process.env.KANDEV_BUNDLE_DIR`
in afterEach — `process.env = { ...origEnv }` already restores the
original environment. The redundant delete would silently un-set the var
if a developer had it in their shell, leaking test side effects across
suites.

update-homebrew-tap.sh: fetch_sha256 now downloads into $WORK_DIR instead
of /tmp/. The EXIT trap covers WORK_DIR, so the .sha256 files are cleaned
up automatically. Avoids leaking 5 tmpfiles per release run on long-lived
runners.

* fix: address third-round PR comments

- update-homebrew-tap.sh: Homebrew formula was calling
  `bin.write_env_script` on the bin directory with target `cli/bin/cli.js`,
  which would create the wrapper at `$HOMEBREW_PREFIX/bin/cli.js` (named
  after the target's basename) instead of `$HOMEBREW_PREFIX/bin/kandev`.
  Result: `brew install kandev` would have produced `command not found`.
  Use `(bin/"kandev").write_env_script(...)` to name the wrapper
  explicitly. Caught by Claude as a P0 blocker.

- publish-npm.sh: main `kandev` publish lacked the EPUBLISHCONFLICT
  idempotency handling that the runtime-package loop has. If publish-npm
  was re-run after a partial failure (e.g. the runtime packages succeeded
  but the main package failed and was then manually published), the
  re-run would fail on the main package even though the version was
  already on the registry. Now: capture output, treat
  "already published" as success, fail loudly with full output on real
  errors. Caught by Greptile as P1.

* refactor(release): direct push to main instead of PR-and-merge dance

The prepare job was opening a release branch, creating a PR, and
immediately squash-merging it via gh CLI. That pattern was carried over
from the old release-pr.sh, but in this workflow it earns nothing:

- main has no branch protection, so direct push is allowed.
- The PR was force-merged in the same step (no `--auto`, no waiting for
  required checks), so it provided no pre-merge validation.
- Discoverability is fine without a PR — git tag, GitHub release, workflow
  run, and main commit are all visible.

The PR-and-merge sequence had ~5 GitHub API roundtrips, each a potential
failure point, plus added 30+ lines of YAML and required pull-requests:
write on the job. Replaced with a straight-line commit + tag + push:

  git fetch origin main && git checkout main && git reset --hard origin/main
  ... bump pkg.json, regen CHANGELOG, npm install ...
  git add ... && git commit && git tag -a && git push origin main "$TAG"

Job permissions tightened to contents: write only.

Race condition note: if main advances between fetch and push, the push
fails non-fast-forward — same outcome as the old PR pattern, where the
auto-merge would have failed. For workflow_dispatch releases with the
maintainer actively driving, the race is essentially never going to
happen, and the recovery (re-run the workflow) is the same.

The dry-run path still bumps + regenerates CHANGELOG inside the runner
so the maintainer can verify the computed values look right, just
without committing or pushing anything.

* chore(release): drop verbose comment about direct-push rationale

* docs: drop 'no first-run download' note from user-facing docs

The phrasing only makes sense to readers who knew the previous model
required a first-run download. New users have no baseline to compare
against, so it adds noise. Removed from docs/cli.md and apps/cli/README.md
(which renders on npmjs.com). Kept in apps/cli/README_internal.md as a
design note for contributors.

* Revert "chore: ignore local secret file"

The /secret entry was added defensively after the bootstrap test, but it
clutters .gitignore with a project-specific path that nobody else needs.
Drop it; secrets stay out of the repo by other means (don't commit them).

* fix: address third-pass review (stale workflow comments + missing test)

release.yml:
- The header comment block and dry_run input description still referenced
  the PR-and-merge step that was removed in 53c2e45. Updated both to
  describe the direct-commit flow.

runtime.test.ts:
- The runtime resolver's npm-package branch (step 2 — the primary path
  after npx kandev@latest or npm install -g) had no positive coverage. In
  the test env, require.resolve for @kdlbs/runtime-* throws
  MODULE_NOT_FOUND, so the existing tests only exercised step 1 (env var)
  and step 3 (cache via --runtime-version), plus the no-runtime-found
  fallthrough.

  Added two tests using vi.spyOn on Module._resolveFilename to point at a
  fake bundle inside tmpDir:
    - happy path: installed npm package resolves to source=npm with the
      right bundleDir
    - corrupt-bundle path: validateBundle errors propagate (rather than
      being swallowed and falling through to "no runtime found")

  These cover the real-world scenarios users will hit on either install
  channel.

Tests: 132 → 134.

* fix(release): push commit + tag atomically

Without --atomic, git push can succeed for the tag ref and fail for the
main ref on a non-fast-forward, leaving an orphaned tag at the pre-bump
commit. The orphan then trips the 'Tag already exists on origin' guard
on the next re-run, requiring manual tag deletion to unblock.

--atomic makes the multi-ref push succeed-or-fail as a unit.

Caught by Greptile as P1.

* refactor(cli): drop runtime.ts step 3 — cache fallback was unreachable

resolveRuntime's runtimeVersion parameter and cache-fallback step were
dead code from production's perspective. The --runtime-version path is
handled entirely in run.ts (findCachedRelease + downloadRuntimeVersion +
findBundleRoot + validateBundle), and run.ts only ever called
resolveRuntime() with no argument. The parameter and step 3 only existed
because tests exercised them.

Two parallel "compute cache dir" implementations (runtime.ts:65 and
run.ts:157) were a drift hazard if the cache layout ever changes.

Changes:
- Drop the runtimeVersion parameter from resolveRuntime.
- Drop the cache step from runtime.ts entirely; remove the unused
  CACHE_DIR + findBundleRoot imports.
- Narrow RuntimeSource from "env" | "npm" | "cache" to "env" | "npm",
  and drop the now-unused tag field from ResolvedRuntime.
- Remove the three "cache fallback" tests from runtime.test.ts. The
  --runtime-version cache lookup is still covered by run.test.ts via
  findCachedRelease tests.

Tests: 134 → 131 (three deleted tests are obsolete, the production
behavior they were verifying is now covered by run.test.ts).

* chore(test): drop dead mock setup left over from runtime.ts step 3 removal

After feb7c5a removed the cache fallback from runtime.ts, the test file
had two dangling artifacts:

- The vi.mock("./constants", ...) block + the mockCacheDir holder + the
  mockCacheDir.value = tmpDir assignment in beforeEach. runtime.ts no
  longer imports anything from ./constants, so the mock was a no-op
  that would mislead readers tracing why constants are mocked here.
- The PLATFORM = "macos-arm64" constant was unused — platform
  determinism is already enforced by the vi.mock("./platform", ...)
  block one section above.

Caught by Claude and the github-code-quality bot.
2026-05-05 00:43:34 +01:00
Carlos Florêncio b6a274183b release: app v0.39, cli 0.17.0
release / build-web (push) Has been cancelled
release / docker (push) Has been cancelled
release / build-bundles (aarch64-linux-gnu-gcc, arm64, linux, ubuntu-latest, linux-arm64) (push) Has been cancelled
release / build-bundles (amd64, darwin, macos-14, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
release / build-bundles (x86_64-w64-mingw32-gcc, amd64, windows, ubuntu-latest, windows-x64) (push) Has been cancelled
release / publish-release (push) Has been cancelled
2026-05-04 22:24:54 +01:00
José Almeida 47ff0804a5 feat(cli): make --port the user-facing port flag (#737)
* feat(cli): add --port flag as the user-facing port knob

Introduces --port (and KANDEV_PORT) as the primary, command-aware port
flag: in start/run it sets the Go backend port (the proxy front door
the browser actually hits), and in dev it sets the Next dev server port.
--backend-port and --web-port still work but emit a one-time stderr
deprecation note pointing at --port and --web-internal-port respectively.

* feat(cli): print labelled "open:" URL on startup

Replaces the ambiguous "[kandev] url:" / "ready at" lines with a
labelled block that always shows the user-facing URL first, plus the
backend (api) and web (internal) URLs when they differ. Each command
now declares its own publicUrl: backend in start/run (where the Go
server reverse-proxies Next.js), web in dev (browser hits Next direct).

* style(cli): apply prettier formatting to args.test.ts

* refactor(cli): tighten arg parsing per review feedback

- Validate value-taking flags (--port, --backend-port, --web-internal-port,
  --web-port, --version) and reject missing/non-numeric values with a clear
  ParseError instead of leaking NaN into the port validator.
- Drop the test-only _resetWarnedFlagsForTest export and the module-level
  warned-flag set; parseArgs now returns deduped deprecatedFlags and cli.ts
  emits warnings command-aware (e.g. --backend-port → KANDEV_BACKEND_PORT
  in dev, where --port maps to web).
- Stop double-printing the "open:" URL: logStartupInfo now lists labelled
  backend (api) / web (internal) URLs only, leaving the post-readiness
  open: line as the single source of truth.
- Fix help text: --web-internal-port works in dev too.
- Drop multi-line JSDoc blocks per CLAUDE.md style guide.

* fix(cli): validate env-var ports and collapse comment to one-liner

Catches non-numeric KANDEV_PORT / KANDEV_BACKEND_PORT / KANDEV_WEB_PORT in
envPort with a clear ParseError instead of letting NaN reach
ensureValidPort. Drops a multi-line comment per CLAUDE.md style.

* fix(cli): reject non-integer port values

Number.isFinite accepts floats like 3000.5 which then fail downstream
in ensureValidPort with a generic message. Tighten parsePort and
envPort to require integers and add float tests for both flag and
env-var paths.

* fix(cli): reject out-of-range port values with flag-named errors

Adds 1-65535 range checks to parsePort and envPort so values like 0,
-1, or 65536 fail with a flag/env-var-named ParseError instead of
reaching ensureValidPort with a generic 'backend port' label.

* test(cli): cover KANDEV_WEB_PORT happy path and range validation

* test(cli): cover KANDEV_WEB_PORT in run/start mode too

* fix(cli): explicit CLI flag wins over env var for port resolution

Previously KANDEV_BACKEND_PORT silently overrode --port because each
process port resolved its own env var before the generic --port
fallback. New precedence per process: explicit CLI flag > generic
--port flag > explicit env var > generic KANDEV_PORT. Also relabel
the startup info from "backend (api)" / "web (internal)" to plain
"backend" / "web" — "internal" was misleading in dev where the web
URL is what users open.

* style(cli): collapse resolvePorts comment to one line

* fix(cli): reject empty --version= and empty port env vars

- --version= (no value after =) now throws ParseError instead of
  silently storing the empty string as the version tag.
- envPort distinguishes unset (undefined) from empty (""); an empty
  KANDEV_PORT / KANDEV_BACKEND_PORT / KANDEV_WEB_PORT is now treated
  as misconfiguration and fails fast.

* refactor(cli): make --port a plain alias for --backend-port

Drops the command-aware split where --port mapped to web in dev and
backend in run/start. --port and --backend-port now both set
options.backendPort regardless of command, and KANDEV_PORT is a plain
fallback for KANDEV_BACKEND_PORT. The whole point of the original
fix was that the *backend port* is the URL users actually open in
run/start (because the Go server reverse-proxies Next.js); a single
flag that always means "the backend port" matches that mental model
without per-command surprises.

Removes deprecationReplacement and the command-aware warning logic
since the two flags are now interchangeable. --web-port → --web-internal-port
deprecation stays.

* test(cli): re-add KANDEV_WEB_PORT run-mode coverage dropped in refactor
2026-04-29 22:25:10 +01:00
Carlos Florêncio 183efad419 feat: expose MCP server to external coding agents (#732) 2026-04-29 10:37:44 +01:00
Carlos Florêncio 7f0fe7de1f release: app v0.38, cli 0.16.0
release / build-web (push) Has been cancelled
release / docker (push) Has been cancelled
release / build-bundles (aarch64-linux-gnu-gcc, arm64, linux, ubuntu-latest, linux-arm64) (push) Has been cancelled
release / build-bundles (amd64, darwin, macos-14, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
release / build-bundles (x86_64-w64-mingw32-gcc, amd64, windows, ubuntu-latest, windows-x64) (push) Has been cancelled
release / publish-release (push) Has been cancelled
2026-04-27 19:44:04 +01:00
Carlos Florêncio a79fd85074 fix(sprites): use bash for prepare scripts and fix pnpm install in sprites env (#701) 2026-04-25 18:39:55 +01:00
Carlos Florêncio 7076a40805 chore: change default ports to avoid common conflicts (#681) 2026-04-23 18:09:53 +01:00
Carlos Florêncio 31e45dcfec fix(cli): default dev mode db to <repo>/.kandev-dev/data (#664) 2026-04-20 22:49:26 +01:00
Carlos Florêncio c31fa98e61 fix(cli): detect standalone server.js at non-default path (#660) 2026-04-20 22:34:15 +01:00
Carlos Florêncio 814efdfa67 fix(cli): isolate dev mode state when running inside a kandev task (#617) 2026-04-17 19:41:29 +01:00
Carlos Florêncio 2e43478e96 release: app v0.34, cli 0.15.0
release / build-web (push) Has been cancelled
release / docker (push) Has been cancelled
release / build-bundles (aarch64-linux-gnu-gcc, arm64, linux, ubuntu-latest, linux-arm64) (push) Has been cancelled
release / build-bundles (amd64, darwin, macos-14, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
release / build-bundles (x86_64-w64-mingw32-gcc, amd64, windows, ubuntu-latest, windows-x64) (push) Has been cancelled
release / publish-release (push) Has been cancelled
2026-04-17 17:29:23 +01:00
Carlos Florêncio cafbb63845 release: app v0.33, cli 0.14.0
release / build-web (push) Has been cancelled
release / docker (push) Has been cancelled
release / build-bundles (aarch64-linux-gnu-gcc, arm64, linux, ubuntu-latest, linux-arm64) (push) Has been cancelled
release / build-bundles (amd64, darwin, macos-14, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
release / build-bundles (x86_64-w64-mingw32-gcc, amd64, windows, ubuntu-latest, windows-x64) (push) Has been cancelled
release / publish-release (push) Has been cancelled
2026-04-16 09:59:38 +01:00
Carlos Florêncio 11ebd1cb16 release: app v0.32, cli 0.13.0
release / build-web (push) Has been cancelled
release / docker (push) Has been cancelled
release / build-bundles (aarch64-linux-gnu-gcc, arm64, linux, ubuntu-latest, linux-arm64) (push) Has been cancelled
release / build-bundles (amd64, darwin, macos-14, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
release / build-bundles (x86_64-w64-mingw32-gcc, amd64, windows, ubuntu-latest, windows-x64) (push) Has been cancelled
release / publish-release (push) Has been cancelled
2026-04-13 10:06:55 +01:00
Carlos Florêncio 7ad7736e07 fix: move frontend off port 3000 and silence reverse-proxy panic logs (#568) 2026-04-09 18:50:43 +01:00
Carlos Florêncio 3c60cdc070 release: app v0.30, cli 0.12.0
release / build-web (push) Has been cancelled
release / docker (push) Has been cancelled
release / build-bundles (aarch64-linux-gnu-gcc, arm64, linux, ubuntu-latest, linux-arm64) (push) Has been cancelled
release / build-bundles (amd64, darwin, macos-14, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
release / build-bundles (x86_64-w64-mingw32-gcc, amd64, windows, ubuntu-latest, windows-x64) (push) Has been cancelled
release / publish-release (push) Has been cancelled
2026-04-08 19:42:49 +01:00