* Gate task providers by availability
Implement provider availability gating documented in docs/task-provider-availability.md.
* Remove task provider availability design doc
* Restore task source after provider availability checks
- Keep saved GitLab/Linear defaults from being lost while provider checks hydrate
- Ignore stale Linear status responses after connect or workspace changes
* fix: address review findings
* feat: add configurable Open In menu
Implements configurable Open In applications in worktree context menus with persisted settings, preload/API wiring, and renderer controls/tests.\n\nDesign doc: docs/configurable-open-in-menu.md
* fix: address review findings
* feat(sidebar): allow manual drag-and-drop reordering of repos
Users can now drag repo headers in the sidebar to reorder them. The
custom order is persisted to disk and survives restarts. Includes
design doc at docs/manual-repo-reorder.md.
Co-authored-by: Orca <help@stably.ai>
* fix: scope post-drag click swallow to dragged repo header
Avoid silently eating unrelated clicks if one races between pointerup and
the failsafe teardown.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
Follow-up to #1254. The lockfile tracks skill source/hash metadata which
now lives alongside the skill sources in the internal delivery repo;
keeping a parallel copy here in the public tree is misleading (entries
can drift) without being useful to anyone.
- Remove skills-lock.json from the tree.
- Gitignore it so local tooling can still write one without making
git status dirty.
Co-authored-by: Orca <help@stably.ai>
Private skill files are now stored and versioned in a separate internal
repo; a setup hook populates .claude/skills and .agents/skills with
machine-local symlinks instead. This keeps contributor-facing patterns
(agent skills in the tree) while allowing some skills to be developed
privately.
- Remove tracked skill files (62 files across 7 skill dirs).
- Gitignore the now machine-local skill directories.
- Add an env-gated hook to orca.yaml scripts.setup. Runs a setup script
whose path lives in $ORCA_INTERNAL_DEV_SETUP when present; silently
no-ops for public contributors.
No new contributor-facing requirements: the hook is optional, the env
var is only set by internal tooling, and the setup itself runs in the
worktree that Orca just created.
Co-authored-by: Orca <help@stably.ai>
* chore: update .gitignore to include stackdump and .serena, enhance pre-commit script
* fix(win32): resolve EPERM on userData writes and batch-file spawn failures
Three Windows-specific issues prevented Orca from running correctly on
machines where Chromium resets the userData DACL during startup:
1. **EPERM on userData writes** — Chromium's BrowserWindow constructor calls
SetNamedSecurityInfo on the userData folder with a Protected DACL. When
propagated to child directories the ACEs carry the Inherit-Only flag,
meaning they apply to children-of-children but NOT to the directories
themselves. Any file write inside codex-runtime-home, agent-hooks, or
similar subdirectories fails with EPERM.
Fix: grant an explicit Full Control ACE (OI)(CI)(F) on userData and all
existing children before BrowserWindow is created (icacls /T /C).
Explicit ACEs survive future DACL propagation from the parent. Per-write
EPERM retries in fs-utils and installer-utils serve as the backstop for
directories created after startup.
2. **Batch-file spawn failures** — resolveCodexCommand() can return a .cmd
or .bat path (e.g. codex.cmd installed via npm). Node's spawn() cannot
execute batch scripts directly without shell:true, but shell:true with an
args array triggers DEP0190 because args are concatenated rather than
escaped. Both service.ts and codex-fetcher.ts were affected.
Fix: detect .cmd/.bat paths and route through cmd.exe /c explicitly,
which is equivalent to what shell:true does internally but avoids the
deprecation warning and arg-escaping hazard.
3. **Native dep rebuild failure** — electron-builder install-app-deps does
not expose the ignoreModules option. On Windows dev machines without the
full VC++ / Python toolchain, cpu-features (an optional dep of ssh2) fails
to build with node-gyp, aborting the entire postinstall step.
Fix: replace electron-builder install-app-deps with a thin wrapper script
(scripts/rebuild-native-deps.mjs) that calls @electron/rebuild's JS API
directly with ignoreModules: ['cpu-features'] on Windows. ssh2 detects
the missing native module and falls back to pure-JS automatically.
Refactoring: extract shared win32-utils.ts with getIcaclsExePath(),
getCmdExePath(), isWindowsBatchScript(), isPermissionError(), grantDirAcl(),
and getSpawnArgsForWindows() to eliminate five instances of duplicated
SystemRoot path construction and two near-identical EPERM retry blocks.
Reduce startup icacls calls from three sequential blocking /T invocations
to one, removing up to 20 s of potential startup delay.
* fix(win32): address review feedback on ACL and spawn helpers
- Fall back to SID via `whoami /user` when `USERNAME` is unset so
`grantDirAcl` works under services, CI, and hardened envs instead of
silently no-op'ing.
- Use a 60s timeout for recursive `icacls /T` walks; the 10s cap could
starve on large userData trees and silently fail the startup grant.
- Pass `windowsHide: true` to `icacls` and the cmd.exe-routed Codex
spawns so no console window flashes in the packaged GUI app.
- Add `/d` to `cmd.exe /c` invocations to disable AutoRun registry
commands — safer default for background spawns.
- Drop unused `createRequire`/`require` from rebuild-native-deps.mjs.
- Add `@electron/rebuild` as an explicit devDependency; relying on the
electron-builder transitive was brittle under pnpm.
- Fix two misleading "Re-enable inheritance" comments that describe
behavior opposite to what the code actually does (explicit ACL grant).
- Add unit tests for `isWindowsBatchScript`, `getSpawnArgsForWindows`,
and `isPermissionError` to lock in Windows batch detection + cmd.exe
routing.
Co-authored-by: Orca <help@stably.ai>
* fix(win32): unify PTY spawn through /d and document cmd.exe safety
- fetchViaPty now uses getCmdExePath() and /d /c, matching the rest of
the codebase instead of hand-rolling 'cmd.exe' + ['/c', ...].
- getSpawnArgsForWindows gains a SAFETY note: when the .cmd/.bat branch
is taken, cmd.exe re-parses the combined command line, so callers
must only pass trusted/literal args.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* 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>
* feat(editor): preserve Cmd+B for bold in markdown editor
Carve out bare Cmd/Ctrl+B from the main-process before-input-event
interceptor when the TipTap markdown editor is focused, so its bold
keymap can run instead of toggling the left sidebar. Focus state is
mirrored from renderer to main via a one-way IPC send, with default-deny
resets on crash/navigate/destroy and sender validation so only the main
window's webContents can mutate the flag.
* fix: add oxlint max-lines disable to createMainWindow.ts
Move build resources to resources/build/, icon source files to
resources/icon-source/, scripts to config/scripts/, and patches
to config/patches/ for a cleaner top-level directory layout.
* feat: use Enter to submit and Shift+Enter for line break in comment editor
Closes#48
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add CLAUDE.md and gitignore package-lock.json
Enforce pnpm usage for AI agents and prevent stray npm lock files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Swap out eslint + 6 plugins for oxlint and prettier for oxfmt (oxc toolchain).
Set up husky + lint-staged to run linter and formatter on pre-commit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>