Commit Graph
96 Commits
Author SHA1 Message Date
386fc42564 Add Source Control fast-forward action (#3364)
* Add source control fast-forward action

* Fix fast-forward overwrite error wording

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

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-05-30 02:06:12 -04:00
4180352856 fix: auto-populate Create Worktree branch tab (#2850)
* fix(new-workspace): show branch results before typing

Query and render Branch tab results for empty input so users can pick existing refs immediately.

* refactor(new-workspace): simplify branch source helpers

Remove thin abstractions from the branch result helper while preserving Branch tab behavior.

* fix(new-workspace): load branch options for empty search

* refactor(new-workspace): keep smart branch search lazy

Limit empty-query branch loading to the Branch tab so Smart mode keeps its start-typing state.

* fix(runtime): populate remote branch searches when empty

Keep runtime-backed repos aligned with the local and IPC branch search paths.

* fix: harden branch search prepopulation

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

* test: align branch search assertions after merge

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

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-05-29 20:16:11 -04:00
Trevin Chow 10f97509ea fix: preserve unmerged branch when removing a worktree (#2927) (#2939)
fix: preserve unmerged branch when removing a worktree (#2927)
2026-05-28 20:19:53 -04:00
Jinjing 47dffe5308 fix: address review findings (#3014) 2026-05-28 16:13:45 -07:00
Jinwoo HongandOrca 8f172f538c Fix clone abort cleanup ownership (#2981)
Co-authored-by: Orca <help@stably.ai>
2026-05-28 01:17:05 -07:00
Jinwoo HongandOrca d917240499 Guard untracked discard against symlink escapes (#2947)
Co-authored-by: Orca <help@stably.ai>
2026-05-27 19:48:27 -07:00
659fe79226 feat: add source control action to abort merge (#2092)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-05-27 14:59:23 -07:00
Trevin Chowandbrennanb2025 a65f7b4216 feat: auto-rename workspace branch from the first prompt (#2888)
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-05-27 14:09:43 -07:00
Trevin ChowandNeil b2b5cac9a9 Add per-file line counts to the source control sidebar (#2865)
* Add per-file line counts to the source control sidebar

Show +N/-N (green/red) next to each file in the Changes, Untracked, and
Committed-on-branch sections so the magnitude of a change is visible at a
glance. Counts are computed per staging area via `git diff --numstat`;
untracked/new files count their full contents as additions and binary
files show no count.

Consolidate numstat parsing and binary-buffer detection into shared
modules reused by the local status path, the SSH/relay path, and the
existing branch-compare code. Include added/removed in the status-entry
equality check so the sidebar doesn't re-render on unchanged polls.

* fix: harden source control line counts

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-05-26 20:09:54 -07:00
Trevin Chow 286729fc7d fix(worktree): match slash-named branches in base-ref search (#2861)
Match single-token base-ref search against both leaf and ancestor segments for local and remote slash-named branches. Keeps the SSH relay argv in parity and adds regression coverage.
2026-05-26 19:33:37 -07:00
Brennan BensonandOrca 91991d6186 Improve refresh local base ref UX (#2570)
Co-authored-by: Orca <help@stably.ai>
2026-05-26 18:28:41 -07:00
Brennan BensonandOrca b7832c0790 Persist worktree creation base in git config (#2859)
Co-authored-by: Orca <help@stably.ai>
2026-05-26 15:54:07 -07:00
Brennan Benson 5287a581a6 Revert "Allow OS metadata during worktree delete preflight" (#2776) 2026-05-24 23:23:31 -07:00
Brennan BensonandOrca fdb63366d3 Allow OS metadata during worktree delete preflight (#2760)
Co-authored-by: Orca <help@stably.ai>
2026-05-24 23:00:40 -07:00
Brennan BensonandOrca 9cb9a9d5e3 Respect GitHub username branch prefix (#2599)
Co-authored-by: Orca <help@stably.ai>
2026-05-22 02:20:32 -07:00
Jinjing 41a2d7ba90 Target source-control actions to publish branches (#2612) 2026-05-22 00:35:30 -07:00
Jinjing d4e9c22f8b Fix worktree creation for selected existing branches (#2543)
* fix: address review findings

* fix: address CI failures
2026-05-21 12:25:32 -07:00
Brennan BensonandOrca a140313d4a Add local diagnostics error tracking (#2351)
Co-authored-by: Orca <help@stably.ai>
2026-05-21 11:15:01 -07:00
Anwesh 6e949d6ec8 Fix worktree base ref ambiguity
Resolve ambiguous git worktree base refs by qualifying local and remote branch refs before invoking git worktree add.
2026-05-21 00:46:31 -07:00
Jinjing bcb86d0778 Fix pull status for legacy origin/main worktrees (#2487)
- Resolve an effective upstream so legacy branches tracking origin/main use
  origin/<branch> when that remote branch exists.
- Pull, sync, and ahead/behind status now operate on the same branch the UI
  reports, including after non-fast-forward push rejections.
2026-05-20 22:10:51 -07:00
Jinjing e0104372da Squashed commits (#2485)
- Fix workspace space cache and git status cleanup checks

- Treat browser tabs as workspace delete blockers

- Move deletion readiness into shared presentation logic so tests cover it
- Preserve explicit empty git status entries for clean worktrees

- Add package manager cache cleanup to space manager

- Detect npm, pnpm, Yarn, and Bun caches from lockfiles during scans
- Add explicit cleanup IPC for safe and aggressive cache actions
- Support local, SSH, and Windows command execution paths safely
- Tighten workspace deletion decisions with git, editor, agent, and terminal state
2026-05-20 22:09:35 -07:00
JinjingandOrca aeaf0dece4 Create PRs from Source Control (#2478)
* Create PRs directly from Source Control

- Replace the modal flow with an inline PR composer in the sidebar
- Keep PR creation state and validation scoped per worktree
- Rename the recovery action to clarify it only pushes before creating PRs

* Clean up fork PR remotes after worktree deletion

- Track Orca-created push target remotes in worktree metadata
- Reuse ownership markers when later worktrees share the same fork remote
- Fetch only the selected PR base instead of every remote before drafting PRs
- Mirror local branch cleanup for SSH worktree deletion

* Stabilize pull request creation flow

- Keep PR actions and composer fields locked while generation or creation is in flight
- Refresh git status, branch comparison, and history after remote actions settle
- Disable push-only actions on diverged branches so users sync first

* Make PR context generation read-only

- Stop rebasing or probing HEAD before collecting PR draft context
- Allow git operations on known repo roots without refreshing worktree cache

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

* fix: address review findings

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-20 20:11:38 -07:00
Neil 942bcf7065 Remove small markdown helper dependencies (#2467) 2026-05-20 19:01:22 -07:00
Brennan BensonandOrca 1fd36e42cd Avoid blocking repo/workspace setup on GitHub CLI login (#2456)
Co-authored-by: Orca <help@stably.ai>
2026-05-20 16:48:14 -07:00
Jinjing dae696ab8f Handle unborn worktrees with resolvable base (#2398)
- Treat new remote worktrees without a HEAD commit as an empty compare when
  the base ref exists, avoiding a broken source-control compare state
- Keep the existing unborn-head error for cases where the base cannot resolve
2026-05-19 21:59:25 -07:00
5b1d84232c Avoid optional Git locks during status checks (#2330)
* 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>

* fix: avoid optional git locks during status checks

---------

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>
2026-05-19 11:47:32 -07:00
Leynier Gutiérrez González ba453a463d fix: normalize windows paths in git blob readers (#2290) 2026-05-18 23:46:24 -07:00
AnweshandNeil 95577ea9a0 Fix start-from PR failures for fork-backed PRs (#2233)
* Fix start-from PR failures for some fork PRs

* fix: limit PR fallback fetches to missing refs

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-05-18 00:36:34 -07:00
Jinwoo Hong e59afeacc4 Fix WSL tool detection on Windows (#2218) 2026-05-18 01:24:45 -04:00
Jinwoo HongandOrca afe90a616f Add mobile source control actions (#2193)
Co-authored-by: Orca <help@stably.ai>
2026-05-17 19:35:55 -07:00
Leynier Gutiérrez González 231bad8e19 feat(file-explorer): add gitignored visibility toggle (#2022) 2026-05-17 18:33:47 -04:00
Leynier Gutiérrez González a99929e42e fix: preserve slashes in branch-created worktrees (#2091) 2026-05-17 18:19:43 -04:00
Jinjing 087f1aa9bf fix(worktree): preflight delete before terminal teardown (#2103)
Implement delete preflight checks before PTY teardown across git/runtime/ipc flows.

Design reference: docs/worktree-delete-preflight.md
2026-05-16 13:52:59 -07:00
Jinwoo HongandOrca ab53fb5c9e feat: add source control git graph (#1969)
Co-authored-by: Orca <help@stably.ai>
2026-05-16 04:44:44 -04:00
2ad01ead7d feat(file-explorer): show gitignored files with dimmed italic decoration (#1941)
* feat(file-explorer): show gitignored files with dimmed italic decoration

Surfaces `.gitignore`d files in the right-sidebar file explorer with an
italicised, dimmed filename and a CircleSlash icon in the same trailing
slot used by the git status letter. A tracked change always wins — the
ignored decoration only applies when no other git status is present.

Gated behind a new `showGitIgnoredFiles` global setting (default on) so
heavy SSH workspaces can keep the smaller payload by skipping
`--ignored=matching` on `git status`.

`ignoredPaths` lives as a peer field on GitStatusResult rather than an
extension of GitFileStatus/GitStagingArea, so Source Control's
staging-area grouping is untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(file-explorer): trim redundant comments from gitignored decoration

Removes duplicated "Why:" explanations that ended up restating the same
backward-compat rationale across five files (relay, ssh provider, runtime
git commands, RPC handler, renderer git client) plus a few comments that
narrated the mechanism the code already shows.

Net: -39 lines of comment across 10 files; no behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(file-explorer): drop remaining comments from gitignored decoration

The code reads well without them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* review: harden gitignored file decorations

- clear ignored decoration cache when ignored status is disabled or omitted
- keep ignored decoration state scoped across worktree and runtime cleanup
- add coverage for local, SSH, runtime, relay, and Explorer precedence

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-05-15 18:58:08 -07:00
b030257c3b feat(source-control): AI commit message generation (#1487)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-05-15 02:18:46 -07:00
Jinwoo HongandOrca a22717bb35 Refactor runtime app architecture (#1878)
Co-authored-by: Orca <help@stably.ai>
2026-05-14 23:13:37 -07:00
Jinwoo Hong 31ab1560a7 Fix WSL gh fallback for task lists (#1879) 2026-05-14 22:42:11 -04:00
Brennan BensonandOrca be72e64a47 Improve source control discard flow (#1870)
Co-authored-by: Orca <help@stably.ai>
2026-05-14 15:20:53 -07:00
bca39bc928 Add GitLab and Bitbucket hosted review support (#1839)
* feat(gitlab): add foundational glab runner, types, and issue operations

First slice of GitLab support, mirroring src/main/github/ structurally
without refactoring the working GitHub path.

- runner: add glabExecFileAsync parallel to ghExecFileAsync (same WSL
  routing and retry policy; HTTP-status / network classification is
  provider-agnostic so the existing helpers are reused).
- types: GitLabProjectRef carries host alongside path so self-hosted
  instances and nested groups round-trip through the IPC layer. Mirror
  shapes for MR/issue/work-item/comment/file/assignable-user.
- gitlab/gl-utils: concurrency limiter, error classification, project-ref
  resolution honoring upstream/origin preference, and known-host
  discovery via `glab auth status` so non-gitlab.com remotes are
  recognized after the user authenticates.
- gitlab/mappers: pipeline-job → check-status mapping, MR state
  resolution (including draft inferred from `Draft:`/`WIP:` title
  prefix), and pipeline rollup.
- gitlab/issues: full issue CRUD via `glab api` against URL-encoded
  project paths, with the same upstream/origin preference semantics as
  the GitHub side.

63 unit tests passing across gl-utils / mappers / issues. Both
typecheck:node and typecheck:web clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): preflight glab auth check and URL parser

- preflight: probe `glab --version` + `glab auth status` alongside the
  existing gh checks. PreflightStatus.glab is optional so renderer call
  sites that only render git/gh keep typechecking; consumers gating on
  GitLab affordances opt in via `glab?.authenticated`.
- gitlab-links: parse GitLab issue and merge-request URLs honoring (a)
  arbitrary self-hosted hosts via the project-internal `/-/` separator
  rather than locking to gitlab.com, (b) nested group paths, and (c)
  GitLab's `!42` MR convention alongside `#42`.

26 unit tests added (5 new preflight cases, 21 URL-parser cases). Full
typecheck (node + cli + web) clean. Pre-existing runtime/orchestration
test failures unrelated to this branch — Node 25 vs the project's
pinned Node 24 engine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): MR list/get + paginated `glab api -i` helper

Lean mirror of github/client.ts focused on the workspace-from-MR
keystone. Adopts GitLab-native filter semantics (Open / Merged /
Closed / All) instead of porting GitHub's search-DSL — that path is
covered by the upcoming My Todos surface.

- gl-utils: glabApiWithHeaders + parseGlabApiResponse for strict
  pagination via X-Total / X-Total-Pages on `glab api -i` output.
  CRLF / LF tolerant; status line never leaks into the headers map.
- types: MRListState, GitLabPagedResult<T>, ListMergeRequestsResult.
- mappers: mapMRToWorkItem + mapIssueToWorkItem produce the unified
  GitLabWorkItem shape the picker consumes. isCrossRepository derived
  from source_project_id !== target_project_id; deterministic id
  fallback when the per-MR detail endpoint omits global id.
- client: getAuthenticatedViewer, getMergeRequest (with head pipeline
  rolled up), getMergeRequestForBranch (mirrors github/getPRForBranch
  semantics including refs/heads/ stripping and detached-HEAD guard),
  listMergeRequests (paginated), getWorkItemByProjectRef (paste-URL
  flow). Re-exports issues + projectRef helpers so callers don't have
  to know the gl-utils module split.

35 new tests (98 total in src/main/gitlab/), full typecheck clean.
Tests split into client.test.ts + client-mr.test.ts to stay under the
oxlint max-lines budget — matches github/client*.test.ts pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): worktrees:resolveMrBase IPC + linkedGitLab* persistence

The workspace-from-MR keystone. Mirror of worktrees:resolvePrBase
shape and semantics — caller passes mrIid (with optional source_branch
/ isCrossRepository hints), handler returns either a remote/branch
ref (same-project MRs) or a SHA fetched from
refs/merge-requests/<iid>/head (fork MRs).

- types: linkedGitLabMR / linkedGitLabIssue on Worktree + WorktreeMeta.
  Marked optional so existing test fixtures and persisted older
  worktrees that pre-date these fields keep typechecking and loading
  without a migration.
- persistence: getDefaultWorktreeMeta initializes both fields to null.
- worktree-logic: mergeWorktree carries them through from meta.
- worktrees IPC: resolveMrBase mirrors resolvePrBase. Resolves the
  GitLab project via getProjectRef + known-host discovery, fetches the
  MR work-item to derive source_branch + isCrossRepository when those
  hints aren't provided, and uses GitLab's refs/merge-requests/<iid>/head
  for fork MRs (parallel of GitHub's refs/pull/<N>/head).
- tests: 6 fixture updates for the new optional fields. Full
  typecheck (node + cli + web) clean; 165 tests passing across
  src/main/gitlab/, preflight, worktree-logic, and gitlab-links.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): IPC channels + preload bindings (gl.*)

Wire the GitLab backend to the renderer. Lean v1 surface — issues
CRUD, MR list/get/getForBranch, viewer, project slug, paste-URL
work-item lookup. Skips workItemDetails / listWorkItems-combined /
listTodos until the matching backend pieces land.

- main/ipc/gitlab.ts: thirteen handlers under the `gitlab:*` channel
  prefix with the same assertRegisteredRepo guard the gh handlers use.
  listIssues unwraps the structured result envelope to bare items[]
  to match window.api.gh.listIssues' shape; consumers that need the
  classified error can graduate to the envelope later.
- main/ipc/register-core-handlers.ts: register alongside gh.
- preload/api-types.ts: typed `gl: { ... }` block parallel to the
  existing `gh: { ... }`. Imports the new GitLab types so renderer
  code consuming the preload gets full inference.
- preload/index.ts: runtime `gl: { ... }` exposes wired to ipcRenderer.

Full typecheck (node + cli + web) clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): workspace-from-MR via paste-URL (keystone end-to-end)

The first user-visible GitLab moment. Pasting a GitLab issue or MR URL
into the workspace name field now resolves through the full pipeline
to a created workspace with the right base ref and linkedGitLab*
persisted. The dedicated GitLab tab + state-filter chips remain a
follow-up; everything below it is wired.

- shared/lib/new-workspace.ts: LinkedWorkItemSummary.type accepts
  `'mr'` alongside `'issue' | 'pr'`. Renderer code that switches on
  type explicitly handles each kind.
- ui store slice: NewWorkspaceDraft mirrors the new linked slots so
  drafts persist GitLab selections across navigation. Optional fields
  for backward compatibility with drafts saved before this branch.
- useComposerState:
  - linkedGitLabIssue / linkedGitLabMR state, draft persistence,
    repo-switch reset, applyWorktreeMeta wiring.
  - applyLinkedGitLabWorkItem mirrors applyLinkedWorkItem; reuses
    getLinkedWorkItemSuggestedName by structurally projecting the
    GitLab item onto the helper's input shape.
  - handleSmartGitLabItemSelect parallels handleSmartGitHubItemSelect:
    for picked MRs, calls window.api.worktrees.resolveMrBase to
    resolve the base ref (refs/merge-requests/<iid>/head for fork
    MRs) and threads it through handleBaseBranchMrSelect.
  - "was MR !N" reset hint when a repo switch wipes a GitLab
    selection — `!N` matches gitlab.com's MR-reference convention.
- preload: window.api.worktrees.resolveMrBase + window.api.gl.* are
  already in. ComposerCardProps grows onSmartGitLabItemSelect (+
  optional onBaseBranchMrSelect).
- SmartWorkspaceNameField:
  - Paste-URL detection: parseGitLabIssueOrMRLink (host-agnostic via
    `/-/` separator) → window.api.gl.workItemByPath → row in the
    dropdown → click → forwarded to onGitLabItemSelect.
  - SmartWorkspaceNameSelection union, RowEntry union, RowIcon,
    RowLabel, SelectionIcon all carry the gitlab-mr / gitlab-issue
    kinds. MR rows show `!N` prefix; issue rows show `#N`.
  - Tab UI not added in this commit — paste-URL works in 'smart'
    mode, the dedicated tab + Open/Merged/Closed/All chips lands
    in a follow-up.
- NewWorkspaceComposerCard: forwards onSmartGitLabItemSelect to the
  picker.

Full typecheck (node + cli + web) clean. 165 unit tests passing in
affected files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): GitLab tab in SmartWorkspaceNameField with state filter

The discoverable demo path. The picker now has a "GitLab" tab — when
selected it lists the project's MRs filtered by state via
`gitlab:listMRs`, with an Open / Merged / Closed / All chip strip
that mirrors gitlab.com's MR-page tab strip. Paste-URL detection in
'smart' mode is unchanged; the new tab simply makes the surface
discoverable without requiring a URL.

- SmartNameMode gains 'gitlab'; Gitlab icon (lucide) added to the
  MODES array between GitHub and Branch.
- MrStateFilter / MR_STATE_FILTERS centralizes the four chip values
  so the labels stay GitLab-native (Open vs the GraphQL 'opened').
- listMRs effect: fires when mode === 'gitlab' and no GitLab URL is
  in the input, with the current state filter and a page-1 fetch
  bounded by RESULT_LIMIT.
- Paste-URL effect now coexists with the list effect: it owns
  gitlabItems while a URL is in the input, the list effect owns it
  otherwise. Switching tabs no longer clears the list.
- Chip strip rendered above the popover's CommandList only when
  mode === 'gitlab'. Buttons use the same Button component the rest
  of the picker uses for visual consistency.

Full typecheck (node + cli + web) clean. 165 unit tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): GitLab source on Tasks screen

The Tasks screen now offers GitLab as a third source alongside GitHub
and Linear. Selecting it surfaces MRs and issues for the primary
selected repo with a state filter (Open / Merged / Closed / All) that
mirrors gitlab.com's MR-page tab strip. Skips cross-repo aggregation,
search DSL, and Projects mode for v1 — those layers are GitHub-API-
shaped and would need a parallel store slice that is not worth porting
ahead of the actual demand for them.

- shared/types: GlobalSettings.defaultTaskSource accepts 'gitlab'.
- TaskPage:
  - TaskSource union grows a 'gitlab' member; SOURCE_OPTIONS adds the
    Gitlab icon between GitHub and Linear so the toolbar order matches
    SmartWorkspaceNameField for cross-surface consistency.
  - GITLAB_TASK_FILTERS centralizes the four chip values.
  - Per-source state slim (matches Linear's pattern) — gitlabFilter,
    gitlabItems, gitlabLoading, gitlabError, gitlabRefreshNonce.
  - Data-fetch effect runs Promise.all over `window.api.gl.listMRs`
    and `window.api.gl.listIssues` for the primary repo, merges and
    sorts by updatedAt desc. 'merged' filter skips the issue fetch
    (GitLab issues are 'opened' / 'closed' only).
  - Filter bar block parallel to Linear's, with chips + a refresh
    icon-button.
  - List block: 5-column grid (ID / Title / Type+State / Updated /
    Open-link). Row click opens the web URL — the GitLabItemDialog
    is a follow-up commit, but the row affordance is enough for the
    Tasks-screen demo.
  - GitLab MRs render as `!N`; issues render as `#N` to match
    gitlab.com's reference convention.

Full typecheck (node + cli + web) clean. 165 unit tests still
passing in affected files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): GitLabItemDialog (minimal) + Tasks screen wiring

Clicking a GitLab row on the Tasks screen now opens a side-sheet
preview with the item's title, state, author, and description body
rendered as markdown. "Open in browser" footer button stays as the
escape hatch; opening from the row is dialog-first now (matching the
GitHub side's row-click-to-dialog pattern). Files / comments /
pipeline tabs are deferred — they mirror substantial GitHub-side
surface area (work-item-details ~550 lines, GitHubItemDialog 2680
lines) and are not blocking the demo.

- types: MRInfo and GitLabIssueInfo gain optional description /
  author / authorAvatarUrl. Optional because list endpoints strip
  them; populated on detail-endpoint reads (`getMR` / `getIssue`).
- mappers: mapMRInfo and mapGitLabIssueInfo now pass description /
  author / avatar through when present. Skipped (rather than
  defaulted to '') so callers can distinguish "no body authored"
  from "this came from a list".
- GitLabItemDialog: new ~200-line side sheet. Fetches the detail
  payload via `window.api.gl.mr` / `gl.issue` on open; renders
  CommentMarkdown for the description (reused from the GitHub
  side); falls back to "No description." when the body is blank.
  State badge tones picked locally — GitLab's MR state space is
  wider than GitHub's so coupling them buys nothing.
- TaskPage: GitLab row now uses a div role=button with keyboard
  handling so the inner Open-in-browser <button> nests cleanly
  (HTML disallows nested <button>s, React would warn). Row click
  sets gitlabDialogItem; the small ExternalLink icon stops
  propagation so it still opens the URL.

Full typecheck (node + cli + web) clean. 165 unit tests passing in
affected files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): My Todos cross-project view on Tasks screen

The GitLab tab now has a Project | My Todos sub-toggle. "My Todos"
fetches gitlab.com/dashboard/todos via `glab api todos?state=pending`
and surfaces them in a separate table — action / title / project /
updated. This is the closest GitLab-native equivalent of GitHub's
notifications/inbox and lands in lieu of porting GitHub's search-DSL
which doesn't translate.

- shared/types: GitLabTodo type with action_name, target_type/iid,
  target_url, project_path, author, updated_at. action_name kept as
  open-ended string because new GitLab versions extend the verb set.
- gitlab/client.ts: listTodos uses `glab api --paginate todos?state=
  pending&per_page=50`. User-scoped — cwd doesn't matter, but the
  IPC path-validation guard still requires *some* registered repo
  path so we keep the signature consistent with the rest of gl.*.
- IPC: `gitlab:todos` channel; preload `gl.todos`.
- TaskPage:
  - gitlabView ('project' | 'todos') gates which list to render.
  - Sub-toggle row above the chip strip; chips are hidden on the
    Todos view since pending state has no Open/Merged/Closed axis.
  - Refresh button serves both views (uses gitlabRefreshNonce).
  - Todos table: 5-col grid, action verb (snake_case → spaces),
    target title, project path (mono font for repo-likeness),
    updated date, open-link icon. Row click opens target_url.

Full typecheck (node + cli + web) clean. 165 unit tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): gitlabProjects settings (recents auto-tracked) + tests

Settings persistence for GitLab project preferences plus tests for
the surface added since the last green run.

- shared/types: GitLabProjectSettings { pinned, recent } and an
  optional GlobalSettings.gitlabProjects slot. Optional for
  backward compat with profiles saved before this branch — the
  persistence merge fills the empty default.
- shared/gitlab-projects: pure helper computeNextGitLabRecents that
  prepends-and-dedupes by host+path, caps at GITLAB_RECENTS_MAX
  (10). Pulled out of the IPC handler so it tests without mocking
  Store.
- gitlab IPC: workItemByPath handler now pushes the resolved
  project ref onto recents on success. 404 / auth-fail lookups
  do not pollute the list — recents reflects projects the user
  actually read.

Tests added (12 new, 177 total passing in affected files):
- gitlab-projects.test: prepend, dedupe, host-vs-host distinct,
  cap at max, no input mutation.
- client.test: listTodos mapping, defensive state coercion,
  empty-on-error fallback, missing-target field defaults.
- mappers.test: description / author / authorAvatarUrl pass-
  through on both mapMRInfo and mapGitLabIssueInfo, plus the
  "absent vs blank" distinguishing assertion.
- mappers-workitem.test (split): mapMRToWorkItem + mapIssueToWorkItem
  cases moved out of mappers.test.ts to keep both files under the
  oxlint max-lines budget.

Full typecheck (node + cli + web) clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): combined listWorkItems IPC + TaskPage refactor

Centralize the MR + issue merge logic that TaskPage was doing inline
into a single backend function and IPC channel. Future callers (the
picker's GitLab tab, any new widget) get the merge / sort / state-
mapping rule for free. The TaskPage effect drops from 60 lines of
inline orchestration to a single call.

- gitlab/issues: listIssues now accepts an IssueListState so the
  combined caller can ask for closed / all instead of always opened.
  CLI fallback path picks the right --opened / --closed / --all flag
  per glab version. Existing callers keep the 'opened' default.
- gitlab/client: listWorkItems(state, page, perPage, preference) fans
  out listMergeRequests + a raw issues fetch in parallel, merges by
  updatedAt desc, returns a GitLabPagedResult<GitLabWorkItem>.
  Bypasses listIssues for the issues side because IssueInfo strips
  updated_at — the combined sort needs it.
  state='merged' skips the issues fetch entirely (issues don't have
  a merged lifecycle).
- IPC: new gitlab:listWorkItems handler.
- preload: gl.listWorkItems alongside gl.listMRs.
- TaskPage: GitLab fetch effect now calls gl.listWorkItems and stops
  re-implementing the merge. Same UX, fewer moving parts.

Tests added (8 new in client-work-items.test.ts; +1 fix to
issues.test.ts for the new url-param order):
- merge ordering by updatedAt desc
- 'merged' state skips issues fetch
- closed / all state pass-through
- not_found envelope when project ref unresolved
- mr-error vs issue-side success interleaving
- combined error surfacing on either side failing

Full typecheck (node + cli + web) clean. 117 unit tests passing in
src/main/gitlab/ and src/shared/gitlab-projects.test.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): work-item-details + dialog Conversation/Pipeline tabs

Task 3 lean version. The minimal description-only dialog grows two
new tabs (Conversation / Pipeline) and four footer actions
(close / reopen / merge / comment). Files-tab and inline review-
comment positioning stay deferred — they mirror substantial GitHub-
side surface (GitHubItemDialog is 2680 lines, work-item-details.ts is
551) and the v1 demo doesn't need them.

- shared/types: GitLabPipelineJob (id, name, stage, status, webUrl,
  duration), GitLabWorkItemDetails (item + body + comments[] +
  pipelineJobs?[]). Mirrors GitHubWorkItemDetails layout.
- main/gitlab/work-item-details: getWorkItemDetails(repoPath, iid,
  type) fans out parallel reads — issue: detail + discussions; MR:
  detail + discussions, then pipeline jobs follow-up keyed off
  head_pipeline.id. Discussion → MRComment flatten skips system
  notes (auto-generated activity entries) so the conversation tab
  shows only user content. Inline-review position carried through
  as `path` + `line` for v1.5 to consume.
- main/gitlab/client: closeMR / reopenMR / mergeMR / addMRComment
  mutations. mergeMR accepts the same 'merge' | 'squash' | 'rebase'
  union as the GitHub side; close/reopen treat "already X" stderr
  as success since the desired state is reached.
- IPC: gitlab:workItemDetails, closeMR, reopenMR, mergeMR,
  addMRComment channels; preload `gl.*` bindings parallel.
- GitLabItemDialog rewrite: three Tabs (Description / Conversation /
  Pipeline-MRs-only) + footer with comment composer + state-aware
  Merge / Close / Reopen buttons. Cmd/Ctrl+Enter sends the comment
  to match gitlab.com's textarea shortcut. Refresh icon in the
  header re-fetches via a refreshNonce. eslint-disable max-lines on
  the dialog matches the GitHub-side equivalent's reasoning.

Full typecheck (node + cli + web) clean. 184 unit tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): sidebar icon, Smart-mix MRs, Integrations card, "Project MRs" rename

Four follow-up fixes that surfaced from smoke-testing:

- SidebarNav: GitLab icon next to GitHub / Linear in the Tasks-row
  shortcut strip; clicks open the Tasks page already filtered to the
  GitLab source. ui.ts taskPageData.taskSource union grows to accept
  'gitlab' so the openTaskPage call typechecks.
- SmartWorkspaceNameField: list-MRs effect now fires in 'smart' mode
  too, not just on the dedicated GitLab tab. The mixed picker
  surfaces the user's project MRs alongside GitHub items. Paste-URL
  effect still wins when a GitLab URL is in the input — the list
  effect bails on parsedGlLink !== null.
- TaskPage: GitLab toggle relabels "Project" → "Project MRs" so the
  pairing with "My Todos" reads more clearly.
- IntegrationsPane: new GitLab card mirroring the GitHub card —
  status badge (checking / connected / not-installed / not-
  authenticated), install link to gitlab.com/gitlab-org/cli, copy-
  ready `glab auth login` block, learn-more link to the auth/login
  doc, re-check button. Search-entry registered so settings search
  finds it. eslint-disable max-lines justified by the same pattern
  that already lives there for GitHub + Linear.
- preload: PreflightStatus.glab is optional on the type so older
  payloads typecheck; consumers gate on the optional chain.

Full typecheck (node + cli + web) clean. 184 unit tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gitlab): multi-repo aggregation on Tasks screen

Mirrors GitHub's cross-repo behavior. Previously the GitLab tab only
queried the first selected repo; now it fans out to every eligible
selected repo in parallel and merges results sorted by updatedAt
desc. The repo selector at the top of Tasks is the project picker —
it's the same one the GitHub tab uses, so the selection model is
consistent across providers.

- TaskPage gitlab fetch effect: Promise.allSettled across all
  selectedRepos that aren't SSH-relay (folder-mode repos and remote
  worktrees fall through). Each repo's project is resolved from its
  own git remote by the main process; non-GitLab repos return
  not_found which the renderer drops silently so a mixed selection
  (GitHub + GitLab repos) doesn't surface false errors on the GitLab
  tab.
- Per-row repoId tagging stays correct — items keep their source
  repo's id through the merge, which matters for the dialog repoPath
  resolution below.
- Banner display: only shown when EVERY eligible repo failed; partial
  failure is signaled by the row count being lower, not a banner that
  overshadows working repos.
- GitLabItemDialog repoPath: derived from the clicked item's
  source repo (selectedRepos.find by repoId) instead of primaryRepo.
  Without this, clicking an item from a non-primary repo would route
  the detail fetch through the wrong repo's remote.

Full typecheck (node + cli + web) clean. 117 unit tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(gitlab): swap MR icon to GitMerge for visual distinction

GitPullRequest (curved-merge) reads similar to GitBranch (forking
line) at the small sizes we use in the picker — feedback was that
MR rows looked like branch rows. GitMerge (arrow-merge-into-line)
reads as its own thing and matches gitlab.com's MR iconography, so
users coming from the web UI find it familiar.

GitHub PRs keep GitPullRequest — that matches github.com and keeps
provider attribution distinct from GitLab MRs at a glance:
  GitHub PR: GitPullRequest (curved merge)
  GitLab MR: GitMerge (arrow merge)
  Branch:    GitBranch (fork)
  Issue:     CircleDot (provider-agnostic)

- SmartWorkspaceNameField RowIcon + SelectionIcon: gitlab-mr →
  GitMerge. github-pr stays GitPullRequest.
- GitLabItemDialog header icon: GitMerge for MRs.

Full typecheck (node + cli + web) clean. 117 unit tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(gitlab): split shared types + preload into per-provider files

Pre-emptive merge-conflict reduction. The two recent main syncs
each surfaced ~5 conflicts, all in the same handful of central
files where every provider lands code. Moving the GitLab footprint
into provider-scoped files cuts the conflict surface roughly in half
without changing any runtime behavior.

- shared/gitlab-types.ts (new, 272 lines): every standalone GitLab
  type that previously lived in shared/types.ts —
  GitLabProjectRef / MRState / MRMergeableState / MRCheckDetail /
  MRInfo / GitLabReaction / MRComment / GitLabCommentResult /
  GitLabIssueInfo / GitLabViewer / GitLabAssignableUser /
  GitLabWorkItem / GitLabMRFile / GitLabProjectSettings /
  GitLabTodo[TargetType] / GitLabPipelineJob /
  GitLabWorkItemDetails / GitLabIssueUpdate / MRListState /
  GitLabPagedResult / ListMergeRequestsResult.
- shared/types.ts: re-exports the GitLab types so existing call
  sites importing from '../shared/types' keep working unchanged.
  GitLabProjectSettings additionally imported locally for the
  GlobalSettings.gitlabProjects field. Worktree.linkedGitLabMR /
  WorktreeMeta.linkedGitLabIssue / GlobalSettings.defaultTaskSource
  union member stay here — they're entangled with non-GitLab
  structs and moving them out would just shuffle the conflict
  vector to a different file.
- preload/gitlab.ts (new, 106 lines): the entire gl.* runtime
  binding block — viewer / projectSlug / mrForBranch / mr /
  listMRs / listWorkItems / issue / listIssues / createIssue /
  updateIssue / addIssueComment / listLabels /
  listAssignableUsers / todos / workItemDetails / closeMR /
  reopenMR / mergeMR / addMRComment / workItemByPath. Exported as
  `glApi`.
- preload/index.ts: imports `glApi` and inlines as `gl: glApi`,
  shrinking the file by ~95 lines.

Net: the two files most prone to conflict on upstream sync
(shared/types.ts, preload/index.ts) lose ~360 lines of
GitLab-specific code that now live in their own files where main's
non-GitLab edits can't touch them.

Full typecheck (node + cli + web) clean. 190 unit tests passing
in src/main/gitlab/, src/shared/gitlab-projects.test.ts,
src/main/ipc/{preflight,worktree-logic}.test.ts,
src/renderer/src/lib/gitlab-links.test.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(gitlab): satisfy pnpm pre-flight (lint + handler-registration test)

- TaskPage: lift the selected-repos identity key into a useMemo so the
  GitLab fetch effect's dep array no longer holds a complex expression
  (oxlint exhaustive-deps).
- register-core-handlers.test: mock ./gitlab alongside ./github / ./linear
  so registerGitLabHandlers doesn't try to call ipcMain.handle in a unit
  test that fakes only individual handler modules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(source-control): add Bitbucket hosted review support

* fix(source-control): align hosted review lookup with provider model

---------

Co-authored-by: Emilian Stoilkov <emilian.stoilkov@qaiware.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:09:52 -07:00
Neil f0705af998 perf(git): fold upstream status into git poll (#1828)
* perf(git): fold upstream status into git poll

* fix: keep git status polling lint-clean
2026-05-14 01:37:36 -07:00
Jinjing 17aef50c82 fix: handle non-git worktree paths (#1805) 2026-05-13 22:22:51 -07:00
Jinjing 5a6b2994ea fix: address review findings (#1770) 2026-05-13 16:14:50 -07:00
Brennan BensonandOrca 8f3c783767 fix(worktree): create branches with --no-track and auto-setup remote (#1563)
* WIP: Changes before auto-review fixes

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

* WIP: Changes before auto-review fixes

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

* fix(worktree): preserve user push.autoSetupRemote, include path in warn

- Probe push.autoSetupRemote with `git config --get` before writing so a
  deliberate user value at any scope (local/global/system) is preserved.
- Include worktree path in the warn log for failed config writes.
- Add test pinning the preserve-existing-value behavior.
- Remove stray 00-review-context.md committed during review tooling.

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

* WIP: Changes before auto-review fixes

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

* fix(worktree): narrow config --get error handling, tighten test asserts

Treat only exit code 1 from `git config --get push.autoSetupRemote`
as "key unset". Other read failures (corrupt config, locked file,
parse error) now re-throw to the outer warn handler instead of being
silently treated as unset and overwriting whatever value the user
actually has.

Also: add test for the non-unset read-error path; convert the
"preserves existing value" test from `.some()` predicates to a
full-array `toEqual` matching sibling-test style; explicitly mock
`config --get` (with code: 1) in the sparse-failure rollback test
so it exercises the intended branch instead of the helper's empty-
stdout fallthrough; document in the design notes that
addSparseWorktree's rollback intentionally does not unset
push.autoSetupRemote.

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

* test(worktree): pin --get-empty-stdout and worktree-add-fail invariants

Why: addWorktree's post-create config probe has two ordering
invariants worth pinning so a future refactor can't silently
regress them: (1) `git config --get` succeeding with empty stdout
still counts as "already set" so we don't overwrite an explicit
empty value, and (2) the entire config block is skipped when
`worktree add` itself rejects.

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

* WIP: Changes before auto-review fixes

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

* docs(worktree): cross-ref local↔SSH addWorktree, clarify SSH-host git version, add empty-stdout parity test

JSDoc on local addWorktree now flags the push.autoSetupRemote side
effect; both paths cross-reference each other so the next change keeps
them in lockstep. Relay comment clarifies that the git version that
matters is the SSH host's, not the client's. Adds the missing
empty-stdout-as-already-set parity test on the relay side.

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

* chore: remove 00-review-context.md from PR

Stray file from local review workflow; should not ship in this PR.

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

* chore: remove worktree-ssh-no-track-parity.md from PR

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-11 12:46:03 -07:00
buf0-bot[bot]andorca-bot 390f9f52fc fix: pr-bug-scan findings from #1524 (#1572)
Don't seed empty-success worktree roots in worktrees:list[All] catch (prevents auth-cache poisoning); restored focus-gated fetchWorktrees polling for the active repo so non-active worktrees pick up terminal-driven branch switches; encode detached HEAD as undefined instead of '' so renderer's nullish-coalescing preserves prior branch.

Rebased onto current main to apply only the intended diff.

Co-authored-by: orca-bot <bot@stably.ai>
2026-05-07 22:59:33 -07:00
buf0-bot[bot]andorca-bug-scan-bot 9bc8166ad8 fix: pr-bug-scan findings from #1465 (#1558)
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
2026-05-07 21:58:13 -07:00
Neil 7b83b2dcdc fix: avoid repeated macOS privacy prompts (#1524)
* fix: avoid repeated macos privacy prompts

* fix: reduce background worktree permission probes

* chore: pin oxlint for ci

* fix: preserve optional rpc params with zod 4.4

* fix: preserve optional inline rpc params with zod 4.4
2026-05-06 23:30:03 -07:00
Brennan BensonandOrca e7fef4e14d fix(source-control): preserve UTF-8 paths in status and branch diff (#1515)
Co-authored-by: Orca <help@stably.ai>
2026-05-06 16:22:24 -07:00
9f8bf81c38 feat(source-control): commit, push, pull, and sync actions in panel (#1211)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Alexander Saavedra <mralexsaavedra@gmail.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-05-06 15:03:52 -07:00
Jinwoo HongandOrca 28caefd82c Handle stale SSH repo worktree refreshes (#1508)
Co-authored-by: Orca <help@stably.ai>
2026-05-06 13:01:09 -07:00