Skip to content

Source Control (Git) Panel

The Source Control panel is a VSCode-style SCM sidebar mounted on the session detail page (/tools/agents/sessions/<id>). It lets you stage, commit, push, pull, view diffs, and browse history for any git repositories inside the session's working directory — without leaving the chat.

Source

Backend: internal/agents/scm/git.go (git shell-outs), scan.go (multi-repo discovery). Handler: internal/tools/agents/scm.go, scm_watch.go (SSE watcher). Frontend SPA: fe/agents/scm/ — Svelte 5, Monaco editor, mounted as an island into the session page. Endpoints: /tools/agents/api/sessions/{id}/git/*.

Prerequisite

git must be on PATH on the host running wick. The panel shells out to the real git binary — it does not bundle one. Existing SSH / PAT credentials and ~/.gitconfig settings apply as-is.

Opening the panel

The session detail page has a right-edge rail with three tabs: Context, Process, and Source. Click Source (or the tab label) to open the Source Control panel.

The panel opens as a slide-over overlay by default. To dock it, click the pin icon in the panel header. When pinned, the chat content area reflows to the left to make room — the panel does not overlap it.

StateBehaviour
UnpinnedOverlay on top of the chat; click outside or press Esc to close.
PinnedDocked alongside the chat; chat content pushes left.

Pin state and panel width are persisted in localStorage. The panel is resizable: drag the left handle to any width between 240 px and 640 px (default 260 px).

Layout (full mode)

In full mode the panel uses a two-column layout:

  • Left column (220 px) — file list grouped into Staged and Changes sections, commit message input, and branch bar at the bottom.
  • Right column (flex) — Monaco diff editor as the primary surface. The first changed file is selected automatically on load.

The active repo selection is persisted per session in localStorage and restored on next open.

Multi-repo support

On open, the panel recursively scans the session cwd for git repositories. The scan skips heavy directories (node_modules, vendor, dist, .cache, etc.) and caps at a fixed depth. If more than one repository is found, a dropdown appears in the left column header. Select a repo from the dropdown to switch; all sections apply to the active repo.

File list

The left column shows two flat sections:

SectionWhat it contains
Staged (N)Files added to the index (git add).
Changes (N)Modified tracked files and untracked new files.

Click a file row to open it in the diff editor. The active file is highlighted.

Each file row shows a status badge (M, A, D, ?) on the right edge.

Diff editor

Clicking a file opens it inline in the right-column Monaco diff editor — no modal. The diff is git-correct:

File stateWhat is diffed
StagedHEAD ↔ index (staged content)
Unstaged (tracked)Index ↔ working tree
Untracked (new file)Empty ↔ working tree

Unchanged regions are collapsed by default with a "N hidden lines" expand bar (3-line context, same as VSCode). Click the bar to expand.

The diff renders in unified (inline) mode by default. Click the split-view icon in the diff header to toggle side-by-side mode.

Editing files

The diff editor is directly editable — no "Edit" button required. Start typing in the modified (right) side and a Save button appears automatically in the diff header. Click Save to write the file to disk. Click Revert edit to abandon unsaved changes without touching the file.

Per-file actions (diff header)

ButtonAction
StageStage the current file (git add).
UnstageUnstage the current file (git restore --staged).
DiscardDiscard working-tree changes (see warning below).
SaveWrite in-editor edits to disk (visible only when there are unsaved edits).
Revert editAbandon in-editor edits without touching the file (visible only when there are unsaved edits).
Split-view iconToggle unified ↔ side-by-side diff layout.

Discard is destructive

Discard cannot be undone. For tracked files it runs git restore <file>; for untracked files it runs git clean -f <file>. A confirmation dialog appears before the operation runs.

On mobile or when the panel is opened as a slide-over overlay, a compact sidebar mode is used instead. The file list, commit box, and branch bar stack vertically. Clicking a file opens a full-screen diff modal (Monaco diff editor, same editing and save behavior as full mode).

Commit

The commit message input is at the bottom of the left column. Type a message and press Enter (or click Commit (N)) to commit all staged files. The button shows the staged file count and is disabled when staging is empty or the message is blank.

Branches

The branch bar at the bottom of the left column shows the current branch name and the ahead/behind count (↑N ↓N). Click the branch name to open a dropdown with:

  • a filter input to search local and remote branches
  • local branches (current highlighted)
  • remote branches
ActionHow
Checkout localClick a local branch name.
Checkout remoteClick a remote branch — creates a local tracking branch automatically.
Create + checkoutType a new name in the bottom input and click +.

Pull and Push buttons are below the branch picker.

History

The History tab inside the panel shows a commit log for the active repository. Each row displays:

ColumnContent
SHAShort commit hash (7 chars).
SubjectFirst line of the commit message.
AuthorCommit author name.
DateRelative date (e.g. "2 hours ago").

Click a commit row to expand it and see the list of files changed in that commit. Click a file in the expanded list to open the Monaco diff viewer for that file (parent commit ↔ this commit).

Live updates

The panel subscribes to the server-sent event stream for the session. A server-side filesystem watcher monitors the session cwd and pushes a git_status event over SSE whenever the working tree changes. The Changes section and the Source rail tab badge both update in real time — no polling, no manual refresh required.

The rail tab badge shows the current count of changed files (staged + unstaged) across all discovered repositories.

Endpoint reference

All endpoints are scoped to /tools/agents/api/sessions/{id}/git/ and require the same RequireToolAccess middleware + session-ownership check as the rest of the agents tool.

MethodPathPurpose
GET.../git/statusFull git status snapshot for all discovered repos.
GET.../git/diffFile diff (query: path, staged, commit).
POST.../git/stageStage files. Body: {paths: [...]}.
POST.../git/unstageUnstage files.
POST.../git/discardDiscard working-tree changes (destructive).
POST.../git/commitCommit staged files. Body: {message}.
GET.../git/branchesList local + remote branches.
POST.../git/checkoutCheckout or create a branch.
POST.../git/pullPull from upstream.
POST.../git/pushPush to upstream.
GET.../git/logCommit history.
GET.../git/log/diffDiff for a specific commit.

See also

Built with ❤️ by a developer, for developers.