- Late child/parent settlements now report individually instead of
being batched into a single combined result, so toasts and
onLateResult callbacks reflect actual per-candidate outcomes.
- Post-batch reconciler retains only unresolved provisional-ancestor
state instead of the full batch arrays, and releases it once no
provisional ancestor remains, avoiding unbounded retention for
long-hung IPC promises.
- Unblocked ancestors retried post-batch now go through the same
timeout/settlement tracking as in-batch removals, including
further late settlement if the retry itself hangs.
* fix(terminal): preserve Linux IME candidate digits
* fix(terminal): preserve overlapping Linux IME keys
* docs(terminal): document Linux IME candidate state
* docs(terminal): describe IME candidate event handling
* docs(terminal): clarify Linux IME state callbacks
* fix(terminal): harden Linux IME candidate fallback
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: yuqili03 <yuqili03@deeproute.ai>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* feat(diff): add F7/Shift+F7 keyboard navigation for diff changes
Stacks on the Previous/Next change buttons (#6668) to add keyboard
navigation for single-file diffs, matching VS Code / JetBrains diff review.
- Register editor.nextChange (F7) / editor.previousChange (Shift+F7) in the
keybinding registry (Editors group) so they show in Settings and stay
rebindable.
- Teach the keybinding normalizer function keys (F1-F24) and make them
first-class in the bare-key safety model (safe standalone or with Shift,
opt-in per action) - F7 was previously unbindable.
- Install a capture-phase listener from DiffNavigationProvider so keyboard
and the existing header buttons share one goToDiff path; works on
read-only and editable single-file diffs.
- Translate the Previous/Next change strings for es/ja/ko/zh.
Refs #6215
* test(diff): cover change navigation shortcuts
* fix(diff): use shortcut chips in navigation tooltips
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* feat(diff): add Previous/Next change navigation buttons to Diff View
Add up/down navigation buttons to the single-file Diff View toolbar so
users can jump between change regions (hunks) without scrolling, matching
IntelliJ/Android Studio. The buttons bridge the Monaco diff editor to the
header via an instance-scoped DiffNavigationProvider and call Monaco's
goToDiff('previous'|'next'); they disable when the file has no changes.
Closes#6215
Co-authored-by: Orca <help@stably.ai>
* test(diff): cover stale onDidUpdateDiff guard in fast-swap
Address CodeRabbit nitpick: prove that an update fired from a replaced
editor is ignored after a new editor registers (the subscription is
disposed on re-register). The fake editor's dispose now clears its
callback, matching real Monaco subscription semantics.
Co-authored-by: Orca <help@stably.ai>
* fix(diff): isolate navigation registration updates
* fix(diff): use arrows for change navigation
* fix(editor): tighten header action spacing
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* perf(renderer): sweep suppressedPtyExitIds and pendingCodexPaneRestartIds on bulk worktree purge
Both are ptyId-keyed maps cleared alongside codexRestartNoticeByPtyId on the
normal pty-exit path, but buildWorktreePurgeState (remove-project / external
authoritative-scan / hydration-stale reconcile) runs no terminal teardown and
only swept codexRestartNoticeByPtyId and migrationUnsupportedByPtyId. A
suppression / pending-restart flag planted just before a doomed pty's exit
therefore stranded one entry per removed pty for the renderer session.
Add both to omitByPtyId, matching their two swept siblings; extend the
bulk-purge leak regression test to cover them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(renderer): cover dormant PTYs in bulk purge
* Fix worktree-purge PTY id leaks: proportional alias scan and stricter su
- purgeWorktreeTerminalState now short-circuits the surviving-alias scan
once every doomed remote handle alias is matched, instead of scanning
every surviving tab's terminals on each purge.
- Repo removal (removeRepo) no longer force-marks killed PTY ids as
suppressed/pending-restart; those tabs are gone before async exit
events fire, so retaining the guards just leaked entries.
- Update tests to cover raw remote-handle aliasing against surviving
local PTY ids and to assert the tightened cleanup behavior.
* Fix environment-scoped remote PTY id handling for guards and lifecycle s
- Canonicalize legacy remote PTY ids at attach time so guard/restart/notice
maps stay scoped per runtime environment instead of colliding on raw
aliases shared across environments.
- Migrate suppressedPtyExitIds, pendingCodexPaneRestartIds,
codexRestartNoticeByPtyId, and migrationUnsupportedByPtyId entries when a
tab's PTY id transitions from a legacy alias to its scoped id.
- Narrow shutdown/purge guard sets to renderer-visible PTY ids only, since
raw exact-stop RPC handles never emit pane exit callbacks and previously
caused cross-environment leakage and unnecessary alias scanning.
- Prevent a late PTY exit from resurrecting a ptyIdsByTabId entry for a tab
whose worktree/repo was already purged.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* fix(i18n): use useTranslation hook in SidebarNav for reactive language switching
Replace plain translate() calls with the useTranslation() hook so the
React.memo-wrapped SidebarNav re-renders when the i18n language changes
after async catalog loading, fixing sidebar labels staying in English
on non-English locale startup.
Closes#7530
* fix(i18n): make sidebar language subscription race-safe
* refactor(i18n): use standard sidebar language subscription
---------
Co-authored-by: 文小松 <wenxiaosong@wenxiaosongdeMac-mini.local>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* fix(pr-comments): let users mark comment authors as bots for the Humans/Bots filter
Some review bots post from regular user accounts that defeat both provider
bot metadata and login heuristics, so their comments were misclassified as
human. Adds a persisted prBotAuthorOverrides setting with a "Mark author as
bot" comment action, applied consistently across desktop and mobile.
Fixes#7597
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(pr-comments): address review feedback on bot-author overrides
- Cap sanitized prBotAuthorOverrides at 500 entries so malformed payloads
can't bloat GlobalSettings or slow comment classification
- Reuse the shared normalizePRCommentAuthorLogin in isBotPRComment on
desktop and mobile instead of duplicating the normalization inline
- Pass botAuthorOverrides from CommentRow to CommentMoreMenu instead of
re-subscribing per menu instance
- Re-fetch mobile bot-author overrides alongside each PR refetch so they
don't stay a stale one-shot snapshot for the whole session
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(pr-comments): harden bot author override sync
* fix(pr-comments): bound and recover override updates
* fix(pr-comments): merge overrides from canonical settings
* fix(pr-comments): make bot override updates atomic
* fix(pr-comments): surface rejected bot overrides
* fix(i18n): translate bot override warning
* fix(i18n): translate bot author actions
---------
Co-authored-by: Dzmitry Bachko <dbachko@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* Surface the Pi CLI's real error when branch auto-naming and commit generation fail
Pi failures (missing provider credentials, HTTP 4xx/5xx, connection errors)
previously collapsed to the generic 'Pi CLI command failed with code 1.'
because extractAgentErrorMessage only recognized Error:-prefixed lines.
Add two stderr-only extraction passes for pi's failure formats and narrow
the unix-path redactor so pi's /login remedy token survives redaction.
Fixes the OP variant of STA-1492 (#7808).
* Replace per-CLI failure parsing with sanitized raw output excerpts
Every agent CLI formats errors differently, and the parsing passes only
ever covered the vendors someone had already debugged (the pi passes
fixed pi and nothing else). Show the output itself instead: a positional
excerpt (first two non-empty lines plus the last one) of stderr, falling
back to stdout when stderr is silent, path-redacted and capped as
before. Exit-0 runs with empty stdout now stay 'returned an empty
<result>' instead of misreporting a command failure. The sanitizer also
strips Cf characters (bidi overrides) now that provider-controlled
bodies flow through verbatim, and OSC sequences are stripped alongside
CSI.
* Keep the full CLI output of failed branch-name generation for on-demand viewing
The persisted rename-failed badge carries only a sanitized excerpt (it
syncs to paired clients), so the complete diagnosis was previously
buried in the main-process log. Failed generations now capture their
full stdout/stderr (bounded to 64 KiB per stream, head and tail) into a
main-memory-only store keyed by worktree — never persisted or synced.
The rename-failed dialog fetches it on demand and shows it in place of
the excerpt, ANSI/control/bidi-stripped; after a restart or on paired
web clients it falls back to the excerpt. Renderer-bound generation
results explicitly drop the capture so IPC payload shapes are
unchanged.
* Cover the rename-failed dialog's full-output fetch and excerpt fallback
* Split the folder-workspace title rename flow into its own module
first-work-branch-rename.ts sat at the max-lines ceiling; the folder
title flow is a self-contained concept and main's concurrent additions
to this file pushed the CI merge ref over the limit.
* Harden the failure-output capture and dialog against review findings
- Flatten bounded captures so V8 sliced strings no longer retain the
multi-megabyte parent stream in the capture map (128 MiB -> ~4 MiB in
a 32-entry probe).
- Require an OSC terminator and stop its char class at newlines so an
unterminated/boundary-truncated sequence can no longer swallow the
omission marker and diagnostic tail.
- Exclude stdout from the persisted branch-name failure detail (it can
echo the prompt into synced metadata); the full local-only capture
still keeps it for the dialog.
- Refetch and reset the dialog's full output when the persisted error
changes so an open dialog never shows or copies a stale run.
- Report signal-terminated generators (null exit code) as 'was
terminated before exiting' instead of 'code null'.
* Redact JSON-escaped Windows drive paths without breaking scheme URLs
Provider JSON bodies double backslashes (C:\\Users\\name), which slipped past the drive-letter redaction into the persisted, client-synced failure excerpt. Allow repeated backslashes only — a URL's :// must stay single so remedy links survive.
* Fix branch rename failure IPC re-registration
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Three independent root causes all produce unhandled Promise rejections
that accumulate in the renderer heap and eventually exhaust it:
1. node-pty Windows backend: WindowsTerminal.kill(signal) throws
"Signals not supported on windows." for any signal argument.
Add killPtyProcess() helper in pty-handler.ts that catches the
Windows signal error and falls back to kill() with no argument.
Update isPtyAlreadyGoneError() in main/ipc/pty.ts to classify
the Windows signal error as an already-gone PTY so it does not
re-throw and propagate to the renderer as an unhandled rejection.
2. Uncaught pty.kill() calls in renderer: three call sites in repos.ts,
pty-transport.ts fire-and-forget window.api.pty.kill() without
.catch(). On SSH disconnect or Windows PTY teardown these produce
unhandled rejections that the crash-diagnostics breadcrumb system
retains in memory without releasing. Add .catch(() => {}) to each.
3. ResizeObserver loop: TerminalPane.scheduleSync ran syncPaneTitleOverlayRects
inside a single requestAnimationFrame, which fires while the browser
is still dispatching ResizeObserver notifications. The resulting React
state update re-triggers the observer, producing a "ResizeObserver loop
completed" error on every resize and potentially freezing the renderer
thread. Switch to a double-rAF so the work defers to the next paint
frame after the observation cycle completes. Also suppress the
ResizeObserver loop ErrorEvent in crash-diagnostics.ts so that
any that slip through do not accumulate in the breadcrumb buffer.
Add .catch(() => callback(undefined)) to the Promise.all in
terminal-link-handlers.ts so SSH-disconnect errors don't flood
the unhandled-rejection count.
Co-Authored-By: Claude <noreply@anthropic.com>
Persist and safely restore the active top-level view on startup. Unknown, removed, legacy, or unavailable views fall back to the terminal, while cross-window UI sync cannot navigate the current window.\n\nCloses #8264
* fix(settings): make WSL skill commands pasteable (#7795)
* Fix WSL skill commands so PowerShell 7 pastes match PowerShell 5.1 argv
- Encode the WSL login-shell script as base64 and decode/eval it inside
the sh -c invocation, avoiding raw nested quotes at the paste boundary
- Scope $PSNativeCommandArgumentPassing = 'Legacy' to the invocation so
PS 5.1 and PS 7 both hand wsl.exe the same escaped argv
- Extract powershell-native-argument.ts as the shared quoting module and
reuse it from ssh-remote-powershell.ts
* test(runtime): stub getRepo in mobile-tab startup cwd test
Main's #7892 made listMobileSessionTabs validate selectors via
this.store?.getRepo; the mock store here only defined
getWorkspaceSession, so the merged CI build threw 'getRepo is not a
function'. Return null (wt-1 is a worktree id, not a repo).
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Automatically resolve and require explicit push targets for linked
GitHub PRs and GitLab MRs before allowing remote actions.
This prevents operations like push or sync from targeting helper
upstreams or falling back to default behaviors when the review target
is unavailable. Also, recover missing push targets during metadata
saves to heal existing linked PRs.
* fix: work-item naming, usage % rounding, and terminal/delete copy
Address prod-release-scan P2s:
- #8238: recognize Bitbucket Server (/projects|users/.../repos/.../pull-requests/N)
and Azure DevOps (/_git/REPO/pullrequest/N) PR URL shapes in
work-item-reference, alongside Bitbucket Cloud; graceful fallback preserved.
- #7574: getDisplayedUsagePercentage now rounds the used value before taking the
`remaining` complement, so the compact status bar (raw usedPercent) and tooltip
(pre-rounded clampUsedPercent) can no longer disagree by 1% at a .5 fraction.
clampUsedPercent moves to the shared module as the single rounding source.
- #7459: mixed remote+local batch delete confirm no longer claims the whole
batch is a permanent "remote host" delete — it now states remote items are
permanent while local items move to the Trash/Recycle Bin.
- #8322: right-click-to-paste settings copy is platform-aware — "Control-click"
on macOS, "Ctrl+right-click" on Windows/Linux — matching the ctrlKey gate.
Localization catalog synced (also picks up pre-existing UsagePercentageDisplayChangeNotice drift).
* Fix NaN% usage bar and label for non-finite provider values
Non-finite usedPercent inputs (NaN/Infinity) propagated through Math.round/min/max into the CSS bar width (`NaN%`) and displayed copy. clampUsedPercent now short-circuits to 0 in that case, with a test covering the divergence from getDisplayedUsagePercentage for the 'remaining' case.
* fix(cleanup): reconcile late removal results
* fix(cleanup): report authoritative late removals
* fix(cleanup): reconcile skipped ancestors on late child settlement
Ancestor rows skipped past the removal deadline were reported as
definitive failures even when the blocking child later succeeded or
was still in flight. Track provisional vs. definitive skips, re-derive
them as child settlements arrive mid-batch, requeue unblocked
ancestors for retry, and add a "still removing" toast state so batch
summaries stop contradicting rows that settle later.
* Fix skipped-parent rows going stale after post-batch child settlement
- Post-batch late child results (settling after the batch loop ends)
previously ignored ancestor skip state, so a parent skipped for a
since-resolved child kept a stale "could not be removed" failure
instead of reclassifying and retrying.
- Extracts reclassification logic into
workspace-cleanup-skipped-ancestor-reclassification.ts, shared by the
mid-batch and new post-batch paths.
- Adds workspace-cleanup-post-batch-late-settlement.ts to reconcile
ancestor skips and retry unblocked parents after late child
settlement, serialized via a reconcile chain to avoid interleaving
concurrent settlements.
* docs: design Grok orchestration group
* docs: plan Grok orchestration group implementation
* fix: add Grok orchestration group
* test(orchestration): accept Windows skill newlines
* Fix @grok orchestration group matching and remove stale planning docs
- Reuse the shared buildAgentNameRe matcher in groups.ts instead of a
divergent local regex, so orchestration groups honor the same
Windows launcher-suffix rule (grok.exe/.cmd/.bat/.ps1) as the rest
of Orca's agent-title detection.
- Add test coverage for real Grok OSC title shapes (spinner-collapsed,
session titles) and Windows launcher-suffix titles.
- Delete the now-completed design and implementation-plan docs for
the Grok orchestration group work.
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Include fallbackGitHubPR alongside linkedGitHubPR/linkedGitLabMR when
determining whether a hosted review link resolves to a push target.
Worktrees without persisted linkedPR metadata (e.g. child worktrees)
were incorrectly blocked with "target unavailable" despite having a
real matching upstream, since their PR was only known via the queue
fallback. Also splits hasPositiveHostedReviewNumberLink to build on
the resolvable subset so the two helpers can't drift.
- Pass the active `worktreePath` through IPC, RPC, and the GitHub client
to ensure we fetch the correct HEAD OID when resolving merged PRs.
- Validate incoming worktree paths against known repository worktrees in
the main process to prevent forged path usage.
- Escalate Checks Panel PR refresh requests from 'swr' to 'active' when
a cached "no PR" miss predates when the panel became visible.
- 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 host removal lifecycle safeguards and credential cleanup retry UI
- Sequence host removal so metadata commits before the client socket
closes, avoiding a stranded host when storage fails, and add a
cancellable open-registry to stop races between host-client opens
and closes/unmounts.
- Queue AsyncStorage host-list mutations (rename/removal/lastConnected)
to prevent concurrent writers from clobbering each other's changes.
- Track keychain credential cleanups that fail or time out as durable
pending intents, surfaced with a manual retry affordance in Settings.
* Fix host removal error handling to reopen confirm dialog and alert user
Previously a failed host removal silently closed the confirm dialog,
leaving the host listed with no feedback and no easy retry path. Now
the confirm modal reopens and an alert surfaces the failure so the
user can retry.
* test: reconcile settings tests with universal right-click paste and promoted worktree symlinks
Merging main surfaced two semantic conflicts against this branch's tests:
- #8322 exposed right-click paste on every platform, so the settings
navigation metadata now indexes it even when only the terminal host is
Windows. Update the stale assertion accordingly.
- #8318 promoted APFS worktree shared paths by dropping the
experimentalWorktreeSymlinks gate, so WorktreeSymlinksSection now always
mounts inside RepositoryPane and reads window.api.fs. Stub a minimal
renderer fs bridge in the pane test, matching the AppearancePane pattern.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
Remove the duplicated open/start workspace buttons and dropdown from
GHEditSection now that the primary CTA lives in the issue header;
show the attached workspace or "None yet" as plain metadata instead.
* fix: address pr-bug-scan validated finding from #6799
Retain last-good resolved cwd on empty/error getCwd poll instead of committing null, so a transient lsof timeout no longer flips the followed worktree and resets the panel.
* Add renderer startup timing diagnostics and fix stale terminal-cwd reten
- Wraps each renderer startup hydration step (settings, repos, worktrees,
session, SSH reconnect, etc.) with timing instrumentation gated behind
ORCA_STARTUP_DIAGNOSTICS, wired through a new IPC channel
(app:startupDiagnostic) so cold-start regressions can be measured.
- Fixes the Checks panel's terminal-worktree tracking to clear the
retained cwd when the active terminal itself changes, instead of only
retaining across transient empty/error polls on the same terminal.
---------
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* feat(file-explorer): confirm multi-select deletes with a single batch prompt
Deleting a multi-file selection on a remote host prompted once per file:
requestDeleteAll awaited runDelete per root, and the remote-delete
confirmation lived inside runDelete. Hoist the confirmation for batches —
one 'Permanently delete {{count}} items?' dialog up front, then per-node
deletes with skipConfirmation. Single-file and local (Trash) deletes are
unchanged. Root filtering and the batch loop move to
file-explorer-batch-deletion.ts with unit coverage.
Fixes#7457
* fix(file-explorer): count the full selection in the batch delete prompt
Address review: roots.length undercounts when a selected directory's
children are also selected (could even read 'delete 1 items?'). Use the
visible selection size instead. Also reword the zh strings so 项目
cannot read as 'project' in a file-delete flow.
* review: skip batch delete confirm for unresolved-owner selections
Narrow the batch-confirm gate to a resolvable remote route so an
unresolved-owner multi-select no longer pops a destructive prompt for
deletes that fail closed anyway — mirroring the single-delete path.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Tom de Bres <tomdebres@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* feat(markdown): support HTML superscript links
* Add HTML superscript citation links to the rich markdown editor
- Introduce marked-based tokenizer support for `<sup><a>` citation
links, with a shared editor context so key/link handlers can resolve
selection status and open an action bubble for citation atoms
(which markdown setLink/unsetLink can't edit).
- Extract clipboard-write logic shared by cut and cut-visual-line into
rich-markdown-clipboard-write.ts, and surface a cut-limit error when
clipboard readback fails.
- Fix MarkdownPreview same-file anchor scrolling to run before the
unknown-ownership guard so ambiguous folder-workspace ownership
still scrolls within the open document.
- Keep search's replace-disabled state derived from live matches
instead of a stale snapshot.
* Update lockfile to dedupe stale package versions after dependency resolu
- Removes duplicate/superseded resolutions (older @babel/* versions, fs-extra 11.3.4/11.3.5, plist 3.1.0, semver 7.7.4) left over from a prior install
- Aligns transitive deps to the single resolved versions already in use elsewhere in the tree
* feat(status-bar): notify upgraded users usage meters show % used
Show a one-time status-bar callout when upgraded profiles still use the
new percent-used default. Brand-new profiles and users who already chose
remaining stay quiet; dismissing or changing the setting is permanent.
* Add settings deep-link to expand Appearance's Window accordion for one-s
- Replaces the searchQuery-based redirect (fragile, flashed filter UI) with a
dedicated appearanceAccordionDeepLink store field that force-opens the
correct accordion and scrolls to the target row
- Rebuilds the status-bar change notice as a plain elevated card instead of
a Popover, since PopoverContent's glass/backdrop-filter defaults fought
the opaque callout styling and needed heavy overrides
- Simplifies the light/dark card CSS tokens accordingly
* Reposition status-bar usage-change notice via fixed-position portal
Portal the one-shot callout to document.body with fixed positioning
anchored via getBoundingClientRect, since the status-bar's overflow-hidden
flex ancestors clipped or mispositioned the previous in-tree absolute
layout.
* Refine status-bar usage notice styling and test coverage
- Replace hand-tuned light/dark card colors with existing design
tokens (--popover, --border) and the documented floating elevation,
so the callout stays in sync with the design system instead of
duplicating its own palette
- Add tests covering dismiss via X button, "Got it", and Escape
- Scope the foreground-process confirm assertion to the pane's ptyId
so an unrelated pane's delayed confirm can't cause a false failure
* fix(naming): lead workspace and tab names with the work-item identifier
Auto-generated workspace names, branch-rename display names, and tab
titles now lead with the referenced PR/MR/issue/ticket — e.g.
`PR 1033 - Review` instead of a paraphrase like "Review community pr
1094" that buried or dropped the number. Identifiers are the highest-
signal, most searchable token, so leading with them makes the sidebar
and tabs scannable and disambiguates same-verb work items.
- New shared `work-item-reference.ts` extracts the identifier from the
raw prompt: URLs are validated by path structure (owner/repo/pull/N,
GitLab's `/-/` marker) so GitHub Enterprise / self-hosted GitLab still
resolve while stray `/pull/<n>` paths (CDN, docs) do not; a ticket-
prefix denylist keeps `SHA-256` / `UTF-8` / `ISO-8601` from being read
as Jira/Linear keys.
- Reconciles the existing create-from-work-item naming (was action-first
`Review PR 1033`) with the first-work auto-rename onto one identifier-
first format via a shared `formatIdentifierFirst`, so the two paths
can't drift.
- Fixes a pre-existing tab-title bug where markdown punctuation was
stripped before URLs, splitting a GitLab `merge_requests` URL at its
underscore and leaking "requests" into the title.
* fix(naming): keep emphasis-wrapped URLs intact in generated names
The URL/markdown strip reorder left the tab-title URL strip anchored on
\b, which fails when a URL is wrapped in markdown emphasis (_...pull/5_)
and leaked URL fragments into the title. Drop the \b anchor and trim
trailing markdown emphasis (*_~) in the URL identifier parser, keeping
interior underscores (merge_requests) intact. Adds regression tests.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>