#14397 split `shared/types.ts` into 46 per-domain modules but kept the path as
a re-export barrel so the import sites did not have to change. This removes
the barrel: every consumer now imports from the module that actually declares
the type, and `src/shared/types.ts` is deleted.
Barrels hide where a type lives, make every consumer look like it depends on
the whole domain, and let an unrelated edit invalidate a module that ~2,000
files transitively import.
2,323 import declarations across 2,321 files. Rewritten mechanically: each
specifier was resolved to an absolute path via the TypeScript AST and
recomputed, rather than string-substituted, so alias forms (`@/../../shared/
types`) and per-specifier `type` modifiers survive.
Four cases the mechanical pass had to handle, each found by a gate rather than
by reading the diff:
- Modules inside `src/shared` import the barrel as `./types`, not
`shared/types`. A pre-filter on the latter string skipped 176 of them and
left imports dangling at a deleted file, which surfaced as confusing
`Property 'x' is optional in type 'Repo' but required in Pick<Repo, ...>`
errors rather than "module not found".
- The barrel RENAMED one type on the way through
(`WorkspaceSource as WorkspaceCreateTelemetrySource`), so the original name
in the owning module has to be re-aliased at each consumer.
- Three test files put `;(globalThis as ...)` on the line after the import.
TypeScript parses that `;` as the import statement's terminator, so
replacing through `statement.getEnd()` deletes it and breaks ASI. The
rewrite now stops at the module specifier.
- A file that already imported directly from a module got a SECOND import
from it, because the barrel re-exported those same names — which trips
`import/no-duplicates` under `--deny-warnings`. A post-pass merges
declarations sharing a specifier and type-only-ness; the `import type` plus
`import` pair from one module is left alone, since that form is allowed.
Splitting one barrel import into several genuinely adds lines, which pushed
`terminal-layout-pty-ownership.ts` to 301 counted lines: its 107-character
import must wrap, and neither local type collapses onto one line (101 and 116
characters). Rather than contort a type declaration to fit a line budget,
`collectLeafIds` and `pruneLeaves` move to `terminal-pane-layout-tree.ts` —
they are pure structural operations on the layout tree and independent of PTY
ownership. `visible-worktrees.ts` similarly loses its own mini-barrel
re-export of `isDefaultBranchWorkspace`, with the four real consumers
repointed at the declaring module. No `max-lines` bypass added.
Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches); the full
`pnpm lint` green, not just bare oxlint — the narrower local check is what let
the duplicate imports reach CI; max-lines ratchet OK at 344.
* fix(github): align PR source and review head origin
* fix(github): pin number-based work item open to the repo source preference
Open-by-number and details still ran the upstream-first multi-candidate PR
probe, so a fork and its upstream sharing a PR number opened different PRs
than the list and start-point paths did once #10677 pinned those to origin.
Thread repo.issueSourcePreference through dispatchWorkItem, getWorkItemDetails,
getRepoWorkItem, and getRepoWorkItemDetails. getWorkItemByOwnerRepo is left
alone: explicit owner/repo already pins identity. auto/upstream/undefined keep
the multi-candidate probe.
Co-authored-by: Orca <help@stably.ai>
* test(github): enforce origin preference in review head origin resolution
The explicit origin preference must short-circuit before any identity probe, so no remote queries should occur. Add validation to reject unexpected remotes and tighten the test assertion to verify no remote get-url calls happen at all.
* fix(github): enforce origin preference in issue open-by-number lookup
listWorkItems and getWorkItem must share preference so origin/upstream
toggles cannot disagree. Explicit origin preference now fail-closes when
origin identity is unresolved (no bare-lookup fallback), matching the
PR candidate resolution rule.
---------
Co-authored-by: Orca <help@stably.ai>
* fix(github): load PR diffs for Enterprise remotes
* fix(github): encode PR content paths by segment
* Fix PR review actions failing on GitHub Enterprise remotes
- Threads GitHub host identity (not just owner/repo) through the client,
work-item-details, issues, and RPC layers so gh commands target the
correct Enterprise server instead of silently falling back to github.com
- Adds a shared github-api-repository helper to resolve/host-qualify repo
identity consistently across REST, GraphQL, and CLI shorthand calls
- Scopes the gh rate-limit breaker and singleton rate-limit snapshot by
host/runtime so a github.com block or probe can't affect GHES or WSL
- Coalesces concurrent host-auth probes and paginates PR file fetching
beyond 100 results
- Propagates `host` through renderer PR caches, checks-panel keys, and
preload IPC types so Enterprise and github.com data never collide
* Route gh host qualification through runner options instead of argv sniff
Move GHES/GH_HOST resolution from parsing --hostname/--repo out of gh argv to an explicit options.host passed through ghExecFileAsync, since SSH-backed repos spawn gh with no cwd and argv sniffing couldn't reliably detect the target host. The runner now injects --hostname and qualifies --repo/-R at spawn time from options.host, and rate-limit scoping/guards use the same explicit host instead of inferring it. Also adds a shared githubRepoIdentityKey helper to keep cache/store keys consistent with the new host-aware repository identity.
* Fix gh CLI GHES host pinning and rate-limit scope leaks
- Pin `--host` on every gh call site so a process-level GH_HOST can't
silently redirect requests, and qualify `-R`/`-R=` repo shorthand
alongside the existing `--repo=` handling.
- Check the target scope for an active rate-limit block before each
WSL/native or host fallback retry, not just on the initial attempt,
so a blocked scope can't be hit again through a fallback path.
- Compute idempotency once per call instead of re-deriving it after
fallback reassigns args.
* Fix GitHub Enterprise host identity loss across PR/work-item paths
- Thread `host` through mobile PR RPC params, IPC work-item lookups, and
RPC schemas so GHES identity survives the renderer/mobile/main boundary
instead of silently falling back to a same-named github.com repo.
- Qualify `--repo`/`-R` args for github.com too (not just GHES), since
gh resolves bare shorthand against a process-level GH_HOST that can
redirect pinned github.com commands.
- Cache `getOriginGitHubApiRepository` to avoid a per-call uncached
`git remote get-url` round trip on connection-backed repos.
- Add a local-fork fallback in `getWorkItemDetails` so PRs living on a
base repo (not visible via the origin slug) still resolve via cwd.
- Centralize the github.com-vs-GHES host predicate in
`isDefaultGitHubHost` so cache keys, quota scoping, and identity
checks can't drift out of sync.
* Make repository identity host-aware across all GitHub surfaces
Generalize the auth-gated enterprise resolver to any remote and build a
cached hosted-identity family (origin/issue/candidates/source) on top of
it, then migrate every github.com-only consumer: Tasks listing/counting,
branch-to-PR discovery, push targets, fork upstream, issue operations,
Projects, web links, avatars, and PR-link facts. Scope the rate-limit
breaker probe per runtime:host and classify WSL UNC cwds correctly.
Co-authored-by: Orca <help@stably.ai>
* Fix expected slug to include host field in GitHub PR link test
Updates the smart-source paste-intent test fixture to match the
repository slug shape that now carries a `host` field, keeping GHES
host identity intact through the paste-intent parsing path.
* Surface per-host gh auth state for GitHub Enterprise
diagnoseGhAuth accepts the host a surface needs credentials for, scopes
the account/scope diagnosis to that host, and reports whether gh has any
login there; GhAuthErrorHelp renders host-qualified login/refresh
commands so an unauthenticated GHES host stops masquerading as a
github.com scope problem. Also fixes the mobile paste-intent expectation
for host-carrying parsed links.
Co-authored-by: Orca <help@stably.ai>
* Bound GHES identity caches and preserve non-default ports in host identity
Cap the origin-repo and host-auth caches like ownerRepoCache; keep ports
from remote/link URLs so GHES on a non-default port is a distinct
identity; make positional github.com slugs explicit against GH_HOST;
compare work-item sources by host-aware identity key; bail cwd-less
branch lookups when no repository candidate resolved; thread host
through the renderer work-item slug lookup.
Co-authored-by: Orca <help@stably.ai>
* Thread GitHub host through issue detail requests
Incorporates ghes-issue-host-support (ed6bb96ef): one hosted issue
repository identity is resolved before the details fan-out so comments,
timeline, participants, and mention lookups cannot drift across hosts,
with SSH guards so unresolved issue/PR repositories never fall through
to gh's default host.
Co-authored-by: Orca <help@stably.ai>
* Scope remaining GitHub rate-limit accounting
* Resolve typed PR lookups across hosted repository candidates
getWorkItem's PR path probes upstream-then-origin hosted candidates
instead of origin alone, so fork checkouts resolve the base repo's PR
with the right host; issue detail resolution reuses the up-front hosted
identity and keeps the SSH unresolved-host guards.
Co-authored-by: Orca <help@stably.ai>
* Refactor GitHub repository execution setup
* Carry host on smart-submit link intents
Co-authored-by: Orca <help@stably.ai>
* Carry the project host on GitHub item dialog origins
Co-authored-by: Orca <help@stably.ai>
* Keep GHES web ports but drop SSH transport ports in host identity
Supersedes PR #9118 on this branch: http(s) remote ports identify the
Enterprise web/API endpoint and are preserved, while ssh/git transport
ports (including ssh.github.com:443) never leak into gh's host identity.
Replaces the ssh.github.com:443 special case with the structural
protocol split and ports the PR's parsing test suite.
Co-authored-by: Orca <help@stably.ai>
* Support GitHub Enterprise diffs and mutations with host-scoped caches
Parse GitHub host identity from work-item URLs and carry it through PR/issue mutations, labels, and assignments. Bound rate-limit and scope-probe caches (1024 and 512 entries) to prevent unbounded growth when interacting with multiple GHES instances. Normalize repository identity keys to include host so github.com and GHES slugs don't collide in cache and equality checks.
* Support GitHub Enterprise diffs and mutations with host-scoped caches
- Carry host identity through PR mutations and reads so fork PRs on
different GHES instances don't collide in cache or state tracking.
- Validate host authentication before routing requests to unconfigured
Enterprise servers; ambient credentials must never reach untrusted hosts.
- Scope rate-limit guards and spend tracking per host so GHES quota stays
independent from github.com quota.
- Respect explicit --hostname arguments in gh CLI calls ahead of GH_HOST or
ambient defaults, so breaker state follows the actual request target.
- Detect implicit WSL runtimes from UNC paths for consistent host auth and
execution-options scoping across mobile and desktop clients.
* Support GitHub Enterprise work-item diffs with host-scoped execution
Enterprise PRs must use their selected host consistently across diff, comments,
and file-content loads. Validate repository slugs before authenticated execution
to prevent path-injection via renderer overrides. Scope project browsing cache
and rate-limit tracking by host to prevent cross-host pollution. Use parsed
URLs as authoritative over ambient hosts for project resolution.
* Support GitHub Enterprise work-item diffs with host-scoped execution
Preserve host identity on PR/issue work items throughout the mutation and diff
pipeline so Enterprise instances (including ported endpoints like
github.acme.test:8443) can execute mutations without ambiguity. Rate-limit gh
commands by the pre-qualified --repo host, cache auth state per ported host,
and surface Enterprise hosts in project metadata and error messages.
* fix(review): drop dead rateLimitGuard/noteRateLimitSpend re-export
Both callers (project-view.ts, mutations.ts) moved to the host-scoped
repositoryRateLimitGuard/noteRepositoryRateLimitSpend; the bucket-only
re-export in internals.ts had zero importers left.
Co-authored-by: Orca <help@stably.ai>
* fix(ci): split Enterprise host work-item tests under max-lines
Move GHES/SSH host-routing cases out of work-item-details.test.ts so
the suite stays within the 800-line test max-lines budget.
* test(github): align mocks with host-scoped repository resolution
- Route origin repository resolution through getOwnerRepoForRemote, not getOwnerRepo, to match production path
- Pin github.com host on origin results so host-less fixtures pass host gate in resolveGitHubApiRepository
- Add generation-based invalidation to prevent stale slug-cache writes from in-flight resolutions
- Fix ref-sync race in ProjectPicker: use useLayoutEffect so committed tree owns browse cache key
- Defer handledCrossRepoUrlRef assignment in SmartWorkspaceNameField until resolution succeeds
- Update Enterprise host routing: found work items must not silently fall back to default host when unresolved
- Normalize GHES avatar URLs: accept explicit port 443 as canonical form, not a fallback trigger
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.
Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.
Area: main — git, source-control, providers & integrations. 40 files changed, 1432 insertions(+), 4473 deletions(-).
Co-authored-by: Orca <help@stably.ai>
On a fork checkout (origin=fork, upstream=parent) PR details failed to load because getOwnerRepo() resolved only the origin remote while GitHub PRs live on the upstream parent. Make getOwnerRepo() prefer upstream (mirroring getIssueOwnerRepo), and pin the call sites that genuinely need the checkout's own origin identity (getRepoSlug, getRepoUpstream, createGitHubPullRequest, resolvePrWorkItemSource) to the origin-only primitive.
Lands the fix community-identified in #7332 and hardened in #7513.
Closes#7331
Co-authored-by: fsdwen <1214772+fsdwen@users.noreply.github.com>
Co-authored-by: brennanb2025 <79079362+brennanb2025@users.noreply.github.com>
- gh file-fetch failures (rate limit, auth, unresolved remote) previously
returned an empty array, which the Files tab rendered as "No files
changed." — indistinguishable from a real empty PR
- getPRFiles now returns null on failure; work-item-details surfaces this
as filesUnavailable so GitHubItemDialog and PullRequestPage can show a
retry action instead of a misleading empty state
* Add GitHub issue timeline activity feed to details dialog
Fetch and display issue timeline events (assignments, mentions, closes,
reopenings, and project board column moves) in a unified activity tab
within the GitHub item details dialog.
- Query the GitHub REST timeline endpoint up to a bounded 300 items.
- Merge comments and timeline events into a single sorted conversation.
- Render tailored icons, links, and localizable messages per event.
- Ensure issue reply targets are not incorrectly filtered by stale PR state.
- Correctly update local item state from details payload.
* Cap issue timeline pagination by supported activities
Count only mapped, supported timeline items instead of raw REST events
when checking the maximum item threshold. This prevents pagination from
stopping early when pages contain many unsupported event types.
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>
* 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>
* 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>
* 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