VS Code: a Bench sidebar view listing each specialist on this project and its changed files
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 0
- Avg merge
- 1h 21m
- Merged PRs (30d)
- 41
Description
Builds on #122 (edit events) and #123 (the extension). Covers the developer's asks 2 and 3 together, because they are one piece of UI: VS Code has no numbered badge on a status-bar item — a real badge belongs to a view container, and the file tree *is* that container.
## Decided by the developer
Intake, Bench turn `b2a64f4e/4`:
- **Tree lists** every specialist on this project, each with its changed files under it. *(my default, not reviewed — treat as an assumption)*
- **Badge counts** specialists waiting on you in this window's projects. *(my default, not reviewed)*
- **Diffs are taken** against where the specialist's branch started. *(my default, not reviewed)*
- **WSL Remote**, so workspace folders are `/var/www/bench` and paths match what the daemon sends. Confirmed by the developer — no path translation needed.
The three marked unreviewed came back as my own defaults. If one turns out to be load-bearing, it deserves a second look rather than being treated as instruction.
## The evidence this is mostly wiring
**The daemon already computes changed files and throws the names away.** `worktree.ts:134`, `inspectWorktree()`:
```ts
const { stdout } = await exec("git", ["status", "--porcelain"], { cwd: worktree });
changes = stdout.split("\n")
.filter((line) => line.trim() !== "")
.filter((line) => !isBootstrapLeftover(line))
.length;
```
It already filters Bench's own leavings — the symlinked `node_modules`, a regenerated lockfile, `.bench/`, `.claude/` (`BOOTSTRAP_LEFTOVERS`, `worktree.ts:96`). That filter is not optional: without it every worktree looks entirely rewritten, because `node_modules` is a symlink into the developer's checkout.
**The badge predicate is already written**, in `src/client/waiting.ts`:
```ts
export function isWaiting(row: RosterRow): boolean {
return row.status === "awaiting_decision"
&& row.latestReportSeq !== null
&& row.latestReportSeq !== row.answeredReportSeq;
}
```
The comment there is explicit that `awaiting_decision` alone is not enough — a specialist that answered a question and wrote no report has that status too. Copy this predicate; do not re-derive it.
**VS Code will not find the worktrees itself.** Its git extension scans one level deep by default and a worktree is four (`/.claude/worktrees//`). The built-in Source Control view is not already doing this.
**The extension already receives the roster.** `follow.ts` connects to `/events` and currently discards `{ type: "roster" }` frames. The badge needs no new endpoint — just to stop ignoring them.
## What to build
**Daemon** — the changed files, not merely a count. Either widen `inspectWorktree` to return the paths it already parses, or add a sibling that does; do not run `git status` twice. A route or an event carrying, per specialist: path, status letter, and whether it is committed-since-branch-point or uncommitted.
For "against where its branch started", the base is `git merge-base `. `inspectWorktree` already enumerates other refs for `unmergedCommits` (`worktree.ts:158`) — the same enumeration is the input to finding a fork point.
**Extension** — a view container with a tree: specialist → changed files. Clicking a file opens a diff (`vscode.diff` between the base blob and the working file, or a `TextDocumentContentProvider` for the base side). Badge on the view = count of `isWaiting` rows whose project is one this window has open.
Keep the vscode-free split that #123 established: the predicate, the grouping and the path filtering go in modules with no `vscode` import so they are testable in Bench's own suite. Only the tree provider and the view registration import `vscode`.
## Acceptance criteria
- [ ] A view container appears in the activity bar, with a tree of specialists on this window's project(s).
- [ ] Each specialist node expands to its changed files; a specialist with no changes shows as such rather than as an empty expandable node.
- [ ] Clicking a file opens a git-style diff against the branch's starting point.
- [ ] Bench's own leavings never appear: `node_modules`, `.bench/`, `.claude/`, a regenerated lockfile. Reuse `isBootstrapLeftover`, do not reimplement it.
- [ ] The badge shows the number of specialists waiting on the developer, counting only projects this window has open; no badge when that is zero.
- [ ] The badge uses the same rule as `isWaiting` — a specialist that merely ended a turn does not count.
- [ ] The tree updates as specialists work, without the developer refreshing it.
- [ ] Specialists on projects this window does not have open appear nowhere — not in the tree, not in the badge.
- [ ] `pnpm typecheck` and `pnpm typecheck:editor` both clean.
## Out of scope
- The cockpit-side "connect to a project" button. Separate issue.
- Staging, committing, discarding, or any write from the tree. It is a view.
- Devin specialists — still no structured paths from them (see #122).
- Changing what the cockpit shows.
## Verification
```
pnpm typecheck
pnpm typecheck:editor
pnpm test
```
Manual and required: open `/var/www/bench` in VS Code, have a specialist edit and commit something, and confirm the file appears in the tree and its diff opens. A green build proves none of this.
Contributor guide
Research direction
Start with worktree.ts:134-158 to understand the existing status filtering and ref enumeration, then follow.ts and src/client/waiting.ts for roster updates and the waiting predicate. Keep grouping and path filtering in the vscode-free modules established by #123, and run pnpm typecheck, pnpm typecheck:editor, and pnpm test. Done means the activity-bar tree, filtered specialist/file nodes, base diffs, live updates, and scoped badge meet the acceptance checks, including manual verification in /var/www/bench.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript, vscode
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 56/100