* Default repo avatars to the GitHub upstream owner and flag forks
Make the GitHub owner avatar the default repo icon and surface it as the
primary choice in the icon picker (the "Image" tab becomes "Avatar",
moves first, and opens by default). For forks, resolve the upstream/
parent owner so the avatar reflects the source repo instead of the
personal fork, and show a fork indicator (GitFork glyph + "Fork of
owner/repo" tooltip) next to the repo in the sidebar and settings.
Fork detection prefers the offline `upstream` remote, falling back to a
`gh repo view --json isFork,parent` lookup; the resolved upstream is
stored on the repo. Existing repos self-correct via a one-time startup
backfill (local repos) and a lazy backfill when their icon settings open
(SSH repos); new repos resolve at add-time. Reset now restores the
default (re-detecting the upstream) instead of clearing to the Folder
icon.
* Harden repo upstream avatar handling
---------
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
* Create GitHub issues with markdown and metadata
- Add a reusable rich markdown composer for GitHub issue bodies and comments
- Let new GitHub issues include selected labels and assignees through IPC/RPC
- Preserve created issue metadata in the task list and cover the new API path with tests
* fix: address review findings
- Use fallback PR numbers after branch lookup misses, including detached HEAD
- Preserve review cards for forked or deleted-head PRs across manual refreshes
- Clear stale GitHub PR cache entries when unlinking worktree review metadata
* Squashed commits
- WIP: uncommitted changes before rebase
- ci
- Show inline PR check details in task drawer
- Add a Checks tab that opens from the PR checks cell and expands runs inline
- Fetch check output, annotations, and workflow job steps through IPC/RPC
- Improve markdown/comment wrapping so long PR content stays within the drawer
* Use app-styled confirmations for PR actions (#2324)
Co-authored-by: Orca <help@stably.ai>
* fix: pr-bug-scan validated finding from #2274 (#2296)
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
---------
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: buf0-bot[bot] <252831055+buf0-bot[bot]@users.noreply.github.com>
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Reopening a GitHub issue/PR drawer paid full IPC + `gh` startup latency on
every open. Two changes here:
1. Module-level SWR cache in GitHubItemDialog.tsx keyed by
(repoPath, issueSourcePreference, type, number). Reopening within 30s
paints cached data instantly; older entries paint stale-then-refresh.
Concurrent opens dedupe on a shared in-flight promise. Mutation
handlers invalidate by (repo, type, number); a cache-generation
counter prevents in-flight refetches from resurrecting stale data
after a mid-flight invalidation.
2. Collapsed GraphQL query for issue details replaces 3 serial `gh`
subprocesses (REST issue + REST comments + GraphQL participants) with
one round-trip. Falls back to the legacy fan-out on any GraphQL error
so historical contract is preserved.
Cross-window invalidation rides a new `gh:workItemMutated` IPC broadcast
that skips the originating sender (the source already updated its cache
optimistically — re-broadcasting would race the optimistic write).
`addIssueComment` now takes a `type` so the broadcast scopes correctly
when a PR shares its number with an issue.
Co-authored-by: Orca <help@stably.ai>
* fix(gh-project): diagnose env-shadowed gh tokens in auth errors
`gh auth refresh -s project` silently no-ops when GITHUB_TOKEN/GH_TOKEN
is exported in the user's shell — gh prefers env tokens and refuses to
modify them, exiting 0. Users follow the canned remediation, see no
error, retry, and stay stuck.
Add a one-shot `gh auth status` probe (gh:diagnoseAuth IPC) that:
- Detects env-shadowed credentials and rewrites the fix to `unset
GITHUB_TOKEN` plus a grep to find where it's exported.
- Detects missing gh install, plain missing-scope on a keyring login,
and SAML SSO authorization.
- Surfaces a tailored multi-button error UI in ProjectViewWrapper and
ProjectPicker instead of one canned 'Copy command'.
Co-authored-by: Orca <help@stably.ai>
* fix(gh-project): address review feedback
- Cross-platform shell guidance: PowerShell commands on Windows
(Get-ChildItem Env:, Remove-Item Env:, [Environment]::SetEnvironmentVariable)
via navigator.userAgent platform check.
- Use `window.api.shell.openUrl` for the docs button instead of
`window.open`, matching SidebarToolbar's external-URL pattern.
- Tighten gh auth status parser: accept single-label hostnames and
optional trailing colon; recover host from the inline 'Logged in to
<host>' line so a missed section header never silently drops accounts.
- Add tests for multi-host output and host-recovery fallback.
- Drop dead command/copy locals in ProjectViewWrapper.ErrorState by
short-circuiting the auth-error case before they're computed.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
When a worktree is created from a PR via the source picker, the new
local branch differs from the PR's head ref, so the branch-keyed PR
lookup misses and the worktree card shows no PR strip. Pass the
worktree's linkedPR number through the IPC call and fall back to a
number-based lookup in the main process. Also recover linkedPR from
a PR URL pasted into the workspace name when the user skips the
source picker. PR strip now wraps the whole row as the PR link.
Co-authored-by: Orca <help@stably.ai>
* Fix new workspace composer focus restore
* Unify new workspace source selection
* WIP: selected source pill in smart workspace name field
Co-authored-by: Orca <help@stably.ai>
* fix(new-workspace): truncate source pill so it doesn't expand the dialog
Co-authored-by: Orca <help@stably.ai>
* feat(new-workspace): add open-in-browser button to source pill, fix vertical alignment
Co-authored-by: Orca <help@stably.ai>
* refactor(new-workspace): drop redundant kind suffix, distinct PR/issue icons, tooltips on pill actions
Co-authored-by: Orca <help@stably.ai>
* fix(new-workspace): type linked URL into agent input without auto-submit
Co-authored-by: Orca <help@stably.ai>
* fix(new-workspace): use bracketed-paste for draft URL injection so it actually appears in the agent input
Co-authored-by: Orca <help@stably.ai>
* feat(agents): per-agent draft injection strategy (codex slow paste, pi/opencode type-chars)
Co-authored-by: Orca <help@stably.ai>
* fix(agents): smarter TUI-ready heuristic + bracketed paste for codex/pi/opencode
Replaces per-agent strategy guesswork with a measured readiness check:
title-idle / non-shell-foreground stable for 1.5s / 2.5s minimum floor.
Verified against codex, pi, opencode, claude in a node-pty + xterm-headless
test rig — bracketed paste lands in the input buffer for all four.
Co-authored-by: Orca <help@stably.ai>
* refactor(agents): drop unused per-agent draft strategy abstraction
The TUI-ready heuristic in agent-paste-draft.ts works for every tested
agent (claude/codex/pi/opencode), so the AgentDraftInjectionStrategy
field, type-chars + bracketed-paste-slow code paths, and per-agent
overrides are dead. Keep the `agent` arg on pasteDraftWhenAgentReady
for future per-agent escape hatches without touching every call site.
Co-authored-by: Orca <help@stably.ai>
* feat(agents): skip draft URL injection for copilot + cursor-agent
Both TUIs open with a 'Do you trust this folder?' menu on first launch
that consumes keystrokes as menu input — pasting a URL there either
selects an arbitrary option or quits the session. Mark them with
skipDraftUrlInjection so the workspace still opens cleanly; the user
types/pastes the URL themselves once past the trust menu.
Co-authored-by: Orca <help@stably.ai>
* feat(agents): native --prefill for claude, trust pre-write for cursor/copilot
Replaces the empirical TUI-ready waits with two deterministic mechanisms:
1) `claude --prefill <text>` flag — Claude launches with the URL already in
its input box, no submit. Eliminates the readiness/paste race entirely
for the most common agent.
2) DECSET 2004 (`\x1b[?2004h`) detection on the PTY data stream for every
other agent. That escape is the protocol-level "input layer ready,
accepting bracketed paste" handshake — emitted by claude/codex/pi/
opencode/gemini/cursor-agent/copilot the moment the input box mounts.
We tap it via a sidecar subscription on pty-dispatcher (no interference
with the primary xterm handler) and paste as soon as it lands. The
8s budget is now an upper bound, not a target.
Cursor-agent and Copilot's "Do you trust this folder?" menus are bypassed
by writing the same trust artifacts the CLIs themselves write after the
user accepts:
- Cursor: `~/.cursor/projects/<slug>/.workspace-trusted` (slug = abs path
with leading `/` stripped, remaining `/` → `-`).
- Copilot: append cwd to `trustedFolders` in `~/.copilot/config.json`
(the same array the bundled `addTrustedFolder` writes).
Verified against the cursor-agent CLI bundle (versions/2026.04.17-787b533/
index.js: `_=".workspace-trusted"`) and the @github/copilot 1.0.32 bundle
(`isFolderTrusted` / `addTrustedFolder` both read/write `trustedFolders`).
Both check via realpath() before string-comparing, so the trust preset
canonicalizes too.
skipDraftUrlInjection is dropped — both agents now get the draft URL
paste once the trust menu is pre-resolved.
Tests: 24 passing across tui-agent-startup, agent-trust-presets,
pty-dispatcher routing.
Co-authored-by: Orca <help@stably.ai>
* fix(agents): wait for post-?2004h render burst to settle before paste
OpenCode emits DECSET 2004 at ~500ms during alt-screen setup, then runs
a 1.3s splash render with NO bytes on the PTY, then paints the actual
input box at ~1.85s. Pasting on the bare ?2004h signal lands during the
silent gap and the bytes are dropped.
The fix: take ?2004h as the necessary precondition, then wait for the
TUI's render burst to finish — defined as 1500ms of stream silence
after the most recent post-?2004h byte. This captures both the fast
TUIs (claude/pi/codex emit setup escapes in one burst then go quiet)
and the slow ones (opencode emits, sleeps for the splash, emits again,
then goes quiet).
Verified against opencode/claude/pi in a node-pty rig: paste lands on
the first try with the new strategy. The hard 8s timeout still caps
the wait when an agent fails to launch.
Co-authored-by: Orca <help@stably.ai>
* fix(agents): guard agentTrust IPC so stale preload doesn't crash launch
If the preload bundle is older than the renderer (a real situation in
electron-vite dev because preload changes only apply on full restart,
not HMR), `window.api.agentTrust` is undefined and the launch crashes
with "Cannot read properties of undefined (reading 'markTrusted')"
before the worktree even opens.
Guard the call sites in launch-work-item-direct and useComposerState
to skip the trust pre-write when the IPC isn't exposed, and wrap the
invoke in try/catch so an IPC error never blocks the launch — the user
just sees the trust menu and accepts it manually, same as before this
feature shipped.
Co-authored-by: Orca <help@stably.ai>
* feat(tasks): route 'Use' through the New Workspace dialog instead of yolo-create
The Use CTA on the Tasks page used to create+activate a worktree
synchronously, which surprised users — the worktree appeared in the
sidebar before they had a chance to confirm name / agent / setup. The
unified New Workspace dialog landed in this branch already supports
opening with a linked work item pre-filled (see openComposerForItem /
openComposerForLinearItem), so just route Use through it.
The launchWorkItemDirect helper stays exported for ProjectViewWrapper,
which has its own UX where the immediate-create flow is the right call.
Co-authored-by: Orca <help@stably.ai>
* test(agents): include `agent` field in autohand startup-plan assertion
Merging main brought in the Autohand Code agent test (PR #1382), which
predated this branch's addition of `agent` to AgentStartupPlan.
Aligning the assertion fixes the lone CI test failure on this PR.
Co-authored-by: Orca <help@stably.ai>
* refactor(agents): drop unused expectedProcess arg + snapshot sidecar set
Two minor follow-ups from self-review:
1. `pasteDraftWhenAgentReady` no longer reads `expectedProcess` — readiness
is gated on DECSET 2004 alone now, not on PTY foreground process. Drop
it from the signature and from the two callers (launch-work-item-direct,
new-workspace).
2. The pty-dispatcher's sidecar fan-out iterates the live Set, which is
safe against deleting the current element but not against a watcher
that synchronously subscribes a sibling. Snapshot via Array.from
before the loop. Cheap (Set is tiny) and removes the latent footgun.
No behavior change.
Co-authored-by: Orca <help@stably.ai>
* test(e2e): match the unified smart-name input's new placeholder
The CreateFromTab refactor in this branch replaced the separate "Workspace
name" Input with a single SmartWorkspaceNameField whose default-mode
placeholder is "Type a name, #1234, branch, GitHub or Linear URL". The
worktree-create e2e test was still anchoring on the old "Workspace name"
text and could not find the input.
Update the placeholder regex to match the new copy. Free-form text typed
into smart mode is treated as a workspace name by submitQuick — same
contract the test used before.
Verified locally: targeted e2e passes in 2.2s.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* 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>
Adds a + button on the tasks page that opens a dialog to create a new
GitHub issue in the selected repository. Wires createIssue through the
main/preload IPC using gh api, and refreshes the tasks list after
creation so the new issue shows up immediately.
Also clears a stray Radix pointer-events lock on GitHubItemDrawer mount
so Close/open-in-GitHub buttons remain clickable after the New Issue
dialog closes.
* New workspace page with agent catalog, composer modal, and terminal integration
* fix lint
* better blank state
* fix: resolve typecheck errors in new-workspace flow
- widen activateAndRevealWorktree's issueCommand to accept direct
command shape used by NewWorkspacePage, not just the main-process
runner-script variant
- use a ref object in pty-connection tests to dodge TS narrowing the
captured callback to never across the mock closure boundary
* fix: bound worktree name auto-suffix loop to prevent OOM in tests
The auto-suffix loop in createLocalWorktree had no termination cap.
When tests (or a misconfigured env) mocked getBranchConflictKind /
getPRForBranch to always return a collision, the loop ran forever and
the vitest worker crashed with "Ineffective mark-compacts near heap
limit".
Cap the search at 100 suffixes, and if all fail, fall back to the
original specific error messages (branch already exists / PR already
owns the name) instead of a generic failure.
Update the two tests that asserted the old throw-on-first-conflict
behavior to verify the new auto-suffix path end-to-end.
* Add richer feedback dialog
* Keep anonymous submission client-side only
* Use lowercase feedback API host
* Fallback feedback submission when api host is unavailable
The `gh api --cache 60s` flag caused stale check-run data even when
the user explicitly clicked refresh. Thread a `noCache` flag through
IPC so manual refreshes skip the gh CLI cache while polling still
benefits from it.
- Use gh api --cache for issues, issue lists, and PR checks endpoints so 304 Not Modified responses don't count against the rate limit
- Pass branch to getPRChecks to enable the check-runs REST endpoint with caching
- Extract mapping functions into separate mappers.ts to keep client.ts within 300-line limit
- Extract mapIssueInfo() helper to deduplicate issue mapping logic
- Remove dead re-exports from client.ts
- Fix reference instability in ChecksPanel poll dependency array
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: extract path-security helpers and refactor IPC modules
- Extracted path-security helpers from filesystem.ts into filesystem-auth.ts to fix max-lines lint error (402 -> 293 lines)
- Extracted duplicated ENOENT detection into isENOENT() helper to eliminate code duplication
- Fixed missing curly braces on single-line if-return in isDescendantOrEqual (lint violation)
- Replaced any with unknown in test files to satisfy lint rules
- All tests passing (29/29)
- Lint clean (0 errors, 0 warnings)
* fix: bundle preload deps for sandbox mode and fix editor test types
The sandbox: true change in createMainWindow broke the app because
electron-vite was externalizing @electron-toolkit/preload, producing
a require() call that fails in sandboxed preload scripts. Exclude it
from externalization so it gets bundled inline.
Also fix type errors in editor.test.ts from the partial store setup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Add PR checks panel to right sidebar
- Stream checks from GitHub API
- Update PR title real-time with loading state
- Add error logging and dependency fixes
- Fix linting issues (curly braces, exhaustive deps)