github yvgude/lean-ctx v3.8.7

5 hours ago

Added

  • Dashboard: sort the live call feed by per-call cost (#426) — the Live
    Activity feed already showed per-call detail (tool, file/query, tokens in →
    out, tokens saved, read mode); it now has a Sort selector — Recent / Top
    saved / Largest / Slowest — so you can rank tool calls by cost and instantly
    see which reads/searches/shell calls were expensive vs cheap. Read-only,
    reuses the existing /api/events journal data; no new routes.
  • Dashboard: Quick Settings — flip core switches from the UI (#427) — a new
    Settings tab (Context area) flips the four high-impact, mid-session
    switches without dropping to the terminal: compression level
    (off/lite/standard/max), tool profile (minimal/standard/power/lean),
    structure_first (on/off) and terse agent (off/lite/full/ultra). Writes go
    through a new /api/settings endpoint that inherits the dashboard's
    Bearer-token auth and CSRF-Origin check, validates every value against the
    config schema and a fixed four-key allow-list (no arbitrary config keys
    are writable), and persists to config.toml exactly like the matching CLI
    commands. Settings pinned by a LEAN_CTX_* environment variable are flagged
    in the UI so a toggle never silently no-ops.
  • Dashboard: --open=browser|none|vscode reveal control (#424)lean-ctx dashboard always launched the system browser, which is jarring inside an
    editor or behind a reverse proxy. A new --open=<mode> flag (or --no-open),
    resolved as --open > LEAN_CTX_DASHBOARD_OPEN > the browser default, picks
    the reveal behaviour: browser (launch the system browser, unchanged default),
    none (start silently and just print the URL) or vscode (suppress the
    external browser and print the VS Code Simple Browser steps). Flag parsing is
    case-insensitive and falls back to browser on an unknown value.

Fixed

  • macOS: the "lean-ctx wants to access your Documents folder" prompt no longer
    returns after every update (#356)
    — lean-ctx binaries are ad-hoc signed, so
    their cdhash changes on every build. macOS TCC anchors an ad-hoc binary's
    privacy grant to that cdhash, so each update looked like a brand-new program and
    re-popped the prompt — clicking "Allow" only lasted until the next build. New
    lean-ctx codesign-setup (macOS) creates a dedicated keychain with a persistent
    self-signed code-signing identity and trusts it once (a single Touch ID / login
    password confirmation). dev-install and the self-updater now sign every build
    with that identity, giving TCC a stable Designated Requirement
    (identifier "com.leanctx.cli" and certificate leaf = H"…") instead of a
    per-build cdhash. Result: a single "Allow" survives all future updates. Falls
    back to ad-hoc signing when the identity isn't set up, so the binary always runs.

  • doctor --fix now fully empties ~/.lean-ctx instead of leaving items behind
    (#429)
    — the XDG split migration skipped any entry whose destination already
    existed and left the source in place. On Windows (and after any partial
    earlier run or a parallel data dir) the targets routinely pre-existed, so ~30
    legacy items lingered and doctor warned about the single-dir install forever,
    no matter how often you ran --fix. Collisions are now reconciled instead of
    skipped
    : directories are merged child-by-child, a source file byte-identical
    to the destination is dropped as a duplicate, and a genuinely different source
    is moved aside next to the winner under a *.legacy name. The destination is
    never overwritten and nothing is lost, so the legacy directory empties out and
    the warning clears. doctor --fix now reports N moved/merged, N duplicate(s) dropped, N kept as *.legacy.

  • macOS TCC "Documents" prompt — definitive structural fix (#356) — the
    privacy prompt asking to access your Documents folder, which kept returning
    after every lean-ctx update despite earlier patches (v3.8.0, v3.8.2), is now
    fixed at the root. The TCC guard (may_probe_path) was opt-in per call site,
    so every new or forgotten heuristic filesystem probe re-introduced the prompt
    (whack-a-mole). The model is inverted to a choke-point / opt-out design:

    • safe_canonicalize — the sink that ~8 heuristic call sites funnel through —
      returns the path lexically (no stat/realpath) when the process is
      launchd-standalone and the path is under ~/Documents, ~/Desktop or
      ~/Downloads.
    • every duplicated project-marker probe (config, graph_index, setup,
      dashboard, knowledge_bootstrap, graph_provider) now routes through the
      single guarded pathutil::has_project_marker, with one marker set.
    • is_safe_scan_root refuses launchd-standalone scans under the protected dirs
      before any marker probe or read_dir; has_multi_repo_children now also
      refuses nested protected paths (e.g. ~/Documents/proj), not just the bare
      magic dirs. The project-local .lean-ctx.toml read and the git rev-parse /
      cwd-fallback in project-root detection are guarded too.

    Why it kept coming back: lean-ctx update run from a terminal makes the daemon
    inherit the terminal's TCC grant, masking the bug; end users run the daemon and
    proxy as LaunchAgents (ppid 1, standalone), where the unguarded probes hit
    ~/Documents and prompt — and every update changes the binary's code signature,
    invalidating any prior grant. A new macOS sandbox-exec regression test
    (rust/tests/tcc_sandbox.sh) boots the daemon as a standalone process under a
    profile that SIGKILLs on any ~/Documents access, reproducing the real
    end-user condition that terminal testing hid, alongside standalone unit tests
    in pathutil / graph_index / session.

    Note: installing the update that contains this fix may show the prompt one
    last time (the old, still-running binary's signature changes as it is
    replaced); after that it stays quiet.

  • auto_update_mcp = false now suppresses MCP writes on every registration
    path (#281)
    — earlier fixes only gated the shared JSON-config writer and
    configure_agent_mcp; the per-agent hook writers (Claude, JetBrains, OpenClaw,
    Crush, OpenCode) and the editor-registry registration in interactive setup,
    non-interactive setup and doctor --fix still wrote MCP server entries
    unconditionally. The check is now centralized in hooks::should_register_mcp()
    and applied on every path: hooks, rules and skills still install, only the MCP
    server entry is withheld. A subprocess regression test guards it.

  • ctx_read map/signatures no longer serve pre-rebuild output after
    ctx_index build-full (#420)
    — the CLI build-full path cleared the daemon
    read cache, but the MCP tool runs in the process that owns the SessionCache,
    so a forced rebuild left ctx_read map/signatures returning stale output.
    The MCP tool now invalidates the in-process graph cache and clears the
    SessionCache in-process, matching the CLI guarantee.

  • Dashboard auto-refreshes the active view on data change and tab focus
    (#425)
    — the 10s poll only refreshed the status bar and flagged the manual
    refresh button; the main panels listen to lctx:refresh, which only the manual
    button dispatched, so stats/metrics stayed static until a reload. The poll now
    dispatches lctx:refresh on a content-hash change while the tab is visible
    (panels reload in place, preserving UI state), and a visibilitychange handler
    catches up immediately when the tab regains focus.

  • lean-ctx watch backfills recent events on start (#560)watch set the
    tail offset to EOF on startup, so an idle launch showed a blank screen even
    when events.jsonl was already populated. It now seeds the view with the last
    20 events (bounded, O(n) memory) and advances the offset to EOF, so the live
    poll stream continues without re-emitting them.

  • Homebrew installs no longer run a stale shadowed binary (#559) — a
    brew-managed shim (/opt/homebrew/bin/lean-ctx../Cellar/lean-ctx/<old>)
    could shadow the freshly built ~/.local/bin/lean-ctx on PATH, so the daemon
    and CLI ran different builds (md5 drift). After installing, lean-ctx repoints
    any Cellar/linuxbrew shim at the just-installed binary and warns about any other
    PATH entry that still resolves before it. (The drift helper is correctly
    gated to unix so the Windows cross-compile stays warning-clean.)

  • JetBrains plugin ships under a discoverable release-asset name (#418)
    buildPlugin emitted lean-ctx-<version>.zip, indistinguishable from a source
    archive in the GitHub Release asset list, so the plugin looked "missing" even
    though it was attached. The artifact is renamed to
    lean-ctx-jetbrains-plugin-<version>.zip before upload, and the release job
    now fails loudly if buildPlugin produced no zip.

Security

  • PathJail keeps resolving symlinks under TCC-protected dirs (#356 follow-up)
    — the #356 choke-point accidentally routed PathJail's canonicalization through
    the same TCC guard (canonicalize_or_selfsafe_canonicalize_bounded
    safe_canonicalize), so a launchd-standalone daemon validating a path under
    ~/Documents got a lexical (unresolved) path and could miss a symlink jail
    escape. Security canonicalization is now split from heuristic canonicalization:
    PathJail (jail root, candidate ancestor, extra-roots, TOCTOU re-check, and the
    allow-list) uses a new unguarded pathutil::canonicalize_secure[_bounded] that
    always resolves symlinks; only self-initiated heuristic probes keep the guard.
    The jail only ever runs on a path the client explicitly asked for, so a
    one-time prompt there is legitimate, while #356's self-initiated boot prompts
    stay suppressed (verified by the sandbox-exec boot test plus a new
    canonicalize_secure_bypasses_tcc_guard_for_pathjail unit test).
  • Cookbook dev-dependency upgrade — Vite 6 → 8 (#595) — the example apps now
    build on Vite ^8.0.16 with @vitejs/plugin-react ^6 (peer vite ^8),
    pulling a patched esbuild and clearing the esbuild dev-server advisory
    (GHSA-67mh-4wv8-2f99). npm audit reports 0 vulnerabilities; the
    knowledge-graph-explorer example builds and typechecks unchanged. Node engine
    floor raised to >=20.19.0 to match Vite 8's requirement.

Upgrade

lean-ctx update                 # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx          # or
npm update -g lean-ctx-bin      # or
brew upgrade lean-ctx

Note: After upgrading via cargo/npm/brew, run lean-ctx setup to refresh shell aliases. lean-ctx update does this automatically.

Full Changelog: v3.8.7...v3.8.7

Don't miss a new lean-ctx release

NewReleases is sending notifications on new releases.