Replaces the local `pnpm release:{rc,patch,minor,major}` scripts with a
single manually-dispatched GitHub Actions workflow (`release-cut.yml`)
that takes `kind` (rc|stable), an optional `ref`, and an optional explicit
`version`.
Why: cutting releases locally was too easy to get wrong — `npm version
prerelease` behaves differently depending on whether the current version
is already an rc, there was no guard against tagging a dirty tree or an
off-main branch, and the "bad commit just landed, release the previous
one" case had no first-class answer. The new workflow:
- Resolves the next version automatically from GitHub Releases (or takes
an explicit override).
- Refuses to cut a stable release whose version isn't strictly greater
than the latest published stable. This is the only invariant
electron-updater needs within the `latest` channel.
- Only fast-forwards `main` with the version-bump commit when the caller
released the tip of `main`. Off-main releases publish only the tag, so
main is never polluted by a one-off RC against a feature branch.
CONTRIBUTING.md now documents the new flow with the common scenarios
(normal release, bad-commit-revert-to-previous-SHA, one-off RC, explicit
minor/major). The scheduled RC cron in release-rc.yml is untouched.
Cmd+F in the rich markdown editor previously inserted a sticky search bar
in the flex column, pushing document content down when opened. Wrap the
scroll area in a relative container and absolutely position the search bar
top-right so it overlays the content like Monaco's find widget.
Switch the status bar right-click menu from the bespoke Radix ContextMenu
to the onContextMenuCapture + hidden-trigger DropdownMenu pattern used by
WorktreeContextMenu, TerminalContextMenu, and the tab bar. Updates the
anchor point on every right-click so opening in a new spot re-anchors
instead of leaving the menu where it first appeared. Adds provider /
feature icons to each toggle to match the rest of the app.
* fix(terminal): drop FORCE_HYPERLINK=1 from PTY spawn env
FORCE_HYPERLINK=1 was set on every local PTY. It is read by
oh-my-zsh's supports_hyperlinks(), the Rust supports-hyperlinks
crate, GNU coreutils, and other tooling — so forcing it on
makes every subprocess invoked during shell init take extra
branches and emit OSC-8 escapes.
Reproduced locally with a heavy zshrc (oh-my-zsh + p10k + nvm +
pyenv): time-to-prompt jumps from ~600 ms without the variable
to ~2000 ms with it (3.2×) on a clean sandbox. Stacked with the
user-reported configuration (conda init, gcloud completions,
compaudit over Homebrew paths, corporate network calls) that
multiplier applied to a long serial chain of forks can stretch
into "terminal is effectively never ready."
xterm still renders OSC-8 hyperlinks when tools emit them on
their own detection, so link support in Orca is preserved. VS
Code does not set this variable either.
* feat(terminal): make FORCE_HYPERLINK opt-out via settings, default on
The previous commit on this branch removed FORCE_HYPERLINK=1 from PTY
spawn env entirely. Revise: keep the historical default (on) so
existing users don't silently lose OSC-8 hyperlink emission from
tooling that checks this flag, but expose a toggle in Settings →
Terminal → Advanced so users with heavy rc files (oh-my-zsh +
powerlevel10k + nvm + pyenv + conda, etc.) can turn it off and reclaim
the shell-startup time that the extra subprocess branching and escape
emission was costing them.
- Add `terminalForceHyperlink: boolean` to GlobalSettings (default true)
- LocalPtyProvider gains an `isForceHyperlinkEnabled` hook; IPC wires
it from the live settings snapshot
- New SearchableSetting row in TerminalPane with zshrc-adjacent search
keywords so users troubleshooting slow startup find it
- Unit tests cover both default-on and opt-out paths
Wire the two escape sequences that TUIs and modern shells use to tell
the terminal about state that xterm.js doesn't handle natively:
- OSC 7 — shell emits its current working directory on every `cd`.
- OSC 133 — FinalTerm / iTerm semantic prompt marks (A/B/C/D with
optional exit code on D).
Parsing lives in a new pure `shell-integration.ts` helper with 21 unit
tests covering URI decoding, Windows UNC / drive-letter paths, all four
OSC 133 letters, the `key=value` hint suffix some emitters append, and
the ring-buffer cap on recorded marks.
Wiring follows the DEC 2031 / OSC 52 pattern from #896 and #952:
- Handlers registered per-pane in onPaneCreated, disposed symmetrically
in onPaneClosed.
- Two new refs (`paneCwdRef`, `paneSemanticMarksRef`) live on
TerminalPane and are passed through UseTerminalPaneLifecycleDeps.
- Semantic marks record the absolute buffer row (`baseY + cursorY`) so
future block-navigation consumers can scroll to them.
- A 10 000-entry ring buffer caps per-pane memory.
Shipped consumer: the terminal context menu grows a platform-aware
"Reveal in Finder / Show in Explorer / Open Folder" item that opens
the shell's current directory in the native file manager. The item
is hidden when no OSC 7 has been received, consistent with how other
terminal emulators surface shell-integration-dependent features.
No consumer yet for OSC 133 — recording is cheap and the consumer UX
(block-nav keybinds, exit-code gutters) deserves its own design pass.
xterm.js ships with no OSC 52 handler, so tmux, Neovim, fzf, and ripgrep
running inside Orca — locally or over SSH — silently fail to copy to the
system clipboard. Add an opt-in handler gated behind a new setting
`terminalAllowOsc52Clipboard` (default off because untrusted output
piped into the terminal can silently overwrite the clipboard).
Implementation mirrors the DEC 2031 wiring from PR #896: extract parsing
into a pure helper (parseOsc52) with a full unit-test suite, register
the handler in onPaneCreated and dispose it symmetrically in
onPaneClosed, and read the gate from settingsRef at fire time so the
toggle takes effect without recreating panes.
Clipboard *queries* (Pd = "?") are intentionally dropped — answering
them would leak the user's clipboard to any process writing to the
PTY — along with malformed payloads, oversized payloads (>128KB),
and payloads using unknown selection letters.
TUIs feature-gate on TERM_PROGRAM_VERSION (Neovim's terminal
autodetection, bat/delta styling hints, etc). We already set
TERM_PROGRAM=Orca but left the version unset, so tools can't distinguish
Orca builds or tell when version-gated features are safe to enable.
Seed process.env.ORCA_APP_VERSION from app.getVersion() at main startup
and read it from both PTY spawn sites (main-side local-pty-provider and
the daemon-side pty-subprocess, which inherits env via fork). Keeps
providers/local-pty-provider.ts free of electron imports.
* feat(terminal): broadcast theme changes to PTYs via DEC mode 2031
Claude Code's /theme auto reads Orca's background via OSC 11 at startup
(xterm.js answers that natively), so claude matches the terminal on
launch. But when Orca's theme flips mid-session, claude had no signal:
Orca never told running TUIs anything changed, so auto-theme stayed
stuck wherever it started.
This wires the DEC private mode 2031 + CSI 997 protocol that Neovim,
tmux, Kitty, and Ghostty use. A TUI sends CSI ?2031h to subscribe;
when the theme flips, the terminal pushes CSI ?997;1n (dark) or
CSI ?997;2n (light). The TUI then re-queries OSC 11 and repaints.
- Per-pane subscription flag and last-emitted mode live on TerminalPane.
- The CSI ?2031 h/l handlers are installed in onPaneCreated before the
PTY attaches so the child's initial subscribe is captured, and an
immediate push on enable seeds the current mode without a query.
- applyTerminalAppearance only emits CSI 997 when the resolved mode
actually flipped, so font/size/opacity tweaks that re-run the same
effect don't spam subscribed TUIs.
* refactor(terminal): extract mode 2031 helpers and add spam-gate tests
Pull the CSI 997 sequence builder and the subscribe/flip gate out of
applyTerminalAppearance so the appearance path and the subscribe-time
seed path in the lifecycle hook share one source of truth. Annotate the
?h / ?l parser handlers to make it explicit they observe (return false)
rather than suppress — compound DEC sequences like `?25;2031h` must
still flow to xterm's built-in handler. Adds unit coverage for the
no-op-on-no-flip gate, reconnect path, and per-pane tracking.
---------
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Pass FrontMatterBanner as headerSlot into RichMarkdownEditor so it renders
between the toolbar and the editor surface, keeping formatting controls at
the top of the pane.
* feat: cross-repo issues view with multi-repo selection
- Add multi-repo selection via RepoMultiCombobox with persisted defaultRepoSelection setting (null = sticky-all)
- Stamp repoId on GitHubWorkItem at the renderer fetch boundary; merge items from all selected repos with per-repo failure tracking
- Extract task-query helpers (tokenize/strip/parseTaskQuery) to src/shared and add unit tests
- Refactor NewWorkspacePage row to <div role=button> to allow nested interactive elements without invalid-HTML hydration errors
- fix(repo-combobox): toggle all-repos selection on repeat click
* fix(tasks): route Use CTA to item's own repo in cross-repo view
handleUseWorkItem previously referenced a removed 'repoId' state; use item.repoId so launching from a merged cross-repo list targets the correct repo.
* test(e2e): add tasks page smoke test
* fix(quick-open): don't clear Cmd+P input on unrelated store updates
The file-load effect depended on excludePaths, a new array reference
produced on any worktreesByRepo update. Each such update re-ran the
effect and called setQuery(''), wiping input the user was typing.
Split the open-reset into its own effect keyed on `visible`.
* fix(quick-open): remove manual scroll-to-top that fought user scroll
The rAF scrollTo(0,0) ran on every query/visible change, which meant
scrolling down and then typing (or any re-render that touched the
effect) snapped the list back up. The clipping issue it was papering
over is already solved by CommandList's p-2 padding, and cmdk's own
scrollIntoView handles keeping the selected item visible.
* fix(quick-open): stop refetching on unrelated worktreesByRepo updates
The excludePaths memo returned a new array reference on every
worktreesByRepo mutation, even when neither the active worktree's
path nor its siblings changed. That re-ran the file-load effect,
cleared files, and blinked the list on focus/scroll. Memoize on a
stable joined-string key instead.
The e2e reusable workflow defaulted to github.ref, which on
pull_request events is refs/pull/N/merge. GitHub does not compute
that ref when the PR has conflicts or before the merge commit is
ready, causing actions/checkout to fail with "couldn't find remote
ref refs/pull/N/merge" for reasons unrelated to the code.
Pass github.event.pull_request.head.sha from pr.yml so e2e checks
out the PR head directly.
Motivating failure: https://github.com/stablyai/orca/actions/runs/24768962365
Seen on #937.
Introduce a dedicated detected-agents store slice that replaces the
module-scoped detect-agents-cached cache, with a shared useDetectedAgents
hook driving AgentsPane, composer, and the new QuickLaunchButton in
TabBar. Extracts launchAgentInNewTab helper to share the startup-command
path used by the tab bar's + button.
Previously the dropdown's last row was always a "[input] — Google Search"
fallback, which the auto-select-first effect highlighted when no history
matched. Enter then navigated to Google even for URL-like inputs like
"www.example.com" — opposite of Chrome/Firefox behavior.
Now the top row mirrors what normalizeBrowserNavigationUrl would do:
for URL-like input it's the typed URL (Open URL), for bare queries it's
the search. The synthetic row is suppressed when a history row already
targets the same URL.
Cmd/Ctrl+click on an HTML file path in the terminal now renders the page
in a new browser tab instead of loading its source into Monaco. Mirrors
PR #908's "Open Preview to the Side" entry point and matches how we
already route http(s) URLs into Orca's browser.
The hover tooltip now shows a distinct "open in browser" hint for HTML
paths so the different behavior is discoverable.