• v0.5.7 2ddec07712

    Ghost released this 2026-06-17 16:23:15 +00:00 | 316 commits to main since this release

    • fix(code-mode): make packaged code mode work via on-demand engine provisioning

    Packaged builds could never run code mode: the Claude/Codex ACP adapters are
    spawned as separate node <entry> processes resolved at runtime, but esbuild
    can't inline a dynamic spawn target and Forge strips the workspace node_modules,
    so every release threw Cannot find module '@agentclientprotocol/...'. Dev
    worked only because of the pnpm symlink.

    Rather than bundle the ~400 MB of native engines (one claude + one codex binary
    per OS), provision them on demand:

    • forge.config.cjs: stage the two ACP adapters + their JS dependency closure into
      .package/acp/node_modules (npm-style nested layout, native engines skipped),
      exempt .package from the node_modules ignore rule, and only sign/notarize when
      APPLE_ID is set so unsigned local/CI builds can package.
    • agents.ts: resolve the adapter from the staged location first (node_modules
      fallback in dev); provision the pinned engine and point the adapter at it via
      CLAUDE_CODE_EXECUTABLE / CODEX_PATH. No dependence on a user's global install.
    • engine-provisioner.ts: ensureEngine() downloads the per-platform engine package
      from npm AT THE EXACT VERSION THE ADAPTER WAS BUILT AGAINST, verifies its sha512
      integrity, extracts atomically into ~/.rowboat/engines///, and
      caches it. Version-pinning keeps the ACP handshake compatible.
    • engine-manifest.ts + scripts/gen-engine-manifest.mjs: committed manifest of
      tarball URLs + integrity for all platforms, regenerated from the adapters'
      pinned versions on a bump.

    Verified on macOS arm64: both engines provision and run, and both adapters
    complete the ACP initialize handshake from the packaged .app against the
    provisioned engines. Installer drops from ~790 MB to 390 MB.

    • feat(code-mode): explicit per-agent Enable in Settings; no silent chat download

    Code mode now requires the user to explicitly enable an agent before use, instead
    of silently downloading a ~200 MB engine on the first chat message.

    • Settings → Code Mode: each agent shows "Not enabled" + an Enable button that
      downloads its engine with a live progress indicator (download % → verify →
      install), then flips to "Engine ready". Driven by a new codeMode:provisionEngine
      IPC call + a codeMode:engineProgress push channel. The section now states the
      prerequisite explicitly: the agent must be installed (Enable) and logged in
      (claude login / codex login — code mode reuses that saved credential).
    • Chat path no longer auto-downloads: getProvisionedEnginePath() returns the
      enabled engine or throws a clear "enable it in Settings → Code Mode" error, so
      there's never a surprise mid-conversation download. getAgentLaunchSpec is sync
      again.
    • Agent status: installed now means "engine provisioned" (downloaded), driving
      the Enable/Ready state; the new-session dialog shows "Enable in Settings" and
      disables un-enabled agents. Dropped the dead PATH-probing for a global CLI.

    Verified: empty cache -> status installed=false and the chat path throws the
    enable-in-Settings error (no download); core, renderer, and main typecheck/build;
    no new lint errors.

    • fix(code-mode): show only percentage during engine download in Settings

    • feat(code-mode): prune superseded engine versions after install

    After a successful provision, remove any other version dirs (and their .meta) for
    that agent so old ~200 MB engines don't accumulate across version bumps. Best-effort;
    never fails a good install. Verified: a planted stale version dir + meta are both
    removed after provisioning the current version.

    • fix(code-mode): keep showing engine download % after reopening Settings

    Provisioning state lived in the row component, which unmounts when the Settings
    dialog closes — so reopening mid-download showed the Enable button again even though
    the download was still running in the main process. Move provisioning state to a
    module-level store with one persistent listener on codeMode:engineProgress, so a row
    remounting (dialog reopened) reflects the live % and resolves to Ready on completion.

    • fix(code-mode): flip Enable row straight to Ready after install (no Enable flash)

    On successful provision the in-flight flag was cleared before the async status
    refresh completed, so the row briefly (or until reopen) showed the Enable button
    again. Await the status refresh before clearing the flag so it transitions directly
    to Ready.

    • fix(code-mode): optimistically show Ready right after Enable completes

    Awaiting the status refresh wasn't enough — setStatus re-renders the parent
    separately from the row, leaving a window where the in-flight flag was cleared but
    the status prop was stale, so the row flashed/stuck on the Enable button until
    reopen. Track just-enabled agents in a module-level set and treat them as installed
    immediately; loadStatus still syncs the real status in the background.

    • fix(code-mode): graft login-shell PATH + add startup deadline

    #1 (the gh/git "command not found" in packaged builds): GUI/Finder launches inherit
    launchd's stripped PATH (/usr/bin:/bin:...), so tools the engine spawns — gh, git,
    rg, bash — fail even though they work from a terminal (e.g. Homebrew's
    /opt/homebrew/bin/gh). Probe the user's login-shell PATH and graft it onto the
    engine's env before spawn (shell-env.ts; no-op on Windows / probe failure).

    #2: add a 60s startup deadline (initialize / session create+load) so a wedged engine
    fails with a clear, stderr-enriched error instead of an infinite "(pending...)".
    Overridable via ROWBOAT_ACP_STARTUP_TIMEOUT_MS. Manager now disposes the client on
    startup failure so the spawned adapter doesn't leak.

    Verified: getAgentLaunchSpec's env.PATH now includes /opt/homebrew/bin (where gh
    lives); core builds; no new lint errors.

    • chore(code-mode): comment out signing/notarization for local builds

    Revert to the explicit comment-out approach for osxSign/osxNotarize: uncomment them
    (with APPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID) for a signed release build.

    • chore(code-mode): keep signing/notarization active in committed config

    The repo's forge.config ships with osxSign/osxNotarize enabled (release-ready).
    Developers comment them out locally for unsigned test builds and don't commit that.

    • chore: approve workspace build scripts so packaging runs non-interactively

    The allowBuilds entries were left as "set this to true or false" placeholders, so
    pnpm install / the pre-build deps check aborted with ERR_PNPM_IGNORED_BUILDS and
    npm run package failed. Set them to true (and add node-pty, used by the code-mode
    embedded terminal) so build scripts are approved and packaging works without a manual
    pnpm approve-builds.

    Downloads