* feat: make relay opt-in for device pairing
* fix(relay): keep pairing state authoritative
* fix(relay): refresh pairing state across transports
* fix(onboard): honor explicit relay opt-out
* fix(relay): surface transport startup failures
* fix(pairing): gate offers for older daemons
* test(cli): rely on real pairing transport coverage
* fix(cli): report live relay without a pid file
* fix(relay): synchronize config during daemon startup
* fix(relay): preserve mutability for ignored overrides
* fix(cli): use reachable daemon for pairing
* fix(app): redirect invalid pair device routes
* fix(pairing): support remote host device pairing
Keep Pair Device host-relative through the existing settings and daemon RPC flow. Route CLI output through an injected adapter so workflow tests do not mutate process globals.
* fix(pairing): validate pairing destinations
Expose the host-relative Pair Device flow on every supported app platform. Reject a reachable daemon whose identity does not match the requested Paseo home before reading or mutating its pairing state.
* fix(pairing): render QR codes across platforms
Generate SVG text without DOM or canvas and render it through the existing cross-platform SVG stack.
* fix(cli): pair from persisted relay state
Allow explicit relay pairing while the daemon is stopped when relay is already enabled in persisted configuration.
* feat(projects): add local icons and colors
* refactor(projects): simplify appearance handling
* fix(projects): serialize appearance saves
* test(app): update project appearance expectations
* test: fix project appearance CI expectations
* refactor(projects): organize appearance code
* fix(server): adopt main's reconcile model in project appearance updates
* fix(projects): preserve host-local appearance targets
* fix(desktop): restore Linux development startup
Ubuntu can block Electron's unprivileged user-namespace fallback while the npm-installed sandbox helper is not SUID. Disable the Chromium process sandbox only in the Linux development runner so packaged desktop policy remains unchanged.
* feat(projects): add custom project icons
Keep URL acquisition in the client so the host daemon only validates uploaded bytes. Project name and icon changes share one adaptive edit sheet.
* refactor(settings): scope projects by host
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* Switch reasoning, mode, plan, and fast from the Command Center
Extend the Command Center (Cmd-K) with selectors for a focused agent's
Thinking (reasoning effort), access Mode, Plan mode, and Fast mode,
mirroring the model switcher. Typing surfaces tiered "<Setting> › <Value>"
breadcrumb rows with icons and a check on the current value, for both a
running agent and a new draft tab. Rows only appear while typing, so the
default palette is unchanged.
- Thinking and Mode are multi-option selects; Plan mode and Fast mode are
On/Off toggles.
- Plan mode is dual-natured: a permission mode for Claude/Copilot/OpenCode
(excluded from the Mode list, given its own toggle) and the plan_mode
feature for Codex. Fast mode drives the fast_mode feature (model-gated).
- All logic lives in command-center/agent-setting-contributions.ts as pure,
unit-tested builders, with a small icon helper and a hook that registers
the running-agent selectors; the draft registers from its composer tab.
No changes to the command palette component and no protocol changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Unify Command Center agent controls
* refactor: clarify agent control ownership
* refactor(app): tighten command center agent controls
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(settings): choose which orchestration skills to install
The skills row installed the whole bundle or nothing. A settings control
next to it now opens a sheet where you keep every bundled skill or pick a
subset, and saving converges ~/.agents, ~/.claude, and ~/.codex to it.
"All skills" reads the bundle directory instead of a hardcoded catalog, so
a skill added in a later release installs itself without another code
change. Retired bundle names stay in a separate cleanup list so old copies
on disk are still removed but never become selectable.
The selection persists under Electron user data, separate from install
state, so uninstalling does not erase the choice — a later install brings
back exactly the chosen set. Installs with no saved selection read as
"all", which is what they already had.
* Restore skill selection after failed save
* fix(settings): don't save a skill selection that never reached disk
A failed save wrote the preference, converged, then wrote the old preference
back. That leaves a window where a selection the user was told did not save
is the one on disk, and a crash inside it makes the window permanent.
Converge first and persist only on success — nothing to roll back, and a
failed save now leaves disk and preference exactly as Cancel would.
Resolving the bundle path when the command handlers are wired also broke
daemon-manager's tests: the packaged branch reads process.resourcesPath,
which is undefined outside Electron. Resolve targets per command instead —
registering handlers should not touch the environment.
* fix(settings): make skills convergence single-writer and confirm removals
Startup auto-update and the settings commands were separate writers over the
same skill directories. Either could apply a plan computed from a selection the
other had already replaced — narrow the selection while startup is repairing
drift and the repair puts the removed skill back. There is now one controller
that owns scan, apply, and persist, and every entry point queues through it, so
concurrent callers see a consistent result instead of an interleaved one.
Save also had no recovery once it had converged: if the selection could not be
committed afterwards, disk was changed while the preference still said the old
thing, and the next startup silently undid the change. Save now puts the
managed directories back to what the committed selection describes before it
reports the failure, so a failed Save lands where Cancel would.
Deselecting a skill deletes its directory in all three agent homes, including
files the user put inside it. That is now confirmed like every other
destructive action, naming the skills and the directories. Adding skills or
saving an unchanged selection still saves without a prompt, and a skill the
host has not installed yet deletes nothing so it does not prompt either.
* fix(settings): confirm removal for skills installed in only some agent homes
The removal confirmation read an `add` op as "not on disk yet". An `add` op
means "missing from at least one target", so a skill sitting in ~/.claude but
not the other two looked absent, and deselecting it deleted that directory —
with anything the user had put inside it — without asking.
The status snapshot now carries the installed skill names directly, taken from
union presence across the three agent homes, and the confirmation reads that.
`ops` keeps its one meaning.
A host that predates the field cannot say what is on disk, so the app bridge
assumes the saved selection is installed. Asking about a directory that turns
out to be absent is harmless; the reverse is not.
* fix(settings): warn about every skill directory a save deletes
The confirmation asked "was this selected before and is it selected now",
which is a different question from the one the host answers. The host
converges to the desired set and deletes every managed directory outside it,
so a skill on disk that the saved preference never mentioned — reinstalled by
hand, restored by a sync, left behind by drift — was deleted with no warning,
user files included.
The predicate now mirrors the host rule: everything installed that the draft
will not keep, regardless of what was saved before. It moved out of the sheet
into its own module so the rule has one owner and can be tested directly.
Saving with nothing to delete still does not prompt.
* fix(settings): a failed save keeps files the user added inside a skill
Save converges before it commits, and convergence deletes whole skill
directories. When the commit then failed, the recovery re-installed the
committed selection — which rebuilds the bundled files and nothing else, so
notes, hooks, and anything else the user had put inside a deselected skill
were gone for good. Cancel does not do that, and a failed save is supposed to
land where Cancel would.
Save now runs as a transaction. Before converging it stages a copy of every
managed skill directory across the three agent homes, next to the tree so the
copy stays on one filesystem. Committing drops the staging; failing anywhere
puts each directory back exactly as it was, deletes the ones the candidate had
created, and then drops the staging. Staging left by a process that died
mid-save is cleaned up by the next one.
The controller only sees begin/commit/rollback; the filesystem work lives in
skills-transaction.ts.
* fix(settings): make skill removal confirmation authoritative
* fix(settings): verify skill convergence before commit
* fix(settings): preserve concurrent skill edits on rollback
* fix(settings): preserve staged skill metadata
* fix(settings): stage skill deletions atomically
* fix(settings): stage removals per filesystem
* fix(settings): restore staged skill symlinks
* fix(settings): canonicalize skill transaction roots
* fix(settings): keep automatic skill updates non-destructive
* fix(settings): report committed skill saves accurately
* fix(settings): keep skill updates non-destructive
* fix(settings): route skill removals through selection
* fix(settings): close skill selection edge cases
* fix(settings): preserve skill data during maintenance
* fix(settings): hide unavailable skill install
* fix(desktop): keep install policy at command boundary
* fix(desktop): restore staged skill directories atomically
* test(app): retry transient skills sandbox cleanup
* fix(desktop): recover externally deleted skill stages
* fix(desktop): preserve conflicting skill rollback data
* fix(desktop): generalize skill rollback conflicts
* fix(desktop): preserve skill rollback collisions
* fix(desktop): recover quarantined skill backups
* fix(app): inset adaptive sheet content on compact
The compact Choose skills sheet rendered edge to edge: the All skills card
touched both screen edges, the section label was glued to it, and the skill
list ran to the edges too. Desktop was correct.
AdaptiveModalSheet expressed its content inset through
BottomSheetScrollView.contentContainerStyle. That is a library prop, not the
style prop Unistyles registers, and gorhom's scroller was never rewritten by
the Babel plugin, so on web the emitted class was never applied and the
padding and gap resolved to nothing. The desktop card uses a real ScrollView,
which Unistyles does track, which is why only compact was affected.
The inset now lives on a View the sheet owns, rendered by every presentation,
so one definition covers compact and desktop and a scroller only ever gets
theme-free layout. The caller-facing prop is contentStyle, since it no longer
lands on a scroll content container.
Choose skills drops its ad-hoc label for SettingsSection, so the label carries
the app's section rhythm instead of sitting equidistant between two cards.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(desktop): prevent skill rollback data loss
Preserve externally replaced directory trees during recovery and reject managed child symlinks before synchronization can write outside a skill directory.
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix(app): distinguish changed and deleted files
Successful reads now reconcile transient missing events without letting stale
cached data mask a deletion. File notices expose only actions valid for the
current buffer state.
* fix(app): preserve file state ordering
* fix(app): allow retrying file checks
* fix(app): clear unchanged file conflicts
* fix(app): keep file conflict recovery consistent
Own disk observation and read ordering in one model so stale reads and failed subscriptions cannot produce contradictory actions.
* fix(app): allow retrying file preview errors
Preserve daemon read errors and expose retry for initial and non-editable file loads.
* fix: reject oversized checkout diffs
* fix(diff): keep large previews from disconnecting clients
The physical socket limit is an OOM backstop, not a frame-size contract. Bound structured diff construction incrementally and report oversized previews without closing the client's socket.
* fix(app): preserve oversized diff state
* fix(diff): stay within relay frame limit
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(app): open project folder from project context menu (#2487)
* refactor(app): give file manager action a home
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(app): add ⌘P shortcut to switch project on New Workspace screen
Opens the existing project picker with its search focused so the project
can be switched from the keyboard (type + Enter) instead of clicking the
badge and then the project.
Wires a new "workspace.project.pick" action through the standard keyboard
pipeline (binding -> route passthrough -> dispatcher), handled by a
screen-scoped handler on the New Workspace screen that is only registered
while the screen is mounted and there are projects to pick. Because
preventDefault only fires when a handler handles the key, ⌘P/Ctrl+P still
triggers native print everywhere else. Adds a Settings -> Shortcuts help
row (rebindable) and the "Switch project" label across all locales.
* test(app): cover project picker shortcut
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(commits): distinguish workspace history from base commits
Keep every workspace commit visible while bounding base history to ten
context commits, and make push and base state readable in the commit rail.
* fix(commits): preserve history when base refs disappear
Fall back to recent HEAD history for stale saved bases, and reject truncated
git output instead of presenting an incomplete commit list.
* fix(commits): classify history against the current base
Use the furthest-ahead local or remote base for classification, and re-infer
base history when saved worktree metadata points to a deleted branch.
Open the complete working comparison in a desktop workspace tab while preserving inline sidebar diffs when the tab is closed.
Working and commit comparisons share one diff panel, and workspace layout is the sole owner of live tab persistence and identity.
Refs #1520
Large project, status, and pinned groups now render 20 rows until expanded. Newly discovered workspaces are prepended without disturbing the saved order.
* feat(app): show recent commit history in explorer
* fix(app): keep recent commit history accurate
* fix(app): include merged commits in recent history
* perf(app): bound recent commit classification
* fix(app): refresh recent commits on Git updates
* fix(workspaces): start pasted pull requests from their branch
Recognized pull request links now select their branch directly instead of
waiting for a separate confirmation. Ensure single-branch clones also record
the fetched branch so the new worktree can track it.
* fix(workspaces): keep pasted PR checkout target-safe
* fix(workspaces): make pasted PR resolution deterministic
* fix(workspaces): preserve explicit checkout selection
A recognized pull request selects itself only when its attachment is added. Once the user chooses a branch, later composer edits must not derive checkout state from existing attachments.
* fix(composer): ignore stale pull request lookups
A lookup may finish after the workspace target changes. Only apply its attachment and selection event when the host, working directory, client, and remote still match the target that started it.
* fix(workspaces): make checkout choices authoritative
Arm automatic pull request selection only when a new PR link is detected. An explicit picker choice cancels that pending selection, existing draft attachments never infer checkout state, and target changes suppress carried-over lookups.
* fix(workspaces): stabilize pull request auto-selection
Keep the first PR selected when one edit contains several links, and accept a completed lookup when only the transport client changed for the same host and checkout.
* fix(composer): release removed pull request lookups
Release each auto-attach invocation when its composer state is abandoned, while continuing to discard any stale response.
Keep service route choices independent of daemon transport and persist the selected route per host. Use the shared web overlay stack so menus, tooltips, and toasts layer predictably.
Track proxy route changes from the Git branch rather than the workspace title to avoid transient service health changes.
* feat(files): edit workspace files on web
Keep source buffers synchronized with host file changes and require an explicit overwrite or reload when revisions diverge.
* feat(panels): surface and protect modified tabs
Expose tooltip and modification state through the generic panel boundary so tabs can show stable metadata and guard every close route consistently.
* fix(tests): use portable fake timeout handle
* fix(files): harden editor conflict handling
Preserve modified panel state across tab eviction, use precise revisions for optimistic writes, coalesce concurrent file watchers, and localize the editor interface.
* fix(files): close editor concurrency gaps
Coalesce clean reloads, preserve subscriber identities and file permissions, suspend pending saves during close confirmation, and carry precise revisions through file reads.
* test(files): expect read revision metadata
* Switch models from the Command Center
Add a model switcher to the Command-K Command Center. Typing surfaces a
flat, filterable list of "Model › Provider › Name" breadcrumb rows with
provider icons:
- Running agent: its own provider's models (a live agent can't change
provider); selecting calls setAgentModel.
- New draft tab: every available provider's models in one flat list;
selecting sets provider + model on the draft via a focused-draft
controller published to a global store (the draft form state is local
to the composer subtree and otherwise unreachable from the global
Command Center).
Models only appear once the user starts typing, so the default palette
view is unchanged. Reuses useProvidersSnapshot and the existing
setAgentModel RPC — no protocol changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Update packages/app/src/components/command-center.tsx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update packages/app/src/hooks/use-command-center.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* refactor(app): make Command Center extensible
Let focused features register stable actions while the palette owns search, selection, and a single virtualized result projection.
* fix(app): save model preference after agent switch
Persist the shared model choice only after the daemon confirms the live agent switched successfully.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(app): keep focused agent timelines live
Window focus was conflated with app visibility, so switching OS windows could remove the selected timeline subscription. Separate those signals, grace every visibility-driven removal, and cover retained history until authoritative catch-up completes.
* fix(app): keep timeline catch-up failures non-blocking
* fix(app): surface timeline catch-up failures
* fix(app): preserve file preview refocus
* fix(app): preserve optimistic catch-up flow
* refactor(forge): forge-neutral foundation (GitHub-only)
Decouple git-hosting from GitHub behind a neutral abstraction (issue #1616), GitHub-only for now; existing GitHub behaviour is unchanged.
- Forge manifest, neutral ForgeService contract, forge registry + resolver, and a client forge-module registry.
- GitHub code renamed to the neutral shape; PR/Issue attachment wording preserved.
- forge.search.response enums parse tolerantly (unknown kind/auth state degrade instead of breaking the client).
- createPullRequest reports typed CLI/auth errors instead of a generic message.
- forge-resolver host/remote caches are LRU-bounded.
- Forge host trust is explicit: only a known cloud host or a CLI-authenticated host is ever talked to; an unauthenticated GitHub Enterprise host fails resolution instead of routing to github.com.
- Docs: forge-providers guide, glossary and i18n forge-copy conventions, architecture and rpc-namespacing terminology.
- Vitest React Native mocks (unistyles, svg, linking, lucide) consolidated into shared aliased test-stubs.
* feat(forge): GitLab adapter, forge-aware UI, pipelines and approvals
GitLab adapter over the glab CLI on the neutral contracts: MR status, forge-aware UI, pipeline tree, and N-of-M approvals.
- threadIsResolved is part of the neutral timeline item.
- Pipeline load failures show an error instead of an empty section.
- Manual pipeline jobs render as pending.
- Fork/detached MR head pipelines are fetched by MR iid (glab ci get --merge-request).
* feat(forge): Gitea family adapter (Gitea, Forgejo, Codeberg)
One adapter over the tea CLI serving Gitea, Forgejo, and Codeberg on the neutral contracts.
- CI status aggregates commit statuses and Actions runs together.
- Gitea's terminal "warning" state maps to failure on server and client.
- Gitea Actions check details are reachable from the PR pane by workflowRunId.
* refactor(forge): localize compatibility handling
* test(forge): expect normalized GitLab facts
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(workspace): keep focus mode scoped and easy to exit
Route focus mode through the active workspace so persisted state cannot hide chrome on settings or other screens. Add a visible exit control and keep desktop window chrome aligned and visually quiet.
* fix(app): clarify muted chrome semantics
* feat(app): add fileView preference for changes panel
* feat(app): add buildDiffTree util for changed-files tree
* feat(app): add changed-files tree directory row
* feat(app): changed-files tree view mode in changes panel (#117)
* feat(protocol): add checkout.commits.list RPC + commitsList feature flag
* feat(server): list branch commits ahead of base with on-remote flags
* feat(server): handle checkout.commits.list RPC and advertise capability
* feat(client): checkout.commits.list method + useCommitsQuery hook
* feat(app): per-commit inline view with local-vs-remote markers (#117)
* feat(protocol,server): per-commit file diff RPC (checkout.commits.file_diff)
* feat(app): open per-commit file diff on click (#117)
* refactor: surface baseRef + commits loading/error, drop dead depth field
* fix(app): flip commit local/remote dot — local hollow, remote filled
* feat(app): list/tree view for expanded commit file list (#117)
* fix(app): syntax-highlight per-commit file diff to match Changes view
* feat(app): draggable resize between commits and diff sections
* refactor(app): dedupe wrap-text helpers into diff-highlighted-text
* fix(app): clean up commits resize drag on unmount + a11y label
* fix(app): collapse commits section by default
* fix(app): render per-commit file diff with the shared Changes line renderer
* perf(app): memoize shared diff line row; drop redundant DiffLineView wrapper
* refactor(app): extract shared DiffFileBody; render commit file diff through it
* fix(app): hide inline-comment affordance in per-commit diffs (no reviewActions)
* feat(app): move commits to a resizable bottom drawer in the Changes panel
* feat(review): commitSha scoping for per-commit review drafts + attachment
* feat(app): per-commit inline comments wired through to the composer (#117)
* refactor(app): own commit file-diff open state in CommitFileList (drop reset effect)
* refactor(app): colocate diff-render cluster under git/diff-file-body/
* feat(app): add diff tab target kind (working/commit diff tabs)
* feat(app): useDiffFiles hook unifying working + commit diff targets
* feat(app): diff tab panel rendering working/commit diffs
* feat(app): open a commit diff tab on commit click; drop per-commit drawer/file list
* feat(app): open/scroll working diff tab on changed-file click
* fix(app): keep commit diff tabs ephemeral; align working diff whitespace
* feat(app): collapsible file sections in the diff tab, collapsed by default
* feat(app): make the on-remote commit dot more subtle
Dims the filled green remote dot (row + legend) to ~0.55 opacity so the
local-only ring stays the state that draws the eye.
* Reshape commit diffs around the existing Changes view
* Fix diff tab migration complexity after rebase
* Address diff tab review findings
* Clean up diff tab review interfaces
* Collapse commit diffs into the existing view
* Load commits when expanded
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(app): restore missing workspaces from History
Workspace and agent archival are separate lifecycles. Carry restore intent from History so closed agents can reopen archived workspaces without changing ordinary navigation.
* fix(app): wait for workspace hydration before restore
* fix(app): preserve History agent unarchive
* fix(app): reopen archived History agents outside active directory
History entries are not merged into the active session maps. Carry the row archive state through explicit restore intent so live workspaces still reopen without broadening ordinary navigation.
* fix(app): ignore stale History archive state for active agents
A cached History row can outlive a successful reopen. When the workspace exists, live session state now wins so an active agent is not interrupted by another refresh.
* fix(app): reopen every selected archived History agent
History entries without workspace IDs returned before restoration, and a second archived agent sharing an in-flight workspace restore was dropped. Preserve both agent-only and deferred reopen intent.
* fix(app): require explicit workspace recovery
* fix: preserve workspace recovery contracts
* fix: preserve workspace recovery compatibility
* fix: preserve recovered workspace session state
* fix: preserve terminal navigation timing
* fix(server): preserve successful workspace recovery
* fix: preserve workspace recovery intent
* feat(providers): remove custom providers from settings
Add a destructive removal flow so mistaken custom providers can be deleted from config.json instead of only disabled.
* test(app): cover provider removal with e2e
Move provider removal coverage out of mocked component tests and into the real Settings flow.
* fix(providers): keep removal live after config updates
* fix(settings): show daemon update failures
React Native web ignores Alert.alert, so failed remote updates reset to an enabled button without any user-visible explanation. Keep updater progress and failure as explicit rendered state, including the daemon's error, and cover the real browser-to-daemon failure path.
* test(settings): harden daemon update regression
* fix(settings): disable Desktop-managed daemon updates
Desktop-bundled daemons advertised npm self-update even though the install-origin checks always rejected it. Report Desktop ownership to clients, render actionable guidance, and refuse stale requests before touching npm.
* feat(desktop): share persistent browser data across tabs
Keep browser sign-ins and site storage across tabs and app restarts, with a Settings action to clear the shared profile.
* fix(settings): prevent duplicate browser data clears
* fix(desktop): complete browser data cleanup
Clear legacy per-tab profiles after upgrades and localize the browser data settings in every supported language.
* fix(desktop): close browser profile cleanup races
Individual tool outcomes remain visible in the expanded list. The aggregate header no longer treats a handled tool result as a failure of the overall work.
* feat(subagents): archive finished children and show native names
Keep native provider sessions intact while letting users clear completed children from the track. Persist dismissed provider child IDs so history replay does not restore them.
* fix(subagents): cover persistence and naming edge cases
* fix(subagents): preserve active descendants and native names
* fix(subagents): retain dismissed timelines and trim paths
* fix(subagents): retain history across hydration
* test(subagents): expect normalized native descriptions
* fix(subagents): separate hidden rows from retained history
* fix(subagents): preserve restored hidden tabs safely
* fix(subagents): integrate archive with run state
* test(codex): expect normalized child name
* fix(subagents): retain dismissed tabs on reload
* fix(subagents): hide finished native agents locally
Keep the header action presentation-only so it does not mutate agent lifecycle or expand the runtime protocol.
* fix(subagents): humanize native agent names
* fix(subagents): preserve names in Codex history
* fix(subagents): retain hidden rows across reloads
* feat(app): refine tool call summaries and scrolling
Keep summary groups stable from the first tool call, preserve live activity feedback, and scroll expanded groups to the latest entry. Consolidate web scrollbar styling around the native browser mechanism.
* refactor(app): reuse tool call height constant
* fix(app): preserve tool call summary state
* fix(app): refresh web tool groups on expand
* fix(app): preserve grouped tool errors
* feat(ui): improve changelog and help navigation
Make release notes easier to scan and reach from the app. Also keep the homepage marquee within the layout viewport so it cannot create horizontal scrolling.
* fix(website): reject duplicate release anchors
* feat(app): add sidebar help and support menu
Give users a single place to run diagnostics, find shortcuts, and reach the support channels. Share one global diagnostic sheet between Settings and the sidebar so both entry points collect the same report.
* fix(app): keep cloned workspace navigation atomic
A newly merged clone flow still used the previous navigation signature and opened its draft tab separately. Route the cloned workspace and its draft target through the unified navigation command so attention selection cannot replace the intended tab.
* test(app): harden sidebar help coverage
Accept both the Discord invite URL and its canonical redirect, assert placement through real element geometry, and remove an unnecessary diagnostic-host callback.
* test(app): cover support redirect variants
Keep the help-menu E2E stable across prerelease versions and the valid redirect forms returned by Discord and GitHub.
* feat(app): pin projects to top of the sidebar
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): dedicated Pinned sidebar section with UX fixes
Replaces the invisible float-in-place pinning with a Codex-style Pinned
section at the top of the sidebar that hoists pinned chats and projects.
- Pin-aware keyboard shortcut numbering (badges match visual order)
- Pinned chats show their project name as subtitle (context after hoisting)
- Pinned entries ordered most-recently-pinned first
- Collapsible Pinned section (persisted)
- Double-click guard on workspace pin toggle
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): move Pinned section above the Workspaces header
Pinned now sits at the top of the scroll, above the "Workspaces" section
header (which moves into the scroll below it), matching the market-standard
sidebar layout. Chevron sits beside the "Pinned" label.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): reveal Pinned collapse chevron on hover only
Extracts PinnedSectionHeader with its own hover state; the chevron hides
until hover on web and stays visible on touch/compact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(app): pin the active chat via keyboard shortcut and inline pin button
Adds a ⌘⇧P / Ctrl+Shift+P shortcut that pins the selected chat, mirroring
the worktree.archive action across actions/dispatcher/route-shortcut, gated
by `selected && supportsWorkspacePinning`. Also surfaces an inline pin button
on row hover (always-on for touch) alongside the kebab in the shared trailing
overlay, swapping Pin/PinOff by pinned state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(server): unify project and workspace pin handlers
Collapses the two near-identical pin.set handlers into a shared
handlePinSetRequest that owns control flow, error handling and the
workspace-update fan-out; each caller only supplies its registry apply
step and typed response payload.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): drop empty pinned project shells and empty Workspaces header
Pinning every chat of a project hoisted them into Pinned but left an empty
project header (plus its new-workspace ghost row) below, duplicating the
project. Skip projects emptied by pinning while keeping genuinely empty
projects. Also hide the "Workspaces" section header when no unpinned projects
remain, so it no longer floats above nothing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(protocol): correct workspacePinning COMPAT version to v0.1.105
Feature ships in 0.1.105 per the changelog, not the placeholder v0.1.103.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): hide redundant chat pin control inside pinned projects
A pinned project already hoists all its chats to the top, so the per-chat pin
was a no-op that just toggled a dead icon. Suppress the inline pin button, the
kebab pin item, and the pin keyboard shortcut on chats whose project is pinned;
they still work for chats in unpinned projects.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): hide project pin control on empty projects
Project pin state is derived from workspace descriptors, so a project with no
workspaces would set pinnedAt on the server but emit no descriptor update — the
pin silently never surfaced and the user got no feedback. Offer the pin only
once the project has a chat, matching how chat pins are suppressed inside
already-pinned projects.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): drop collapsed Pinned rows from keyboard shortcut numbering
When the Pinned section is collapsed its chats and projects are hidden, but the
shortcut model kept counting them: visible unpinned rows showed off-by-N badge
numbers and pressing a number key jumped to a hidden pinned chat. Exclude the
pinned rows from numbering when Pinned is collapsed, matching what renders.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): hydrate pinned sidebar rows
* fix(app): keep filtered workspace controls reachable
* feat(app): scope sidebar pinning to chats, drop project pinning
Per PR review: pinning whole projects overlapped with project reordering
and mixed projects into the pinned list. Keep chat pinning only.
- Remove the project.pin.set RPC, its server handler, the projectPinnedAt
descriptor field, and the client setProjectPinned method.
- Drop the project pin control, isProjectPinned reads, and the suppressPin
plumbing (its only source — a pinned project — is gone).
- Fold pin-aware shortcut ordering into SidebarModelProvider (the live
shortcut owner) and delete the orphaned use-sidebar-shortcut-model hook,
so pinned chats and their shortcut numbers share one projection.
- Prune the now-unused project pin i18n keys across all locales.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(server): inline the pin handler into the workspace path
With project pinning gone, handlePinSetRequest had a single caller. Fold it
back into handleWorkspacePinSetRequest and drop the project/workspace
generalization.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert: keep main's use-sidebar-shortcut-model untouched
This hook (and its test) already live on main. The scope-down had deleted
them, but that's out of scope for this PR — the pin-aware shortcut ordering
lives in SidebarModelProvider now, so main's hook is left exactly as it was.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(protocol): align workspacePinning COMPAT version in websocket-server
Match the v0.1.105 version already corrected in messages.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(app): reshape pinned sidebar updates
* chore(app): remove stale pin button styles
* refactor(app): simplify pinned workspace rows
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(app): expose archive for every workspace
Workspace actions now archive one workspace by id. The daemon remains responsible for backing-specific cleanup, including removing a managed worktree only after its final workspace is archived.
* refactor(app): clarify archive descriptor name
* fix(app): preserve workspace archive safety
* fix(app): allow archive without upstream
* refactor(app): remove stale archive callback
* test(app): cover workspace archive ownership
* feat(app): restore compact Changes diff controls
Keep the folder tree behind a view toggle while preserving the flat file list as the default.
Move secondary diff options into the overflow menu so the Changes toolbar stays compact.
* fix(app): mirror whitespace diff option copy
* fix(app): polish diff controls review follow-up
Group changed files in the Changes view into a collapsible directory
tree instead of a flat list, on every form factor — consistent with the
Files explorer, which already renders a directory tree on mobile.
Folders are collapsible and expanded by default, single-child directory
chains are compressed into one row (packages/app/src), and folder rows
show aggregate +/- stats.
Client-only, built entirely from the existing ParsedDiffFile paths:
- diff-tree.ts: pure build/compress/flatten of the directory hierarchy,
keyed by full uncompressed dir path so collapse state survives
compression changes as the diff mutates.
- diff-flat-items.ts: pure buildDiffFlatItems + sumHeightsBefore. The
function retains a flat mode (showTree=false) as a tested seam for a
possible future flat/tree toggle; sticky header indices are
file-headers-only, derived from the post-collapse list.
- panel-store: diffCollapsedFoldersByWorkspace slice (collapsed-set, so
folders default to expanded), persisted via version 12. The set is
reconciled against the current diff so folders that leave and reappear
default to expanded and the stored set stays bounded.
Presentation primitives (chevron, indent guides, indent constant) are
extracted into components/tree-primitives and shared with the Files
explorer, which drops its private copies (and a banned useUnistyles
call site). The two trees now render identical rows; only the
tree-building stays separate (server listings vs. diff paths).
The toolbar's expand-all stays file-diffs-only (renamed
allFileDiffsExpanded); a separate control collapses/expands all folders.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(settings): consolidate daemon management into the host page
The app-level Daemon section and the localhost host page rendered the
same daemon card in two places. The host page is now the single home:
the Host sidebar group leads with Overview, and a desktop-only "Enable
built-in daemon" option in the host switcher recovers a removed
localhost connection. Old settings/daemon deep links redirect
(COMPAT-tagged).
* fix(settings): hide daemon recovery option when the server id probe fails
Review feedback: !query.isLoading treated an errored IPC probe as
resolved, so the recovery affordance could show without knowing the
local server id. Replace the resolved-boolean hook with a single
discriminated state hook (loading | error | resolved) — errors hide the
affordance and fall back to the settings root in the redirect. Also
collapses the two query subscriptions per caller into one.
* fix(settings): keep daemon recovery reachable when no hosts remain
Review feedback: removing localhost as the only host takes the settings
sidebar down its no-host branch, which never mounts the host picker —
so the "Enable built-in daemon" option was unreachable in the most
common recovery case. The sidebar now owns the single hook call, passes
it to the picker, and renders the same action as a sidebar row next to
"Add host" when no hosts exist.
* test(e2e): update settings host header to Overview
Test drift: the host section's sidebar/header label was intentionally
renamed to "Overview"; the spec still asserted the old "Host" title.
Also assert the retired app-level Daemon row stays absent.
Element annotation is now free-text: dropped the Fix/Change/Question/
Approve intent pills and the intent field, so the user just says what
they want. The card uses the shared Button and is titled "Annotate
element".
Toolbar: the element picker and DevTools are no longer dev-gated and
work in prod; DevTools uses a wrench icon, and the old clipboard "grab"
is now a Camera "Screenshot element" tool. Each toolbar button
highlights only its own mode, and the copy toast reflects whether a
screenshot image was actually captured.
* feat: add Schedules screen to manage scheduled agents
Adds a Schedules section in the sidebar header (below Sessions) for viewing
and managing new-agent schedules: recurring jobs that spawn an agent on a
cron or interval cadence.
- Borderless table showing provider icon, model, cadence, next run, and
status, with row actions to edit, pause/resume, run now, and delete.
- Create/edit sheet that reuses the agent provider/model/mode/working-
directory pickers, plus an interval/cron cadence editor with presets,
validation, and a UTC preview.
- Responsive: hover-revealed row actions on desktop, an always-visible
action menu on mobile.
- Data layer over the existing schedule RPCs (list/create/update/pause/
resume/delete/run-once) with optimistic pause, resume, and delete.
Scope is limited to new-agent schedules for now.
* Make the schedule form controls visually consistent
Merge the provider and model pickers into the single nested selector the
draft screen uses, render it as a full-width field, mute the placeholder,
proportion the cadence segmented controls, unify the cron preset chips,
and route the sheet scroll through the themed scrollbar.
* Address schedules review feedback
* Translate schedule hook client fallback errors
* Make schedules global and project scoped
* Stabilize project settings transport E2E
* Reset schedule model on cross-host project change
* Reject malformed cron step expressions
* Tighten schedules project query freshness
* refactor(schedules): clean up review feedback
* test(schedules): extract fake host setup
* refactor(schedules): share cron expression parsing
Use one structural cron parser for app previews and daemon cadence validation so the accepted grammar cannot drift between surfaces.
Remove the unused host-scoped schedules redirect while keeping the global schedules route.
* feat(browser): inspect, annotate, and grab page elements for the agent
Build a design-review flow on top of the in-app Electron browser so users
can send page elements to their coding agent with context.
- Annotate: pick an element, write a comment, choose an intent
(fix/change/question/approve); the element context + intent + comment go
to the agent as text, and a cropped screenshot rides along as an image.
- Grab: pick an element to copy its info + screenshot straight to the
system clipboard (no comment), with a toast on success/failure.
- Hover inspector: in select mode each element shows a floating label with
tag, id/class, React component name, and pixel size.
- Page markers: annotated elements on the current page get numbered badges
that track scroll/resize.
- Device sizes: a viewport-size menu (responsive + 13 common device presets)
renders the page centered in a fixed-size frame.
- Toolbar buttons now expose hover tooltips and no longer get squeezed out
on narrow panes; the element selector is available to all desktop users
(previously dev-only).
Screenshots and clipboard writes go through new Electron main-process IPCs
(capturePage + clipboard) so they work regardless of webview focus. Element
screenshots are referenced by id in the workspace attachment store and are
protected from the draft-store attachment GC. All new strings are
translated across the six supported locales.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(browser): write element clipboard exactly once
Resolve the image before writing so a combined text+image grab no longer
does a redundant text-only writeText() first (which flashed an intermediate
clipboard state). Addresses greptile review feedback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* i18n: add browser annotate/grab/devices strings for ja and pt-BR
main added Japanese and Brazilian Portuguese locales after this branch was
created; add the browser annotation, grab, and device-size keys to keep all
locales in sync with en. resources.test.ts parity passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>