Commit Graph

61 Commits

Author SHA1 Message Date
Carlos Florêncio 58e7be4b78 chore: improve agent harness guidance (#2185) 2026-08-11 08:28:25 +01:00
Carlos Florêncio 4e0c1a3705 feat(launcher): move make dev to the native Go launcher (#2411) 2026-08-10 18:14:45 +01:00
João Salavisa 7a1f7f7809 fix(deps): bump dompurify override to 3.4.13 (GHSA-55q2-fjhq-7xh7) (#2433)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 19:38:59 +02:00
João Salavisa 224206e6d8 fix(deps): bump mermaid to 11.16.1 and js-yaml to 4.3.1 (#2387)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:37:41 +01:00
João Salavisa 01e2376588 fix(deps): resolve open npm Dependabot alerts (#2340)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:45:55 +02:00
Carlos Florêncio c3fd434a24 refactor: remove unused Virtuoso code (#2224) 2026-08-04 22:15:32 +01:00
João Salavisa 6cd89a78d3 feat(i18n): add scoped i18n foundation with one migrated page (#2097)
Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-08-01 23:11:18 +02:00
João Salavisa a0ee801b82 fix(deps): resolve low and medium Dependabot alerts (#1926)
* fix(deps): resolve low and medium Dependabot alerts

Bump vulnerable transitive and direct dependencies across npm, Go, and
Rust to clear all fixable low/medium severity Dependabot advisories.

npm (pnpm.overrides, scoped by major; hono pinned in @kandev/theme to
force the shadcn dev-CLI peer):
- ajv@6 6.15.0, @babel/core 7.29.x, body-parser 2.3.0,
  brace-expansion 1.1.16/2.1.2, dompurify 3.4.12, hono 4.12.x,
  @hono/node-server 2.0.x, js-yaml 4.3.0, lodash(-es) 4.18.x,
  markdown-it 14.3.x, mermaid 11.16.x, path-to-regexp@8 8.4.x,
  picomatch@2 2.3.2, postcss 8.5.x, protobufjs 7.6.5, qs 6.15.x,
  uuid@11 11.1.1, ws 8.21.x

Go: golang.org/x/net 0.55.0, github.com/jackc/pgx/v5 5.9.2

Rust (apps/desktop): serde_with 3.21.0, tauri family 2.11.x
(rand 0.7.3 dropped by the tauri cascade)

Highs are handled by a separate effort; docker/grpc left untouched.
Remaining unresolved mediums: glib (blocked by tauri's gtk ^0.18
constraint) and docker cp advisories (fix requires the docker major
bump owned by the highs effort / no patched version yet).

* fix(desktop): align @tauri-apps/cli with the 2.11 Rust crate bump

The Rust `tauri` crate moved to 2.11.5, but `@tauri-apps/cli` (which runs
`tauri build` for desktop packaging) was still pinned to 2.8.4. Tauri
expects the CLI and crate on the same minor line, so bump the CLI to
2.11.4 to keep packaging tooling in sync with the Cargo crates.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-07-24 19:02:10 +01:00
João Salavisa 41a806b9dd fix(deps): resolve open high-severity Dependabot advisories (#1919)
* fix(deps): resolve open high-severity Dependabot advisories

Patch all fixable open high-severity Dependabot alerts.

Backend (go.mod):
- google.golang.org/grpc 1.82.0 -> 1.82.1 (GHSA-hrxh-6v49-42gf)

Frontend (pnpm overrides in apps/package.json) — all transitive:
- postcss -> ^8.5.12 (GHSA-6g55-p6wh-862q)
- fast-uri -> ^3.1.4 (GHSA-v2hh-gcrm-f6hx / -v39h / -q3j6 / -4c8g)
- sharp -> ^0.35.0 (GHSA-f88m-g3jw-g9cj)
- linkify-it -> ^5.0.2 (GHSA-v245-v573-v5vm / -22p9)
- js-yaml@4 -> ^4.3.0 (GHSA-52cp-r559-cp3m)
- adm-zip -> ^0.6.0 (GHSA-xcpc-8h2w-3j85)
- hono -> ^4.12.25 (GHSA-88fw / -q5qw / -3vhc / -f67f)
- @hono/node-server -> ^1.19.10 (GHSA-wc8c-qw6v-h7f6)
- ws -> ^8.21.0 (GHSA-96hv-2xvq-fx4p)
- lodash / lodash-es -> ^4.18.0 (GHSA-r5fr-rjxr-66jc)
- flatted -> ^3.4.2 (GHSA-rf6f / -25h7)
- @isaacs/brace-expansion -> ^5.0.1 (GHSA-7h2j-956f-4vf2)
- @modelcontextprotocol/sdk -> ^1.26.0 (GHSA-345p-7cg4-v4c7)
- brace-expansion@1 -> ^1.1.16, brace-expansion@2 -> ^2.1.2 (GHSA-3jxr)
- path-to-regexp@8 -> ^8.4.0 (GHSA-j3q9-mxjg-w52f)
- picomatch@2 -> ^2.3.2 (GHSA-c2c7-rcm5-vvqj)
- minimatch@3 -> ^3.1.4, @9 -> ^9.0.7, @10 -> ^10.2.3
  (GHSA-7r86 / -23c5 / -3ppc)

Major-scoped override keys preserve each in-tree major line rather than
force a single version across incompatible consumers.

Not fixable here: github.com/docker/docker alerts (GHSA-x744-4wpc-v9h2,
GHSA-x86f-5xw2-fm2r, GHSA-rg2x-37c3-w2rh). No patched module version is
published — latest tag is v28.5.2+incompatible (the current pin) and two
of the three advisories have no fix yet.

Verified: backend build + vet clean; CLI 280 tests pass; web typecheck,
vite build, lint, and 6388 unit tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(web): regenerate license manifest after dependency bumps

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 17:06:56 +01:00
João Salavisa ed33c7ea3f fix(deps): patch critical Dependabot advisories (#1915)
* fix(deps): patch critical Dependabot advisories

Resolve all open critical-severity Dependabot alerts:

- golang.org/x/crypto 0.51.0 -> 0.52.0 (CVE-2026-39830/39831/39832/
  39833/39834/42508/46595, GHSA ssh agent/key-constraint bypass, DoS)
- vitest ^1.6.0 -> ^4.1.10 in apps/cli (CVE-2026-47429; aligns with
  apps/web, dedupes the lockfile to a single vitest 4.1.10)
- tar ^7.5.11 -> ^7.5.19 in apps/cli (CVE-2026-59873 parse DoS;
  resolves to 7.5.21)

Backend builds and vets clean; all 280 apps/cli tests pass on vitest 4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(cli): bump esbuild to a Vite-8-supported range

The vitest 4 bump pulls vite@8, whose esbuild peer is ^0.27||^0.28,
but the CLI still pinned esbuild ^0.24.0 — pnpm resolved an
unsupported vite/esbuild pair (esbuild@0.24.2) for the CLI test
stack. Bump to ^0.27.0 so vite@8 resolves against esbuild@0.27.2 and
the peer warning clears. CLI tests still pass (280/280).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 13:03:31 +01:00
Carlos Florêncio 8b265d9b82 perf(web): upgrade Vitest and cap local workers (#1696) 2026-07-15 07:50:49 +01:00
João Salavisa 79f280679a fix(deps): bump @playwright/test to ^1.61.1 to fix install hang on Node 26 (#1685)
* fix(deps): bump @playwright/test to ^1.61.1 to fix install hang on Node 26

* ci: bump CI base Playwright image to v1.61.1-noble to match @playwright/test
2026-07-14 18:06:15 +01:00
Carlos Florêncio 7e92f8da7a feat: add tauri desktop app (#1478) 2026-06-24 23:02:50 +01:00
Carlos Florêncio da45983c4c feat: add native kandev launcher (#1391) 2026-06-20 19:40:15 +01:00
Carlos Florêncio aee1846cd5 feat: remove nextjs production runtime (#1389) 2026-06-18 18:36:48 +01:00
João Salavisa 1fdeda904b feat: add voice mode for chat input (#1159)
* feat: add voice mode for chat input

Adds a voice input button to the chat composer with three transcription engines:
in-browser Web Speech API, in-browser Whisper via @huggingface/transformers, and
a server-side OpenAI Whisper fallback gated on KANDEV_VOICE_OPENAI_API_KEY.

User-configurable from a new /settings/voice-mode page:
- Master on/off toggle (defaults to on; opt-out, not opt-in)
- Engine preference (auto / Web Speech / Whisper Web / Whisper Server)
- Language hint (auto-detect or BCP-47 pinned)
- Activation mode (click-to-toggle or hold-to-talk)
- Auto-send after transcription
- Whisper Web model size (tiny / base / small)
- Editable Cmd+Shift+M keyboard shortcut

Voice settings live under user_settings.voice_mode as a nested JSON object —
no schema migration. Setting changes broadcast via the existing
user.settings.updated WS event so any open chat tab updates live.

The mic button is rendered on the right of the composer alongside the send
button (round primary fill; destructive fill with pulse ring while recording).
On browsers without a usable engine (e.g. iOS Chrome over HTTP) the button
stays visible as a muted greyed icon — tapping it surfaces a toast with the
reason (typically a missing secure context).

Whisper Web runs in a Web Worker with fp16 decoder weights (avoids the
MatMulNBits scale-missing crash that q4/q8 hit on WASM). Models are pulled
from the onnx-community/* mirror and cached by transformers.js in the Cache
Storage on first use.

* fix: address voice mode PR review feedback and E2E selector clash

Bot-review fixes from #1159:
- Map http.MaxBytesError from c.FormFile to 413 instead of bare 400.
- Strip BCP-47 region suffix before passing language hint to Whisper Web.
- Recover from worker crash: terminate + null worker + reset ready flag so
  next init/transcribe creates a fresh worker instead of hanging.
- Read live store state via storeApi.getState() in voice-mode settings
  savers to avoid stale-closure clobbering concurrent settings updates.
- Detach Web Speech callbacks before abort() so trailing onend/onerror
  events don't mutate hook state after cancel().
- Spread caller init before method/body in voice-api fetch so a caller-
  supplied init can't override the multipart POST.
- Replace t.Fatalf with t.Errorf+return inside test HTTP handler goroutine
  (FailNow from non-test goroutine is unsafe).
- Add Enabled/AutoSend zeroing assertions in partial-update test so the
  bool-overwrite-on-PATCH behavior is locked in explicitly.

E2E fix:
- toolbar-overflow spec used button.rounded-full which now matches both
  the send and voice buttons (strict-mode violation). Switched to the
  submit-message-button testid for an unambiguous locator.

* fix: scope voice settings rollback + whisper worker error to changed keys

Round 2 of PR review feedback on #1159:
- voice-mode settings rollback now restores only the keys patched by the
  failing request (not the whole snapshot), preserving concurrent edits
  to unrelated voiceMode fields.
- Shortcut rollback diffs previous vs next and reverts only the changed
  keys (restore prior value or delete if absent), preserving other
  concurrent shortcut edits.
- Whisper worker error handler captures the worker reference at attach
  time and only clears refs when this.worker still matches it — a stale
  error from a previously replaced worker no longer nukes the active one.
- Update stale whisper-web-models JSDoc that still pointed at Xenova
  mirrors.

* fix: voice mode progress scale, hold-mode race, race-aware rollback

Round 3 of PR review feedback on #1159:
- Normalise Whisper progress to 0–1 in ensureWhisperClient (transformers.js
  emits 0–100, the button's display + the `ready: 1` convention expect
  0–1 — fixes the 5000% mid-download display).
- Claim driverRef synchronously at the top of finishCapture so concurrent
  pointerup + pointerleave invocations early-return instead of racing and
  clobbering a freshly-started recording's ref.
- Race-aware rollback: voice-mode and shortcut catch branches now skip
  reverting a key when latest[key] !== next[key]. A failed earlier request
  no longer overwrites a later successful save to the same key.
- Update the Worker wire-protocol comment to show onnx-community/whisper-base
  instead of the stale Xenova/* example.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-06-01 14:32:39 +01:00
Carlos Florêncio f1cb1fdb21 chore(deps): bump @pierre/diffs to 1.1.22 with regression coverage (#918) 2026-05-17 22:32:02 +01: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
José Almeida 046ac5c1b6 feat(web): add Ctrl+F search to session, plan, and terminal panels (#686)
* feat(backend): add session-scoped message search endpoint

Adds SearchMessages across repository/service/handler layers and a new
WebSocket action "message.search". Returns lightweight hits with a
60-char snippet around the first match. Session-scoped only: results
never leak across sessions. Uses dialect-aware LIKE/ILIKE so it works
on SQLite today and Postgres later.

* feat(web): add shared panel search bar and Ctrl+F hook

Introduces the reusable PanelSearchBar overlay and usePanelSearch hook
used by the per-panel search features. The hook listens on the window
and fires only when focus is within the panel root, preventDefaults the
browser find-in-page, and wires Esc to close. PanelRoot becomes a
forwardRef so panel components can expose their root element to the
hook. Adds SHORTCUTS.FIND_IN_PANEL plus shared highlight/flash styles.

* feat(web): add Ctrl+F search to terminal panels

Mounts the xterm addon-search on both passthrough and shell terminals,
exposing next/prev plus case-sensitivity and regex toggles. Invalid
regex surfaces an inline error without firing the search. Because xterm
consumes keystrokes before React, the Ctrl/Cmd+F intercept is added to
attachCustomKeyEventHandler so the shortcut opens the bar from inside
the terminal. Results counter comes from onDidChangeResults; decorations
clear on close.

* feat(web): add Ctrl+F search to plan panel

Adds a custom TipTap extension that builds a ProseMirror DecorationSet
over the document and exposes setPlanSearchQuery / planSearchNext /
planSearchPrev / clearPlanSearch commands. The current match gets an
extra class and scrolls into view. Match count is read from the plugin
state via transaction subscription, so edits to the document update the
counter automatically. Find-only — no replace.

* feat(web): add Ctrl+F search to session chat panel

Wires the new backend message.search action into a debounced client
hook and renders a hits dropdown under the search bar, with the query
highlighted in each snippet. Clicking a hit scrolls the message into
view and flashes it. When the target message is older than what is
currently loaded, the hook loops through loadOlder pages until the
message renders, making search work across lazy-loaded history without
needing to scroll-and-wait manually.

* fix(web): lazy-load xterm addon-search to avoid SSR crash

addon-search is published as a UMD bundle that references `self` at
module load. Statically importing it breaks the Next.js static
prerender of /. Deferring the import to an effect keeps the module out
of the SSR graph without changing runtime behaviour.

* fix(web): propagate plan editor via state so plan-search hook re-runs

The plan-search hook subscribed to editor transactions inside a
useEffect that depended on a ref object. Because ref.current changes do
not trigger effect re-runs, if the Editor instance was not yet ready
when the hook first mounted, the transaction handler never attached and
the match counter stayed at 0 / 0. Tracking the editor via state (in
addition to the ref) causes the deps to change when the Editor becomes
available, so the handler attaches and the counter updates.

* feat(web): allow overriding message list renderer via ?renderer= query

Exposes the "native" / "virtuoso" choice at runtime so E2E coverage can
exercise both paths without a redeploy or code change. Defaults to the
existing "native" strategy when no override is present.

* test(web): add Playwright coverage for panel-scoped Ctrl+F search

Adds an e2e/tests/search/ suite with five specs:

- panel-search-shared: cross-panel contract (Ctrl+F opens, autofocus,
  Esc closes, close button, state reset on reopen) parametrised over
  session, plan, and terminal.
- terminal-search: xterm addon-search integration — key intercept
  without leaking "f" to the PTY, case + regex toggles (incl. invalid
  regex error), Esc does not reach the shell, backspace adjusts count.
- plan-search: TipTap plugin — highlight count, exactly one current
  match, counter, Enter / Shift+Enter wrapping, close clears
  decorations, no case/regex toggles in plan bar.
- session-search: WebSocket-backed hits list, "No matches" state,
  click-to-flash, cross-session isolation, close, and virtuoso renderer
  coverage via the new ?renderer= override.
- panel-focus-routing: bar mounts inside the focused panel only.

Three tests capture GIFs for the PR description via prCapture when
CAPTURE_PR_ASSETS is set (one per panel). Helpers live in
e2e/helpers/panel-search.ts and e2e/helpers/seed-session-messages.ts;
SessionPage gains search-bar locators.

* fix(web): prevent plan-search update loop on spurious editor transactions

TipTap fires `transaction` on every cursor move/click. The match-info
handler allocated a fresh `{current, total}` object each time, so React
always saw new identity, re-rendered, and could cascade with downstream
effects (e.g. `rehydrateCommentMarks`) into a "Maximum update depth
exceeded" loop. Use a functional setState with equality bail-out so the
handler only schedules a render when the counter actually changes.

* fix(web): retry terminal search once xterm addon-search finishes loading

The SearchAddon is dynamic-imported and was stored in a ref. The
query→findNext effect called `addonRef.current?.findNext(...)` — a
silent no-op if the addon wasn't loaded yet — and its deps didn't
include addon readiness, so a query typed before the import resolved
was dropped forever. Track the addon via state so the search effect
re-runs when it becomes available (and re-binds cleanly if the terminal
is ever re-created). Also deduplicate match-info updates via functional
setState + equality check.

* fix(web): route Ctrl+F to the last-clicked panel when focus is ambient

The chat panel's message list has no focus-capturing child, so clicking
on a message left `document.activeElement === <body>` and the existing
`isFocusWithin` gate silently fell through to the browser's native find.
Fix with two complementary mechanisms:

- `usePanelSearch`: track the most-recently-clicked panel via a
  window-level pointerdown capture listener. When focus is ambient
  (body/html), that panel claims Ctrl+F. Window-level capture dodges
  any descendant `stopPropagation` that would skip a container-level
  listener.
- Chat panel root: tabIndex=-1 + onMouseDown that focuses the root when
  the click target isn't already focusable. Normal path matches how
  plan/terminal already work (TipTap/xterm capture focus on click).

* feat(web): show agent profile name and logo in session search hits

The hit list showed uppercase "AGENT"/"USER" with vivid blue/green
colors that clashed with the app's muted metadata aesthetic. Replace
with the actual author identity:

- Agent hits render the profile name (falling back to "Agent") with
  the profile's <AgentLogo> inline — same mechanism session tabs use.
- User hits render "You" with a subtle `text-primary/80` tint that
  echoes the user bubble's `bg-primary/30`, keeping a light color
  differentiator without reintroducing loud blue/green.

Added `useSessionAgentIdentity` (plus a `useSessionAgentProfile` helper)
that reads from the store via two primitive selectors — the id, then
the profile — and composes the final object inside `useMemo`. A naive
single selector returning `{label, name}` triggers the "getSnapshot
should be cached" infinite loop in `useSyncExternalStore`.

* style: apply prettier formatting to search files

* fix(web): inline resolveStrategy in useMemo to satisfy react-hooks/use-memo

* fix(backend): escape LIKE metacharacters in message search

User query of `%` previously built LIKE pattern `%%` which matched every
message in the session; `file_name` also matched `filename`, `file name`,
etc. Escape the three LIKE metacharacters (`\`, `%`, `_`) before wrapping
the pattern, and declare `ESCAPE '\'` on the clause. Add regression tests
covering wildcard literals and the literal-percent happy path.

* fix(backend): slice search snippet in rune space to keep UTF-8 valid

`buildSnippet` indexed and sliced on byte offsets, so content containing
multi-byte characters (emoji, CJK, accented letters) could be cut
mid-rune — `encoding/json` then replaced the invalid bytes with `\ufffd`
in the websocket response. Switch to rune slicing and per-rune
case-folding via `unicode.ToLower`; this also avoids the
`strings.ToLower` byte-length-change pitfall (e.g. Turkish İ, German ß).
Add a handler test covering emoji, CJK, and large multi-byte content.

* fix(web): cancel backfill loop when user clicks a newer session hit

`setActiveHit` awaits up to 40 `loadOlder()` iterations when the target
message isn't in the DOM. If the user clicked a second hit while those
awaits were in flight, the in-progress loop kept running and eventually
scrolled to the original target, overriding the user's intent. Gate the
loop on a generation counter so only the most recent call's backfill
drives the scroll.

* fix(web): gate plan-search scroll side-effect on dispatch

TipTap invokes commands with `dispatch === undefined` during `can()`
checks to test applicability (e.g. before enabling a menu item). The
next/prev commands called `scrollToMatch` outside the `if (dispatch)`
guard, so a dry-run invocation would scroll the editor to a match the
user didn't navigate to. Move the scroll inside the dispatch block.

* fix(web): address review nits on search UX

- formatTime now checks Number.isNaN(d.getTime()) before formatting;
  Invalid Date doesn't throw, so the catch branch was unreachable and
  hits with a malformed created_at rendered the literal "Invalid Date".
- prefers-reduced-motion now silences the .search-flash animation
  alongside the other custom keyframe animations.
- planScript escapes `title` symmetrically with `content` so callers
  passing arbitrary strings don't produce malformed JSON.

* fix(web): fold description into opts to keep seedTask under param limit

`seedTask` had 6 parameters, violating the repo's 5-parameter cap and
failing CI lint. Fold `description` into the existing `opts` object and
update all callsites.

* test(web): add unit tests for useSessionSearch + remove redundant ternary

Cover the non-trivial hook logic called out in review: debounced fetch
coalescing, request-ID cancellation for superseded queries, null
sessionId short-circuit, and close() resets. Also simplify the
`matches.length ? 0 : 0` ternary flagged as a dead branch — both sides
were already 0.

* fix(web): exclude PanelRoot (tabindex=-1) from the focus short-circuit

`target.closest("[tabindex]")` was matching PanelRoot itself (which has
`tabIndex={-1}` so it can receive programmatic focus), causing the guard
to early-return and the `panelRef.focus()` call to never fire — defeating
the Ctrl+F focus-routing that this handler was added for.

Narrow the selector to `[tabindex]:not([tabindex='-1'])` so it only
short-circuits on genuinely interactive elements.

* fix(web): read isReadOnlyMode from live ref in shell-terminal key handler

`attachCustomKeyEventHandler` is called once and has no dispose hook, so
the inner handler keeps its initial closure forever. Other values
(sessionId, send, onFindInPanel) are read via `stateRef` so they stay
current, but `isReadOnlyMode` was captured by closure — if the prop ever
toggled mid-session, the Cmd+Arrow→Home/End path would continue firing
against a read-only terminal. Include it in the state ref for
consistency.

* fix(web): flatten doubly-nested seedTask opts in virtuoso spec; drop unused MODIFIER

The regex-based transform in 7152b936 over-nested one call in
session-search.spec.ts — `opts.description` received an object rather
than a string, so the virtuoso renderer test never received a proper
task description. Flatten it.

Also remove MODIFIER imports/declarations in terminal-search.spec.ts and
panel-search-shared.spec.ts that were left dangling behind `void` suppressors.

* fix(web): abort session-search backfill loop on close/unmount

Move reset logic into `close()` so the lifecycle is explicit (and avoids
the set-state-in-effect lint path the previous reset effect triggered).
Bump the `activeHitGenRef` generation counter on both `close()` and
unmount so an in-flight `setActiveHit` backfill can no longer issue
`loadOlder()` calls for a hidden UI.

Also extract `useDebouncedSearch`, `useSetActiveHit`, and a
`focusMessageElement` helper to keep `useSessionSearch` under the
100-line function cap.

* perf(web): skip PanelSearchBar debounce for session search

Session search already debounces the WS call inside `useDebouncedSearch`
(180ms), but the shared bar adds its own 150ms debounce on top — giving
a 330ms keystroke→request latency instead of the intended ~180ms.
Pass `debounceMs={0}` so the bar forwards queries immediately and the
hook-level debounce is the single source of timing truth.
2026-04-24 17:14:03 +01:00
José Almeida 2b8513f902 feat: add multi-select and drag-to-move for file tree and changes panel (#490)
* feat: add multi-select and drag-to-move for file tree and changes panel

Add Shift-click range and Ctrl/Cmd-click toggle selection to the file
tree and staged/unstaged changes sections. Includes bulk actions
(delete, stage, unstage, discard), drag-and-drop file moving into
directories, keyboard shortcuts (Escape, Ctrl+A), and E2E tests.

* fix: move bulk action components to file-row to fix max-lines lint

* style: apply prettier formatting to all changed files

* fix: bulk delete all selected items and update anchor on stale shift-click

* fix: address CodeRabbit review comments

- Keep original file-row-* data-testid for backwards compat
- Remove eager onClearSelection from bulk actions (auto-pruned by hook)
- Variant-scope bulk-discard/bulk-unstage test ids
- Remove index.lock deletion from E2E GitHelper retry loop

* fix: fix E2E tests — create files after session open, remove DnD test

- Create files AFTER opening session so WS detects git status changes
- Add proper git env vars (GIT_AUTHOR_NAME, etc.) matching existing tests
- Use element.press("Escape") instead of page keyboard for proper focus
- Remove drag-to-directory test (Playwright dragTo doesn't support HTML5
  dataTransfer events needed by our DnD implementation)

* fix: rename E2E task titles to avoid ambiguous 'Changes' tab selector

* fix: make file list focusable for Escape key handler in E2E tests

* fix: improve selection styling and ignore right-clicks in selection handler

- Use text-accent-foreground on selected rows for dark mode readability
- Override child span colors with [&_span]:text-accent-foreground
- Use hover:bg-accent/80 instead of hover:bg-muted on selected items
- Skip selection handling on right-click (button === 2) so context menu
  works without clearing multi-selection

* fix: plain click should not select — only Ctrl/Cmd+click and Shift+click

Plain click now clears any selection and performs the original action
(open file / toggle folder / open diff). Selection only happens with
modifier keys. handleClick returns boolean to indicate whether the
click was consumed by selection logic.

* fix: fix multi-select bugs and expand E2E test coverage

Bug fixes:
- Change isSelected from boolean to function prop so children compute
  their own selection state (fixes nested file selection)
- Inline TreeNodeRow JSX into TreeNodeItem so ContextMenuTrigger asChild
  can attach refs directly (fixes right-click context menu)
- Set lastClickedRef on plain click so shift-click has a valid anchor
- Add click-outside handler to clear selection

E2E tests expanded to 14 file-tree tests + 3 changes-panel tests:
- Ctrl-click select, toggle, multi-select
- Plain click clears selection
- Shift-click range after plain click and ctrl-click anchors
- Directory selection and nested file selection
- Right-click context menu (single + bulk delete)
- Click outside clears selection
- Escape clears selection

* fix: move AlertDialog outside ContextMenu to fix delete action

The AlertDialog wrapping the ContextMenu caused Radix focus management
to intercept context menu item selection, making Delete trigger Rename.
Moving AlertDialog to a sibling of ContextMenu fixes the conflict.

* fix: reorder context menu — Delete first, Rename second

Radix ContextMenu auto-focuses the first item on open, which caused
accidental Rename triggers. Putting Delete first (the more common
action for selection) avoids this.

* fix: don't clear selection when clicking context menu or dialog

The click-outside handler was clearing selection when clicking on
context menu items or confirmation dialog buttons (rendered in portals
outside the tree container). Now skips clearing for elements with
role=menu, role=alertdialog, or role=dialog.

Also extended the bulk delete E2E test to verify both files are
actually removed after confirming the dialog.

* fix: hide file count in delete dialog when folder children not loaded

When a folder hasn't been expanded, its children aren't loaded yet so
countFilesInTree returns 0. Instead of showing "0 files", hide the
count entirely and just show "delete <folder name>".

* fix: show confirmation dialog for bulk discard in changes panel

Bulk discard was calling git.discard() directly, bypassing the
confirmation dialog. Now routes through handleBulkDiscardClick which
opens the DiscardDialog with a bulk message ("discard changes to N
files") before executing.

* fix: address remaining CodeRabbit review comments

- Deduplicate filenames on move: when moving files with identical names
  to the same directory, append "(1)", "(2)" etc. to avoid collisions.
  Both tree update and rename API calls use the same deduplication.
- Replace void operator with .catch(() => {}) for bulk stage/unstage
  to prevent unhandled promise rejections.
- Add document-level dragend listener as safety net to clear stuck drag
  state if the element's onDragEnd doesn't fire (Escape, out-of-window).

* test: add unit tests for file tree move deduplication and visible paths

* fix: address all remaining PR review comments

- Move onSelectionChange out of React state updater (side effect fix)
- Stabilize keyboard listener deps (pass callbacks not object)
- Remove unnecessary useMemo in BulkActionBar
- Fix ghost-restore on partial bulk delete (remove all at once)
- Fix blank filename on single-file bulk discard
- Add aria-selected and role=treeitem for accessibility
- Extract computeNextSelection params into object (max-params lint)

* fix: add missing bulk action props to WorkingTreeProps type

WorkingTreeSections referenced onBulkStage, onBulkUnstage, and
onBulkDiscard but WorkingTreeProps Pick didn't include them, causing
a TypeScript build failure.

* fix: address final review comments

- Add unit tests for computeNextSelection (8 tests covering ctrl-click,
  shift-click range, stale anchor, reverse range, shift+ctrl extend)
- Extract shared E2E helpers (GitHelper, makeGitEnv, openTaskSession,
  createStandardProfile) to e2e/helpers/git-helper.ts
- Fix misleading comment in bulk delete (says "re-insert only failed"
  but implementation restores full snapshot)

* fix: use Promise.all for bulk delete to prevent partial rollback

Individual .catch() handlers could restore already-deleted files
if a later delete failed. Now uses Promise.all so rollback only
happens if any delete rejects.

* test: add renderHook tests for useMultiSelect hook behaviors

- Stale-path pruning: items shrink → selected paths auto-pruned
- Plain click: returns false, clears existing selection
- selectAll/clearSelection: work correctly, fire onSelectionChange
- Install @testing-library/react for renderHook support

* fix: handle false returns from onDeleteFile in bulk delete Promise.all

* chore: remove unused @testing-library/react-hooks dependency
2026-04-11 17:43:21 +01:00
dependabot[bot] 533d697474 chore: bump happy-dom from 20.8.8 to 20.8.9 in /apps in the npm_and_yarn group across 1 directory (#495) 2026-03-31 23:51:04 +01:00
dependabot[bot] e5155ff659 chore: bump happy-dom from 20.1.0 to 20.8.8 in /apps in the npm_and_yarn group across 1 directory (#477) 2026-03-28 21:17:57 +00:00
dependabot[bot] 758c9591eb chore: bump the npm_and_yarn group across 2 directories with 2 updates (#445) 2026-03-25 00:34:32 +00:00
Carlos Florêncio fb5886600a feat(web): add markdown preview mode and PR screenshot capture (#461) 2026-03-24 23:38:02 +00:00
José Almeida 06e5114174 feat: add clickable terminal links with configurable open behavior (#401)
* feat: add clickable terminal links with configurable open behavior

Add @xterm/addon-web-links to both passthrough and shell terminals,
enabling Cmd+Click on URLs. Users can configure whether links open
in a new browser tab (default) or the built-in browser panel via a
new setting in the Settings page.

* style: fix prettier formatting

* fix: address CodeRabbit review comments

- Extract applyTerminalLinkBehavior helper to reduce complexity
- Set default terminalLinkBehavior for first-time users in empty settings path
- Use storeApi.getState() for optimistic rollback to avoid stale closures
- Add beforeEach settings reset in E2E tests
- Use 4xx range assertion instead of hardcoded 500

* fix: return 400 for user settings validation errors

Add ErrValidation sentinel to user service and wrap validation errors
from apply* functions. Handler now returns 400 Bad Request instead of
500 Internal Server Error for invalid input. Update E2E test assertion
to enforce client error status (< 500).

* fix: remove cmd+click mention from terminal links description

* fix: mirror validation error handling in WS update path

* style: fix prettier formatting
2026-03-10 15:37:51 +00:00
dependabot[bot] 1724f10340 chore: bump tar from 7.5.9 to 7.5.10 in /apps in the npm_and_yarn group across 1 directory (#377) 2026-03-06 21:36:21 +00:00
Carlos Florêncio 87a4438037 fix: ui improvements for pr panel, git operations, and task sidebar (#359) 2026-03-03 22:52:47 +00:00
Carlos Florêncio 44811da702 feat: web e2e tests (#304) 2026-03-02 08:54:58 +00:00
dependabot[bot] 48383f421e chore: update nextjs dep (#326) 2026-02-27 23:50:00 +00:00
Carlos Florêncio 8ee5b56904 feat: release notes (#298) 2026-02-26 23:08:15 +00:00
Carlos Florêncio 2782d826ec feat: improve PR review watcher + PR info panel (#281) 2026-02-26 08:48:11 +00:00
Carlos Florêncio 98330b05e4 feat: use github.com for releases instead of api to avoid rate limiting 2026-02-25 02:55:54 +00:00
Carlos Florêncio 829236f1be feat: chat improvements (#280) 2026-02-25 02:10:16 +00:00
Carlos Florêncio 32f7ceb319 feat: several UX improvements (#272) 2026-02-24 00:48:55 +00:00
Carlos Florêncio a23ab5be3a feat: remote executors + secrets (#257) 2026-02-21 22:14:56 +00:00
Carlos Florêncio 61ef224ec7 style: fix format issues (#255) 2026-02-19 22:18:01 +00:00
Carlos Florêncio b5ca1dafb8 feat: improve plan comments (#249) 2026-02-18 23:34:50 +00:00
Carlos Florêncio f49158aeb2 feat: improve monaco comments (#241) 2026-02-17 19:37:53 +00:00
Carlos Florêncio 82ea7578dc feat: ci complexity linters (#236) 2026-02-17 09:19:42 +00:00
Carlos Florêncio 613b063787 feat: use tar for bundles 2026-02-16 10:52:46 +00:00
Carlos Florêncio c4ef2d9ba1 feat: improve editors (#226) 2026-02-16 09:55:04 +00:00
Carlos Florêncio e24f65a133 fix: release build
release / build-web (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-15-large, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (amd64, windows, windows-latest, windows-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
2026-02-15 22:16:53 +00:00
Carlos Florêncio cad2d783b3 feat: improve windows support (#225)
release / build-web (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-13, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (amd64, windows, windows-latest, windows-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled
feat: windows support
2026-02-15 18:16:21 +00:00
Carlos Florêncio f4d1dfd3a7 feat: better context files (#222)
feat: context files
2026-02-14 20:13:11 +00:00
Carlos Florêncio 813bfd1215 feat: dockview new ui (#219) 2026-02-13 00:00:04 +00:00
Carlos Florêncio b22e66750c feat: improved file tree (#164)
feat: improved editor
2026-02-03 04:14:07 +00:00
Carlos Florêncio 292e583ce0 feat: update deps 2026-02-02 17:44:56 +00:00
João Salavisa 5bd298ddc2 feat: Add file editor with diff-based save functionality (#145) 2026-02-02 00:59:48 +00:00
Carlos Florêncio 38fb2f638f feat: pierre diffs lib (#147) 2026-02-02 00:17:23 +00:00
Carlos Florêncio c163b61b7d feat: all tasks page plus search (#140) 2026-02-01 19:07:52 +00:00