Commit Graph
2191 Commits
Author SHA1 Message Date
Neil b309f49ee3 Allow large text files to open in editor (#1367) 2026-05-02 18:12:08 -07:00
SSWSer 6f606114a0 feat: add PowerShell 7+ support for Windows terminals
Adds PowerShell 7+ support for Windows terminal selection, with Auto resolving to pwsh.exe when available and falling back to Windows PowerShell.
2026-05-02 16:06:11 -07:00
Leynier Gutiérrez GonzálezandClaude Opus 4.7 37b2996ee0 feat(editor): add toggle to show minimap in file editor (#1346)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 00:40:53 -07:00
Neil 9f2ab5ed0d fix(terminal): mirror VS Code GPU rendering controls (#1344) 2026-05-02 00:04:14 -07:00
Brennan BensonandOrca 70befde774 feat(github): per-repo issue-source selector (#1317)
Co-authored-by: Orca <help@stably.ai>
2026-05-01 21:48:51 -07:00
JinjingandOrca 547448e15c refactor: rename pet → sidekick across codebase (#1334)
Renames the experimental pet overlay to "sidekick" with themed character
names (Claude the Mage, OpenCode the Rogue, Gremlin the Trickster).
Covers IPC channels, preload API, persisted UI state, settings flag,
on-disk userData path, components, and types.

Deletes the standalone pet-overlay design mock.

Co-authored-by: Orca <help@stably.ai>
2026-05-01 18:40:52 -07:00
JinjingandOrca d287342f99 feat: add experimental pet overlay (#1331)
* feat: add experimental pet overlay

Adds an opt-in 3D pet overlay pinned to the bottom-right. Gated behind
an experimental flag so three.js + GLB models stay out of the renderer
bundle for users who never enable the feature. Ships with four bundled
models plus user-uploaded custom GLBs via a pet:import IPC; a status-bar
segment provides model picker + hide toggle.

See docs/design/pet-overlay.md for the full design.

Co-authored-by: Orca <help@stably.ai>

* refactor(pet): replace 3D GLB models with 2D webp images

Co-authored-by: Orca <help@stably.ai>

* chore(pet): compress pet webp images and remove unused assets

Reduce resources/claude.webp, gremlin.webp, opencode.webp sizes; drop
the obsolete pet-overlay design doc and compress-pet-glb script now
that the GLB pipeline has been replaced by 2D webp images.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-01 16:18:21 -07:00
bbd77c2449 fix(worktree): restore sparse checkout without per-poll git subprocesses (#1316)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Colin <colin.chambachan@gmail.com>
2026-05-01 13:25:22 -07:00
d4bbf473c1 Support wiki-style Markdown document links (#1282)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-05-01 13:15:08 -07:00
Brennan BensonandOrca 12858f37e3 feat(github): source-repo indicator + surface issue fetch errors (#1250)
Co-authored-by: Orca <help@stably.ai>
2026-04-30 22:53:37 -07:00
Brennan BensonandOrca 2e372779b6 fix(persistence): add 'inline-agents' to cardProps for existing experiment opt-ins (#1293)
Co-authored-by: Orca <help@stably.ai>
2026-04-30 18:49:09 -07:00
Jinwoo Hong 000d4dc08b Revert sparse checkout worktree creation (#1290) 2026-04-30 15:47:40 -07:00
Brennan BensonandOrca 0f3aaab9f8 feat: default sidebar sort to 'recent' instead of alphabetical (#1285)
Co-authored-by: Orca <help@stably.ai>
2026-04-30 11:07:54 -07:00
79faafcc98 Add sparse checkout worktree creation (#1131)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-04-30 00:43:00 -07:00
NeilandOrca a02987e604 feat(settings): replace task-icon toggle with Tasks-button toggle, expose in View menu (#1271)
Co-authored-by: Orca <help@stably.ai>
2026-04-29 21:13:13 -07:00
Neil 020780c4dd feat(hooks): restore always-trust repo option
Restores repo-level always-trust for orca.yaml hooks and surfaces CLI warnings when --run-hooks is omitted and a hook is skipped.
2026-04-29 20:47:04 -07:00
Jinwoo HongandOrca 7993fb5aaa feat(status-bar): show inline usage for inactive accounts in switcher (#1259)
Co-authored-by: Orca <help@stably.ai>
2026-04-29 19:29:11 -07:00
Brennan BensonandOrca c1620496cf feat(agent-cockpit): replace bottom-panel dashboard with inline per-card agents list (#1251)
Co-authored-by: Orca <help@stably.ai>
2026-04-29 18:51:24 -07:00
mcd77 89f41bfda2 feat(hooks): gate orca.yaml execution on trust
Audited and rebased PR 1138. Squashed to remove the original untrusted commit stack; hook execution now requires explicit UI trust or CLI --run-hooks opt-in.
2026-04-29 18:29:53 -07:00
Jinwoo HongandOrca 2329f56180 Exit beta: SSH badge removal + always-on daemon PTY (#1253)
Co-authored-by: Orca <help@stably.ai>
2026-04-29 17:48:24 -07:00
faf949835f feat(rate-limits): add Gemini and OpenCode Go usage tracking (#1023)
* feat(rate-limits): add Gemini and OpenCode Go usage tracking

- Extend provider union and RateLimitState with 'gemini' and 'opencode-go'
- Add opencodeSessionCookie to GlobalSettings (password input in GeneralPane)
- Add GeminiIcon and OpenCodeGoIcon to status bar icons
- Implement gemini-usage-fetcher: OAuth creds from ~/.gemini/oauth_creds.json,
  auto-refresh expired tokens (including server-side 401 retry), loadCodeAssist
  project resolution, retrieveUserQuota with pro/flash bucket mapping
- Implement opencode-go-usage-fetcher: cookie-based POST to opencode.ai/_server,
  two-step workspace+subscription fetch, regex parse of text/javascript response
- Wire both fetchers into RateLimitService via Promise.allSettled for isolation
- Add formatWindowLabel() replacing hardcoded '5h'/'wk' strings in StatusBar
- Render Gemini and OpenCode Go segments in StatusBar and tooltip
- 28 new tests across fetchers, service, and label formatter

Closes #1022

* fix(persistence): additive merge for statusBarItems on load

New providers (gemini, opencode-go) were invisible to users with existing
settings because the persisted statusBarItems array (without the new entries)
overwrote the defaults. Union saved items with current defaults so new
providers appear automatically after upgrade without user intervention.

* feat(rate-limits): add Gemini multi-bucket usage

Restore provider auth wiring, preserve Gemini model buckets in detailed views, and keep compact status rendering summary-only. Avoid embedding OAuth client secrets by failing closed for auth.json refreshes.

* fix(gemini-oauth): resolve symlinks and use known-path + bundle-dir extraction

Replace the recursive directory walker with two targeted strategies:
1. Known paths: checks explicit Homebrew/Nix/npm layouts without walking.
2. Bundle dir: walks up from the binary to find package.json, then scans
   the bundle dir for hash-named oauth2 chunks.

Also threads the refresh token as a plain string through
tryRefreshTokenFromBundle so both the oauth_creds.json and auth.json
paths share one refresh flow without coupling to either struct.

Windows: uses 'where gemini' and splits on newlines for multi-result output.

* fix(status-bar): fix Gemini bucket display — names, window size, model context

Three issues fixed:

1. windowMinutes was computed as time-remaining-until-reset instead of the
   fixed window size. Gemini buckets are always 1-hour windows; use the
   constant 60 so labels read "93% Pro 1h" instead of "93% 47m".

2. Unknown bucket names now humanize gracefully. Unknown model IDs get the
   "gemini-" prefix stripped and title-cased ("gemini-3.0-ultra" → "3.0 Ultra")
   instead of showing "Unknown (gemini-3.0-ultra)". Added more known model
   mappings (2.0 Flash, 2.0 Flash Lite, 1.5 Pro, 1.5 Flash, Flash Lite).

3. Status bar segment now shows the most-constrained bucket name next to the
   percentage so users know which model is the binding constraint.

Pure unit tests for getBucketName/deriveSessionSummary extracted to
gemini-bucket-helpers.test.ts to keep gemini-usage-fetcher.test.ts
under the 300-line lint limit.

* feat(status-bar): show all Gemini buckets individually in status bar

Instead of showing only the most-constrained bucket summary, render each
bucket by name with its remaining percentage (e.g. "Flash 93% · 3.1 Pro Preview 7%").
Falls back to the session/weekly window display for providers without buckets.

* fix(status-bar): show only Flash and 3.1 Pro Preview buckets in Gemini segment

Filter to the two most relevant buckets (Flash + 3.1 Pro Preview) to avoid
cluttering the bar. Falls back to session summary if neither bucket is present.

* fix(icons): replace GeminiIcon with official 2025 multicolor gradient

The previous icon used a simple linear gradient (blue→purple→red).
The official Google Gemini 2025 icon uses 11 blurred ellipses (feGaussianBlur)
stacked under an alpha mask to produce the characteristic multicolor glow effect.

Each instance gets unique filter/mask IDs via a module-level counter to prevent
ID collisions when the icon is rendered multiple times on the same page.

* feat(opencode): add monthly limits, workspace override, and improved cookie handling

* fix(opencode): show correct status when session cookie is missing

* fix(rate-limits): discard stale data on unavailable and show errors in tooltip

* fix(rate-limits): invalidate stale data when opencode config changes

* fix(security): resolve vulnerabilities and harden rate-limit fetchers

* fix: address critical security, performance and concurrency issues identified during code review

* fix(build): resolve claude rate-limit export and broken type definitions

* fix(build): remove unused code in claude-fetcher after rebase

* Fix claude fetcher

* fix: resolve synchronization, performance, and robustness issues in AI providers

- Convert synchronous file I/O to asynchronous to prevent main process blocking
- Implement concurrency limiting in recursive directory copying to avoid EMFILE errors
- Persist refreshed Gemini OAuth tokens to disk and improve project ID resolution
- Fix OpenCode Go fetcher to correctly handle workspace overrides and robustly resolve IDs
- Refine scraping regexes to handle nested objects and improve resilience
- Update status bar bucket names and fix Gemini model mapping typos
- Ensure proper handling of async operations in background services

* feat(gemini): deduplicate quota buckets and update model mappings

* fix(rate-limits): preserve stale data for Gemini and OpenCode Go on fetch errors

* fix: remove out-of-scope Linear changes and restore files to upstream/main

* fix(opencode-go): handle React Flight wire format and duplicate keys

The opencode.ai page uses React Server Components. Usage keys like
monthlyUsage appear twice: once as `key:$R[N]={...}` with real data
and once as `key:null` inside a billing component. Render order varies,
so on refresh the null could appear first, causing monthly to vanish or
show 100% from a sibling sub-object.

Replace flat regex extraction with extractUsageBlock, which:
- Iterates all occurrences of each key
- Skips null assignments (no { in 30-char window after colon)
- Handles the $R[N]= token between colon and opening brace
- Validates usagePercent + resetInSec as direct fields before accepting

Add regression tests using the real React Flight HTML format.

* fix(rate-limits): address PR review feedback from nwparker

Scope reverts (out-of-scope changes removed):
- Revert codex-accounts async refactor (fs-utils.ts, service.ts)
- Revert filesystem-auth.ts path-auth reordering
- Revert filesystem-mutations.ts bundled changes
- Revert repos.ts handler move; keep only -- separator fix
- Revert relay/fs-handler-git-fallback.ts RegExp try/catch
- Revert fs-handler.ts stat→lstat, useFileDeletion.ts !isRemote guard
- Revert persistence.ts statusBarItems additive merge

Feature fixes:
- Validate opencodeWorkspaceId override with ^(wrk|wk)_[A-Za-z0-9]+$ before URL interpolation
- Remove internal 5-min cache from fetchGeminiRateLimits (service polls already)
- Make saveGeminiCredentials atomic using tmp-file + rename pattern
- Add fetchGeneration counter to OpenCode to discard stale mid-flight results
- Add console.warn on safeStorage decrypt failure in persistence.ts
- Add geminiCliOAuthEnabled opt-in setting (default: false) with UI toggle and risk disclosure
- Use per-candidate AbortControllers in opencode-go fetcher
- Add fragility comments to brace-depth parsers in opencode-go fetcher

Refactor (keep under 300-line lint limit):
- Extract gemini-bucket-formatting.ts from gemini-usage-fetcher.ts
- Extract opencode-go-page-scraper.ts from opencode-go-usage-fetcher.ts

Co-authored-by: Orca <help@stably.ai>

* fix(rate-limits): restore OpenCode Go workspace ID regex + cleanup

- opencode-go-usage-fetcher: regex accidentally shipped as `\\s*` (matches
  literal backslash-s) instead of `\s*` during the review-feedback commit,
  breaking workspace ID extraction when no override is configured —
  parseWorkspaceIds always returned []. 10 tests were failing as a result.
- gemini-usage-fetcher: drop unused `_force` parameter left over from the
  removed 5-min internal cache; update service.ts call site.
- codex-accounts test fixtures: add missing `geminiCliOAuthEnabled` to
  createSettings() to fix pre-existing typecheck errors introduced when
  the new setting was added.

Co-authored-by: Orca <help@stably.ai>

* test(rate-limits): stabilize Gemini fetcher tests, silence max-lines

- Rewire Gemini fetcher tests to mock the CLI-credential extractor at the
  module boundary instead of stubbing every fs/child_process call. The
  extractor is a self-contained dependency with a simple async contract,
  and was previously being reached through sync-fs mocks that stopped
  matching after the extractor was refactored to node:fs/promises — three
  tests were silently failing as a result. With this change, all 17
  Gemini fetcher tests pass.
- Replace the "proceeds with empty projectId" test (which asserted that an
  empty projectId still hits the quota API — the current fetcher
  correctly short-circuits to an actionable error instead) with a test
  that documents the new behavior, plus an explicit test for the
  geminiCliOAuthEnabled=false unavailable path.
- Add a max-lines disable pragma to service.test.ts matching the one
  already on service.ts, so the rate-limit fetch-ordering contract and
  its tests remain reviewable as a single unit.

Co-authored-by: Orca <help@stably.ai>

* fix(codex-accounts): revert runtime-home-service async refactor

The review explicitly asked to drop the codex async refactor from this PR
(fires floating promises from the constructor via void, and the prepare*
helpers no longer block on sync completion — that is a behavior change
around account prep racing with launch that deserves its own design
discussion, not a drive-by).

Earlier reverts removed fs-utils.ts and service.ts but left
runtime-home-service.ts untouched, which was causing the matching
runtime-home-service.test.ts suite (11 tests) to fail in CI. This restores
the file to main so the suite is green and the PR stays scoped to the
Gemini / OpenCode Go feature.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-04-29 16:55:32 -07:00
Neil c25b61c558 fix(mac): request media permissions dynamically (#1233) 2026-04-28 16:18:22 -07:00
Brennan BensonandOrca ae994e7032 feat(settings): gate Agent Dashboard behind experimental setting (#1219)
Co-authored-by: Orca <help@stably.ai>
2026-04-28 14:41:37 -07:00
NeilandOrca 1559cd290f fix(agents): detect Kiro under its real binary name kiro-cli (#1223)
Co-authored-by: Orca <help@stably.ai>
2026-04-28 12:37:50 -07:00
Jinwoo HongandOrca c9391e203f feat(orchestration): add inter-agent orchestration system (#1188)
Co-authored-by: Orca <help@stably.ai>
2026-04-28 12:21:31 -07:00
Brennan BensonandOrca 2c494da361 feat(agents): Agent Dashboard v2 — hook endpoint discovery, dashboard UI, sidebar status overlay (#1147)
Co-authored-by: Orca <help@stably.ai>
2026-04-28 10:13:32 -07:00
6233b8e8a4 feat: Add GitHub PR review workflow to tasks (#1172)
* Render GitHub markdown safely

Co-authored-by: Orca <help@stably.ai>

* Support PR line comment ranges in diffs

Co-authored-by: Orca <help@stably.ai>

* Add GitHub item dialog review workflow

Co-authored-by: Orca <help@stably.ai>

* Make attribution tests environment-safe

Co-authored-by: Orca <help@stably.ai>

* Wire GitHubItemDialog for row clicks and bound file cache

Row clicks in the Tasks GitHub list now open the new GitHubItemDialog
instead of the legacy GitHubItemDrawer, making the PR review workflow
reachable from the main entry point. The unused drawer and its state
are removed.

Also bounds prFileContentCache at 64 entries with LRU eviction so the
module-level map can no longer grow without limit across many PR
openings in a session.

Co-authored-by: Orca <help@stably.ai>

* Convert GitHubItemDialog to a right-side drawer

Replaces the centered Dialog modal with a Sheet drawer (side=right,
max-w 960/1100/1280 across breakpoints) so the review surface slides
in from the side instead of popping over the task list.

Co-authored-by: Orca <help@stably.ai>

* Address review feedback on GitHub work-item details

- Restore full pagination in listAssignableUsers (REST /assignees --paginate)
  so repos with >100 assignees no longer silently drop users from the picker.
- Drop redundant REST /users/<login> fan-out in getMentionParticipants; the
  aliased GraphQL query already returns login/name/avatarUrl.
- Parallelize mention-participant lookup with checks/participants fetch.
- Avoid mutating caller-provided objects in mergeGitHubUsers.
- Type addIssueComment / addPRReviewComment(Reply) preload wrappers as
  Promise<GitHubCommentResult> instead of Promise<unknown>.
- Use typeof startLine === 'number' to harden the review-comment guard.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-04-28 00:11:39 -07:00
NeilandOrca 0e8571b58d Tabbed Create Workspace dialog (Quick / Create from…) (#1191)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 22:48:04 -07:00
Brennan BensonandOrca 4279fc8075 feat(agent-hooks): on-disk endpoint discovery for surviving PTYs (v2) (#1196)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 21:49:18 -07:00
Brennan BensonandOrca 3fe7162634 fix(sidebar): don't bump lastActivityAt when worktree is activated by click (#1169)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 15:53:52 -07:00
Kevin Acuña 7f6d42134d fix: resolve letter window shortcuts by layout-aware key (Dvorak/Colemak/AZERTY) (#1015) 2026-04-27 14:09:35 -07:00
Brennan BensonandOrca 76139f88a9 fix: support upstream remote as base ref for fork workflows (#1186)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 13:51:19 -07:00
JinjingandOrca cfe247659a opt: disable GitHub attribution by default (#1173)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 09:53:33 -07:00
NeilandOrca 5fe4fd4853 fix(pty): honor Windows shell selection on daemon path + shell-specific icons (#1164)
* fix(pty): honor Windows shell selection on the daemon path + shell-specific icons

The "+" menu picker and the Settings → Default Shell preference both set a
shellOverride, but the daemon-backed PTY path never forwarded it. Every
Windows terminal ended up as PowerShell (or cmd.exe via COMSPEC) no matter
what the user picked.

Fix:
- Thread shellOverride through daemon-pty-adapter → createOrAttach RPC →
  terminal-host → pty-subprocess, and actually resolve the correct launch
  args (chcp for CMD, $PROFILE dot-sourcing for PowerShell, /mnt/<drive>
  cwd translation for WSL) on the daemon path.
- Extract the Windows shell-args decision into a shared helper
  (resolveWindowsShellLaunchArgs) so LocalPtyProvider and the daemon
  spawner cannot drift again.
- In ipc/pty.ts, fall back to the persisted terminalWindowsShell setting
  when no per-tab override is sent, so the daemon path honors the user's
  Default Shell preference the same way LocalPtyProvider already did.

UI polish on the "+" dropdown and tab strip:
- Drop the "Default" tag next to the top entry (takes too much space).
- Rename "Command Prompt" → "CMD Prompt" to fit next to the Ctrl+T hint.
- Replace the generic terminal glyph with brand-style icons per shell
  (ShellIcon). Both the "+" menu and the per-tab strip use the same
  icon set so a WSL tab is visually distinct from a PowerShell tab.

Co-authored-by: Orca <help@stably.ai>

* fix(daemon): add oxlint-disable max-lines to daemon-server

CI counts 312 non-blank/non-comment lines for daemon-server.ts after the
shellOverride plumbing was added, exceeding the 300-line .ts override.
The file is a single RPC route table; splitting it would leak the host
reference across modules for no readability win, so add a scoped disable
with the rationale.

Co-authored-by: Orca <help@stably.ai>

* chore(lint): disable max-lines on pre-existing 312-line tabs-hydration test

Unrelated to the shell-selection fix, but surfaced on PR CI: the file is
right at oxlint's 300-line .ts ceiling; each case in the table is a
minimal fixture + assertion, so splitting it across files would scatter
closely related regression coverage for a single reducer.

Co-authored-by: Orca <help@stably.ai>

* chore(lint): disable max-lines on shared text-search module

The shared text-search module exceeds oxlint's 300-line .ts ceiling. It
is the single source of truth for rg arg construction, rg --json parsing,
git-grep submatch parsing, and relative-path normalization shared between
the local main process and the SSH relay. Re-splitting it would
re-introduce the maxBuffer divergence the design doc explicitly calls out.

Also reverts two speculative oxlint-disable directives on daemon-server
and tabs-hydration.test — those files were not the offender; CI's error
message elides the file path but running the lint locally against the
PR-merge commit pinpointed text-search.ts.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-04-27 00:47:15 -07:00
JinjingandOrca 099c4089a7 refactor(search): share rg/git-grep logic between main and relay (#1157)
Extracts rg and git-grep search logic into src/shared/text-search.ts so
the local main and SSH relay paths stop reinventing arg construction,
JSON parsing, submatch regex, and accumulator/truncation semantics.

Fixes silent truncation in the relay: searchWithRg used execFile with
a 50MB maxBuffer cap that rg --json easily exceeds on large repos,
dropping matches with no error surfaced to the user. The relay now
streams via spawn, matching the local path.

See docs/design/share-text-search.md for full rationale.

Co-authored-by: Orca <help@stably.ai>
2026-04-27 00:06:00 -07:00
NeilandNeil Parker 98ba693481 feat(terminal): WSL shell support for Windows users (#1154)
Co-authored-by: Neil Parker <nwparker@anthropic.com>
2026-04-26 22:43:54 -07:00
Brennan Benson 8b87acd72d feat(memory): add Memory & CPU status-bar popover (#1146) 2026-04-26 21:42:06 -07:00
Jinwoo HongandOrca f11aacc795 feat(ssh): add port forwarding for remote workspaces (#1145)
Co-authored-by: Orca <help@stably.ai>
2026-04-26 19:21:26 -07:00
f5ad7aa249 feat(settings): import Ghostty config with preview, color overrides, opacity and blur (#1001)
* feat(settings): add Ghostty config import

Add safe one-shot Ghostty import with preview and success summary,
and probe documented Ghostty config paths before applying changes.

Refs #958

* chore(git): ignore atl artifacts

* feat(settings): map font-weight, cursor-blink and focus-follows-mouse from Ghostty

Adds three safe direct-mapping Ghostty keys that have clear equivalents
in GlobalSettings: font-weight, cursor-style-blink, and focus-follows-mouse.

Refs #958

* feat(settings): expand Ghostty import to support colors, opacity and option-as-alt

- Add TerminalColorOverrides type grouping 21 optional xterm ITheme fields
- Add terminalBackgroundOpacity, terminalPanePaddingColor, terminalPaddingBalance
  to GlobalSettings
- Extend parser to collect repeated keys as string[] (needed for palette lines)
- Map background-opacity, background, foreground, cursor-color,
  selection-background/foreground, palette (0-15), window-padding-color,
  window-padding-balance, macos-option-as-alt in mapper
- Merge terminalColorOverrides into xterm ITheme at theme resolution; apply
  opacity as rgba() with allowTransparency enabled
- Accept hex colors with or without leading # (Ghostty omits it)
- Fix preview diff to use deep equality for object values so already-applied
  color overrides no longer reappear on next import

* feat(settings): support background-blur-radius and window-padding-color extend in Ghostty import

- Map background-blur-radius > 0 to windowBackgroundBlur: true; apply
  vibrancy on macOS and backgroundMaterial acrylic on Windows at window
  creation (blur requires restart — no hot-reload IPC exists)
- Accept window-padding-color = extend/background as valid Ghostty values;
  both map to default Orca padding behavior (undefined field) instead of
  landing in unsupportedKeys
- Split mapper.test.ts into domain-scoped describes to stay under 300-line limit

* feat(settings): add Window section to Terminal settings panel

Expose terminalBackgroundOpacity, windowBackgroundBlur, terminalPaddingBalance,
terminalPanePaddingColor, and terminalColorOverrides in the settings UI so
imported Ghostty values can be viewed and changed manually.

- New TerminalWindowSection component (extracted from TerminalPane to stay
  under the 400-line limit)
- Collapsible color overrides sub-section with ColorField for all 21 xterm
  ITheme fields grouped as base, ANSI normal, and ANSI bright
- Reset button clears all color overrides at once
- Window blur toggle shows restart-required note (blur applies at window
  creation, no hot-reload IPC exists)
- Search entries added for all new controls

* feat(settings): expand Ghostty import with scrollback, padding, divider, cursor and word-chars keys

Map 9 additional Ghostty keys to Orca settings:

- split-divider-color → terminalDividerColorDark + terminalDividerColorLight
  (single value applies to both; Ghostty has no dark/light distinction)
- unfocused-split-opacity → terminalInactivePaneOpacity (direct float 0-1)
- scrollback-limit → terminalScrollbackLimit; applied to xterm scrollback option
- window-padding-x / window-padding-y → terminalPaddingX/Y; applied as CSS
  vars --pane-padding-x / --pane-padding-y in terminal.css
- cursor-text → terminalColorOverrides.cursorAccent (xterm ITheme field)
- bold-color → terminalColorOverrides.bold (persisted; xterm ITheme has no
  bold field yet — stored for future xterm upgrade)
- cursor-opacity → terminalCursorOpacity; blended into cursor rgba at theme
  resolution time
- selection-word-chars → terminalWordSeparator; applied to xterm wordSeparator
- mouse-hide-while-typing → terminalMouseHideWhileTyping field added; renderer
  application deferred (needs per-pane disposable + global mousemove listener)

* feat(settings): expose new Ghostty-imported settings in Terminal Settings UI

- Window section: scrollback limit, horizontal/vertical padding, hide mouse
  while typing toggle, cursor text and bold color in Color Overrides
- Cursor section: cursor opacity NumberField
- Advanced section: word separators text input
- Search entries added for all new controls
- terminalDividerColorDark/Light and terminalInactivePaneOpacity skipped —
  already present in Theme and Pane Styling sections respectively

* feat(terminal): implement mouse-hide-while-typing per pane

Register terminal.onData → cursor:none and mousemove → restore, scoped to
the pane container element. Uses the existing IDisposable per-pane pattern
(same as selectionDisposablesRef). Cleans up on pane close and effect teardown.

* refactor(settings): address ghostty import code review findings

- Centralize GhosttyImportPreview type in shared/types (remove duplicate from mapper)
- Fix parser to strip inline comments without breaking hex color values (#1a1a1a)
- Extract HEX_COLOR_RE to shared/color-validation to avoid duplication
- Remove redundant Number.isNaN checks after Number.isFinite (4 sites)
- Replace unsafe catch-all assignment with explicit font-family branch
- Migrate 280-line if-chain in mapGhosttyToOrca to FIELD_PARSERS registry
- Add human-readable setting labels in GhosttyImportModal via setting-labels map
- Add clarifying comment in index.ts re JSON.stringify undefined behavior

* fix(settings): harden ghostty import from judgment-day review

- Surface readFile errors in GhosttyImportPreview.error instead of
  showing misleading 'No config found' on permission denied
- Guard handleApply against double-apply when already applied
- Strip surrounding quotes from parsed config values (font-family)
- Return null from palette handler when all entries fail validation
- Inform user when background-blur-radius radius is not preserved
- Add valuesEqual key-order stability via stableStringify
- Normalize hex colors to #-prefixed format across all color mappers
- Reject blank values before numeric parsing (Number('') === 0 trap)
- Remove selection-word-chars mapping (inverted xterm semantics)
- Guard window-padding-x/y against negative integers
- Reactive mouse-hide-while-typing on existing panes when setting toggles
- Merge terminalColorOverrides on import instead of replacing

* fix(ghostty): drop broken imports, tighten parsing, prompt restart for blur

Review found three high-impact issues in the Ghostty import: scrollback-limit
semantics are inverted/rescaled (Ghostty is bytes with 0=unlimited, xterm is
rows with 0=disabled), and window-padding-color + window-padding-balance set
CSS custom properties (--pane-padding-color, --pane-padding-balance) that
have no consuming rule anywhere in the tree — so users confirming "changes"
to those keys would see nothing happen.

Because none of the three keys have a safe mapping today, drop them from the
import and remove the dead UI controls + GlobalSettings fields + CSS var
plumbing. The mapper now lists them as unsupportedKeys alongside the
existing window-decoration / keybind / custom-shader entries.

Other fixes in the same review:

- allowTransparency now clears when background-opacity returns to 1 (prior
  code only ever set it to true, leaving a stale flag with measurable render
  cost).
- background-blur-radius = 0 no longer emits a misleading "radius value not
  preserved" note (0 cleanly maps to blur=false with no radius to lose).
- Add a 1 MB size cap on the config read so a pathological or symlinked file
  cannot OOM the main process.
- Make handleApply async and surface IPC errors inline in the modal instead
  of flipping straight to "Import complete" on failure.
- Type settings.previewGhosttyImport as Promise<GhosttyImportPreview> in
  preload so shape drift is caught at compile time.
- Make stableStringify recursive so future nested settings round-trip
  cleanly through valuesEqual.
- Restrict window-padding-x/y and background-blur-radius to decimal ints;
  prior code accepted exponent notation (1e10 sails through Number.isInteger)
  and would have landed absurd values in the store.
- Window blur now shows a "Restart required" banner with a Restart now
  button when the setting differs from the mount-time snapshot, mirroring
  the ExperimentalPane daemon pattern. Blur only applies at BrowserWindow
  creation on macOS/Windows.

Co-authored-by: Orca <help@stably.ai>

* feat(settings): move Ghostty import trigger to Terminal section header

Per review feedback: the "Import from Ghostty" row was taking its own slot
in the Terminal settings list alongside real configuration sections. Move
the trigger into the Terminal section's header (upper-right corner) as a
headerAction, next to the Terminal heading — it's a one-shot action, not a
setting.

- SettingsSection gains an optional `headerAction` slot rendered to the
  right of the section title/description.
- The useGhosttyImport hook is lifted from TerminalPane into Settings.tsx
  so the section header button (owned by Settings.tsx) and the modal
  (still rendered inside TerminalPane) share one state instance.
- TerminalPane drops its own "Import" section + the TERMINAL_GHOSTTY_IMPORT
  search entry group is no longer referenced there.
- Button carries the official Ghostty mark as a 16x16 icon so it reads
  clearly as a cross-app import even before users parse the label.

useGhosttyImport now accepts `GlobalSettings | null` so the parent can call
it above the pre-load spinner guard without violating hook ordering; the
apply path no-ops until settings arrive. Related test file updated to
pass the new `ghostty` prop and to assert the trigger is *not* rendered
inside TerminalPane anymore.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-04-26 17:00:46 -07:00
Ramzi 8e44541c4c Add task provider icon setting (#1128) 2026-04-26 16:01:16 -07:00
JinjingandOrca 0431dd51f5 feat: improve SSH file search with configurable limits and rg fallback (#1130)
* feat: improve SSH file search with configurable limits and rg fallback

- fix: address lint issues (string template, ternary, max-lines via extraction)

Co-authored-by: Orca <help@stably.ai>

* fix: typecheck and improve rg listing correctness

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-04-26 13:38:34 -07:00
fe6eecba0c feat(sidebar): improve PR checks and comments (#1134)
* feat(sidebar): improve PR checks and comments

Co-authored-by: Orca <help@stably.ai>

* fix(sidebar): classify bot comments via GitHub user.type instead of login heuristic

Third-party review bots like qodo-ai-reviewer, coderabbitai, and sonarcloud
don't follow the [bot] suffix or "bot"/"automation" substring convention, so
the regex-based detector misclassified them as human. Plumb REST
`user.type === 'Bot'` and GraphQL `author.__typename === 'Bot'` through to the
renderer as an authoritative isBot flag; fall back to the login heuristic only
when the data source can't report it.

Co-authored-by: Orca <help@stably.ai>

* fix(sidebar): allowlist AI review services that sign in as User accounts

qodo-ai-reviewer, coderabbitai, codium-ai and similar third-party review
services register as regular GitHub user accounts, so REST `user.type` is
"User" and their logins contain no "bot"/"automation" tokens. The previous
fix relied on the GitHub-reported type, which fails for these. Add an
explicit substring allowlist of known automation services so they still
land in the Bots tab.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-04-26 13:28:09 -07:00
Jinwoo HongandOrca 7720ed136c feat(tasks): server-side scope filtering, pagination, and draft bug fix (#1135)
Co-authored-by: Orca <help@stably.ai>
2026-04-26 12:30:22 -07:00
NeilandOrca ae9ddeab5c feat(cursor): first-class Cursor CLI agent status via hooks.json (#1108)
* feat(cursor): first-class Cursor CLI agent status via ~/.cursor/hooks.json

Give cursor-agent the same hook-driven status pipeline Claude/Codex/Gemini/
OpenCode already use so working/done/permission transitions show the proper
sidebar spinner instead of falling back to title heuristics — cursor-agent
only sets its OSC title to the literal string "Cursor Agent" during a turn,
so title-based detection cannot see working→done transitions on its own.

- New CursorHookService installs a managed shell script and registers it
  under ~/.cursor/hooks.json for beforeSubmitPrompt, preToolUse, postToolUse,
  postToolUseFailure, beforeShellExecution, beforeMCPExecution, stop, and
  afterAgentResponse (the subset that marks turn boundaries and surfaces
  in-flight tool context).
- AgentHookServer gains a /hook/cursor route and a normalizeCursorEvent
  mapping the camelCase cursor events to working/done/waiting, with tool
  previews for preToolUse/shell/MCP and lastAssistantMessage from
  afterAgentResponse. stop with status != "completed" surfaces as
  interrupted (matches Claude's is_interrupt behavior).
- cursorHookService joins the startup install loop alongside Claude/Codex/
  Gemini, and the IPC status handler is exposed via preload.
- 'cursor' added to WellKnownAgentType and to the renderer's
  WELL_KNOWN_LABELS so the dashboard prints "Cursor" rather than the raw
  'cursor' id.

Verified end-to-end against a real cursor-agent 2026.04.17-787b533 binary:
a mock hook receiver pointed at by ~/.cursor/hooks.json observes
beforeSubmitPrompt → stop for a live turn.

Co-authored-by: Orca <help@stably.ai>

* feat(cursor): wire hook events into sidebar spinner + unread pipeline

The initial hook wiring landed behind AGENT_DASHBOARD_ENABLED, which is
still false. That meant cursor-agent panes lit up no spinner and no
unread indicator — cursor's native OSC title stays literally "Cursor
Agent" across a turn, so title-based detection cannot transition.

Plumb cursor's hook stream into the existing, shipped title-tracker
pipeline (the one Claude/Codex/Pi drive working/idle/unread off) by
synthesizing OSC title sequences in the main-process hook listener:

  - `working`  → `\x1b]0;⠋ Cursor Agent\x07`   (braille prefix → working)
  - `waiting`  → `\x1b]0;Cursor - action required\x07\x07`
  - `done`     → `\x1b]0;Cursor ready\x07\x07`

The two trailing BELs on done/waiting are load-bearing: the unread badge
keys off BEL (0x07 outside any OSC), and cursor-agent emits none on its
own. The first BEL is consumed as the OSC terminator; the second fires
the bell detector.

Also treats the bare native "Cursor Agent" title as a no-op in
`detectAgentStatusFromTitle` so cursor's own per-turn re-emissions cannot
stomp our synthesized working state back to idle. `isClaudeAgent`
excludes cursor-bearing braille titles so the Claude prompt-cache timer
doesn't fire for cursor panes.

Scope: the hook server + cursor install run unconditionally now, but
Claude/Codex/Gemini installs stay gated behind AGENT_DASHBOARD_ENABLED,
so only cursor events flow through the pipeline. No dashboard surface
is turned on.

Verified end-to-end in Electron (dev build): launched cursor-agent via
the Cursor menu item, submitted three prompts (including a tool-use
turn reading package.json). Observed tab title flip to "Cursor ready"
on done, and the tab + worktree both transitioned to unread
(unreadTerminalTabs[tabId]=true, worktree.isUnread=true). Parallel
Claude Code pane untouched.

Co-authored-by: Orca <help@stably.ai>

* fix(cursor): animate spinner frames + filter bare native title so the spinner doesn't go solid mid-turn

cursor-agent re-emits its bare "Cursor Agent" OSC title on every internal
redraw, which was stomping the single synthesized "⠋ Cursor Agent" frame
in runtimePaneTitlesByTabId within milliseconds and flipping the sidebar
dot back to solid. Two-part fix:

- Main: drive an 80ms Pi-style braille spinner from the cursor hook
  channel, keyed by paneKey, torn down on pty exit via a new
  registerPaneKeyTeardownListener hook in ipc/pty.
- Renderer: drop bare "Cursor Agent" titles in pty-transport so cursor's
  native re-emissions cannot overwrite the synthesized working/idle
  titles.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-04-25 23:14:31 -07:00
NeilandOrca d4399b4c92 feat(terminal): add programming-ligatures support (#1118)
Co-authored-by: Orca <help@stably.ai>
2026-04-25 22:26:33 -07:00
Jinwoo Hong 6729825bc0 feat(ssh): persist and auto-restore SSH sessions across app restarts (#1026) 2026-04-25 20:15:02 -07:00
Neil b9646acbe4 fix(pi): surface Pi working spinner on worktree card (#1083 follow-up) (#1105)
Pi's titlebar extension emits OSC 0 titles every 80ms during agent work
(`⠋ π - cwd`, `⠙ π - cwd`, …) and a trailing idle title on `agent_end`
(`π - cwd`). Node-pty on the main side batches output every 8ms, so the
renderer frequently receives multiple title updates in a single `pty:data`
chunk.

The pty-transport's data handler used `extractLastOscTitle`, which returns
only the final OSC title in a chunk. For fast agents (e.g. Pi with a small
local model) the whole working→idle cycle often lands in one IPC payload —
the intermediate working frames were silently dropped, and the worktree
card's `detectAgentStatusFromTitle` only ever saw the idle title.

Empirically on macOS: polling `runtimePaneTitlesByTabId` 20×/0.25s during
a multi-second Pi working window showed the stored title stuck at "Pi"
throughout, while a raw `ipcRenderer.on('pty:data')` listener captured
the working frames flowing through IPC correctly. After the fix, the same
polling captures "⠋ Pi" alongside "Pi".

- Add `extractAllOscTitles` in the shared agent-detection module and feed
  every OSC title in the chunk through `applyObservedTerminalTitle` in
  order, so the working→idle transition reaches the store and the agent
  tracker.
- Keep `extractLastOscTitle` exported for the main-process stats tracker
  and orca-runtime, which only care about the most recent title.
- Pin the regression with three focused tests: per-chunk spinner frames,
  dispatcher-routed end-to-end, and the exact coalesced-chunk scenario
  that used to swallow the working state.
2026-04-25 19:01:43 -07:00
Jinjing aa10811780 fix: address review findings (#1078)
- fix: address review findings
- wip
- wip
2026-04-24 21:58:21 -07:00
Neil a2928e62be refactor(terminal): remove FORCE_HYPERLINK feature and setting (#1075) 2026-04-24 21:06:21 -07:00
Jinwoo Hong cc47cad95f feat(tasks): add Linear team selector (#1074) 2026-04-24 21:03:30 -07:00