santree 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,623 +11,45 @@
11
11
  <p align="center">
12
12
  <a href="https://www.npmjs.com/package/santree"><img src="https://img.shields.io/npm/v/santree.svg" alt="npm version"></a>
13
13
  <a href="https://www.npmjs.com/package/santree"><img src="https://img.shields.io/npm/dm/santree.svg" alt="npm downloads"></a>
14
- <a href="https://github.com/stoscanini/santree/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/santree.svg" alt="license"></a>
14
+ <a href="https://github.com/santiagotoscanini/santree/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/santree.svg" alt="license"></a>
15
15
  </p>
16
16
 
17
17
  <p align="center">
18
- Create, switch, and manage Git worktrees with ease.<br/>
19
- Integrates with GitHub PRs and pluggable issue trackers (Linear, GitHub Issues) via Claude AI.
18
+ Pick an issue, work on it with Claude in an isolated worktree, ship a PR without leaving your terminal.<br/>
19
+ Pluggable issue trackers (Linear, GitHub Issues), pluggable multiplexers (tmux, cmux), and AI in the loop.
20
20
  </p>
21
21
 
22
- ---
23
-
24
- ## Installation
25
-
26
- ```bash
27
- npm install -g santree
28
- ```
29
-
30
- ### Shell Setup (Required)
31
-
32
- Add to your `.zshrc` or `.bashrc`:
33
-
34
- ```bash
35
- eval "$(santree helpers shell-init zsh)" # for zsh
36
- eval "$(santree helpers shell-init bash)" # for bash
37
- ```
38
-
39
- This enables automatic directory switching after `worktree create` and `worktree switch` commands.
40
-
41
- The shell integration also provides:
42
-
43
- - `st` - Alias for `santree`
44
- - `stw` - Alias for `santree worktree` (e.g., `stw list`, `stw create`)
45
- - `stn` - Quick create worktree with `--work --plan --tmux` (prompts for branch name)
46
-
47
- ### Verify Setup
48
-
49
- ```bash
50
- santree doctor
51
- ```
52
-
53
- This checks that all required tools are installed and configured correctly.
54
-
55
- ---
56
-
57
- ## Quick Start
58
-
59
- ```bash
60
- # Open the interactive dashboard — manage everything from one screen
61
- santree dashboard
62
-
63
- # Or use individual commands:
64
-
65
- # Create a new worktree and switch to it
66
- santree worktree create feature/TEAM-123-my-feature
67
-
68
- # List all worktrees with PR status
69
- santree worktree list
70
-
71
- # Launch Claude AI to work on the current ticket
72
- santree worktree work
73
-
74
- # Switch to another worktree
75
- santree worktree switch TEAM-456
76
-
77
- # Create a PR
78
- santree pr create
79
-
80
- # Clean up worktrees with merged PRs
81
- santree worktree clean
82
- ```
83
-
84
- With the `stw` alias: `stw create`, `stw list`, `stw switch`, `stw work`, `stw clean`.
85
-
86
- ---
87
-
88
- ## Commands
89
-
90
- ### Worktree (`santree worktree`)
91
-
92
- | Command | Description |
93
- | ---------------------------------- | --------------------------------------------------- |
94
- | `santree worktree create <branch>` | Create a new worktree from base branch |
95
- | `santree worktree list` | List all worktrees with PR status and commits ahead |
96
- | `santree worktree switch <branch>` | Switch to another worktree |
97
- | `santree worktree remove <branch>` | Remove a worktree and its branch |
98
- | `santree worktree clean` | Remove worktrees with merged/closed PRs |
99
- | `santree worktree sync` | Sync current worktree with base branch |
100
- | `santree worktree work` | Launch Claude AI to work on the current ticket |
101
- | `santree worktree open` | Open workspace in VSCode or Cursor |
102
- | `santree worktree setup` | Run the init script (`.santree/init.sh`) |
103
- | `santree worktree commit` | Stage and commit changes |
104
- | `santree worktree diff` | View branch-only diff (uses merge-base, like a GitHub PR) |
105
-
106
- ### Pull Requests (`santree pr`)
107
-
108
- | Command | Description |
109
- | ------------------- | ------------------------------------- |
110
- | `santree pr create` | Create a GitHub pull request |
111
- | `santree pr open` | Open the current PR in the browser |
112
- | `santree pr fix` | Fix PR review comments with AI |
113
- | `santree pr review` | Review changes against ticket with AI |
114
-
115
- ### Issue trackers
116
-
117
- Santree supports Linear and GitHub Issues behind a single interface. Each repo picks one. Use the generic `santree issue` commands for tracker-agnostic actions; use `santree linear` / `santree github` for backend-specific auth.
118
-
119
- #### Generic — `santree issue`
120
-
121
- | Command | Description |
122
- | -------------------------------------- | ------------------------------------------------------------ |
123
- | `santree issue switch <linear\|github>` | Pick the active tracker for this repo |
124
- | `santree issue open` | Open the current branch's issue in the browser |
125
-
126
- #### Linear (`santree linear`)
127
-
128
- | Command | Description |
129
- | ----------------------- | --------------------------------------------- |
130
- | `santree linear auth` | Authenticate with Linear (OAuth) |
131
- | `santree linear switch` | Switch Linear workspace for this repo |
132
-
133
- #### GitHub Issues (`santree github`)
134
-
135
- | Command | Description |
136
- | --------------------- | ---------------------------------------------------------------------- |
137
- | `santree github auth` | Verify `gh auth status`, run `gh auth login` if needed, set tracker=github |
138
-
139
- ### Helpers (`santree helpers`)
140
-
141
- | Command | Description |
142
- | ---------------------------------------------- | ------------------------------------------------ |
143
- | `santree helpers shell-init` | Output shell integration script |
144
- | `santree helpers statusline` | Custom statusline for Claude Code |
145
- | `santree helpers session-signal notification` | Signal waiting state (Notification hook) |
146
- | `santree helpers session-signal stop` | Signal idle state (Stop hook) |
147
- | `santree helpers session-signal prompt` | Signal active state (UserPromptSubmit hook) |
148
- | `santree helpers session-signal end` | Signal exited state (SessionEnd hook) |
149
- | `santree helpers session-signal install` | Auto-install session-signal hooks in Claude Code |
150
- | `santree helpers session-signal install --dry` | Print the hooks JSON without writing |
151
-
152
- ### Top-level
153
-
154
- | Command | Description |
155
- | ------------------- | ----------------------------------------------- |
156
- | `santree dashboard` | Interactive dashboard of all your assigned issues |
157
- | `santree doctor` | Check system requirements and integrations |
158
-
159
- ---
160
-
161
- ## Features
162
-
163
- ### Workflow
164
-
165
- End-to-end flow, from picking an issue to merging the PR. Everything runs inside the terminal multiplexer; the dashboard orchestrates and the AI agent does the heavy lifting at two distinct stages.
166
-
167
- ```mermaid
168
- flowchart TB
169
- subgraph Term["Terminal · tmux / cmux"]
170
- direction TB
171
- Dashboard(["① Dashboard<br/>pick issue · add context"]):::action
172
- Editor["Editor<br/><sub>zed · cursor · vscode · nvim · jetbrains</sub>"]:::tool
173
- AI(("② / ⑤ AI agent<br/>Claude Code")):::agent
174
- Pager["③ Diff pager<br/><sub>delta · diff-so-fancy · built-in</sub>"]:::tool
175
- Decision{good?}:::decision
176
- Ship(["④ git commit + PR"]):::action
177
- GitHub[("GitHub<br/>PRs · CI")]:::external
178
-
179
- Dashboard ==>|implement| AI
180
- Dashboard -. write context .-> Editor
181
- Editor -. context · hand-edit .-> AI
182
- AI ==> Pager
183
- Pager ==> Decision
184
- Decision -->|hand-edit| Editor
185
- Decision -->|resume| AI
186
- Decision ==>|ship| Ship
187
- Ship ==> GitHub
188
- GitHub -->|reviews · CI| AI
189
- AI -->|push fix| Ship
190
- end
191
-
192
- classDef action fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a
193
- classDef tool fill:#f3f4f6,stroke:#9ca3af,color:#374151
194
- classDef agent fill:#ede9fe,stroke:#8b5cf6,stroke-width:2px,color:#4c1d95
195
- classDef external fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#78350f
196
- classDef decision fill:#fee2e2,stroke:#ef4444,stroke-width:2px,color:#7f1d1d
197
- ```
198
-
199
- Bold edges (`==>`) follow the happy path; thin edges (`-->`) are branches/loops; dotted edges (`-.->`) are optional editor side-trips. Colors group nodes by role: blue actions, purple agent, gray tools, yellow external service, red decision.
200
-
201
- **Stages, in order:**
202
-
203
- 1. **Pick + add context** — open the dashboard, browse assigned issues, pick one. The inline context box lets you add custom instructions to the prompt; `Ctrl+O` drops into your editor for longer prose.
204
- 2. **AI agent (Claude)** — runs in the worktree's mux window with the rendered ticket + your context. Implements or plans.
205
- 3. **Diff pager (review)** — `[v]` opens the diff overlay. From here you iterate: hand-edit in your editor, or resume the Claude session to keep going.
206
- 4. **Ship** — once the diff looks right, `[C]` for inline commit + push, then `[c]` to create the PR (`--fill` runs Claude non-interactively against the PR template).
207
- 5. **PR feedback loop** — `[f]` (fix) and `[r]` (self-review) re-launch the AI with PR comments + CI failures as input. Patches are pushed; CI re-runs; loop until merged.
208
-
209
- **Supported tools per stage:**
210
-
211
- | Stage | Supported today | Planned |
212
- |---|---|---|
213
- | **Issue tracker** (① Dashboard) | Linear, GitHub Issues | Jira, Shortcut, etc. |
214
- | **AI agent** (②, ⑤) | Claude Code (`claude` CLI) | OpenAI Codex, OpenCode, Cursor agent |
215
- | **Diff pager** (③) | delta, diff-so-fancy, any unified-diff pager — built-in colorizer when none set | — |
216
- | **Editor** (side branch) | Anything taking a path: zed, nvim, jetbrains, cursor, vscode. cursor + vscode also handle `.code-workspace` files via `[E]` workspace shortcut | — |
217
- | **Forge** (④) | GitHub via `gh` CLI | GitLab, Bitbucket, Gitea |
218
- | **Terminal multiplexer** (outer frame) | tmux, cmux *(macOS-only — see [#1472](https://github.com/manaflow-ai/cmux/issues/1472))*, none | zellij |
219
-
220
- The AI agent and issue tracker are already behind interfaces (`lib/trackers/`, plus the `claude` CLI shim in `lib/ai.ts`). Adding a new option in either category means writing one new module and wiring it into the factory — no changes to UI/dashboard code.
221
-
222
- ### Interactive Dashboard
223
-
224
- `santree dashboard` opens a full-screen TUI to manage all your work in one place. It shows your assigned issues from the active tracker (Linear or GitHub Issues) grouped by project, with live status for worktrees, PRs, CI checks, and reviews.
225
-
226
- **Left pane** — issue list. Issues without worktrees show as a single row (priority + ID + title). Issues with worktrees expand into nested sub-rows below the title showing `· diff` (files/adds/deletes/commits-ahead), `· pr` (number, state, CI, review count), and `· session` (state + Claude session ID). Click any row (main or sub) to select; scroll wheel navigates; drag the divider to resize panes.
227
-
228
- **Right pane** — issue detail with description, file-level git status (staged/unstaged/untracked), PR info, checks, reviews, and context-aware keyboard actions.
229
-
230
- **Keyboard actions:**
231
- | Key | Action |
232
- |-----|--------|
233
- | `w` | Create worktree & start working (plan or implement) |
234
- | `↵` | Resume session / switch to worktree |
235
- | `e` | Open worktree in editor |
236
- | `C` | Inline commit & push flow |
237
- | `c` | Create PR (fill from commits or open in browser) |
238
- | `v` | Inline diff overlay — file tree + diff content (mouse + keyboard) |
239
- | `f` / `r` | Fix PR / Review PR (launches in tmux) |
240
- | `o` / `p` | Open issue (in Linear or GitHub) / PR in browser |
241
- | `d` | Remove worktree |
242
-
243
- Commit, PR creation, and diff review happen inline without leaving the dashboard. Work, fix, and review open in new tmux windows.
244
-
245
- **Diff overlay** (`v`) — branch-only diff vs the base branch's merge-base (matches GitHub PR semantics — upstream changes you haven't pulled don't leak in). Left pane shows changed files in a tree, right pane shows the diff. `j/k` navigate files, `J/K` (or shift+arrows) scroll the diff, `g/G` jump to top/bottom, `q`/`esc` close. Mouse: click a file to select, scroll wheel over the file pane changes selection, scroll wheel over the diff scrolls content. Set `SANTREE_DIFF_TOOL` to pipe through delta, diff-so-fancy, or any pager that takes a unified diff.
246
-
247
- ### Worktree Management
248
-
249
- Create isolated worktrees for each feature branch. No more stashing or committing WIP code just to switch tasks.
250
-
251
- ### GitHub Integration
252
-
253
- See PR status directly in your worktree list. Clean up worktrees automatically when PRs are merged or closed.
254
-
255
- ### Issue Tracker Integration
256
-
257
- Santree fetches issue data (title, description, comments, images) from the active tracker — Linear or GitHub Issues — and injects it into prompts when running `santree worktree work`. See [Issue Tracker Integration](#issue-tracker-integration-1) for setup.
258
-
259
- ### Claude AI Integration
260
-
261
- Launch Claude with full context about your current ticket. Supports different modes:
262
-
263
- - `santree worktree work` - Implement the ticket
264
- - `santree worktree work --plan` - Create an implementation plan only
265
- - `santree pr review` - Review changes against ticket requirements
266
- - `santree pr fix` - Address PR review comments
267
-
268
- ### Init Scripts
269
-
270
- Run custom setup scripts when creating worktrees. Perfect for copying `.env` files, installing dependencies, or any project-specific setup.
271
-
272
- ---
273
-
274
- ## Configuration
275
-
276
- ### Init Script
277
-
278
- Create `.santree/init.sh` in your repository root:
279
-
280
- ```bash
281
- #!/bin/bash
282
- cp "$SANTREE_REPO_ROOT/.env" "$SANTREE_WORKTREE_PATH/.env"
283
- npm install
284
- ```
285
-
286
- ### Branch Naming
287
-
288
- Branch names need to encode the issue ID so santree can link the worktree back to the right ticket. The accepted format depends on the active tracker:
289
-
290
- ```
291
- # Linear (uppercased letter prefix + dash + digits)
292
- user/TEAM-123-feature-description
293
- feature/PROJ-456-add-auth
294
-
295
- # GitHub Issues (explicit prefix required, to avoid `fix-typo-1` matching)
296
- feature/issue-42-add-auth
297
- gh-42-add-auth
298
- 42-add-auth
299
- ```
300
-
301
- GitHub's parser is strict on purpose: a commit-style branch like `fix-typo-1` will *not* match (no explicit prefix, no slash-led number).
302
-
303
- ### Issue Tracker Integration
304
-
305
- Each repo picks one tracker. The active tracker is resolved in this order: `SANTREE_TRACKER` env var → per-repo `_tracker.kind` (in `.santree/metadata.json`) → legacy `_linear.org` (treated as Linear) → auto-detect (any Linear creds → Linear, else GitHub).
306
-
307
- #### Choosing a tracker
308
-
309
- ```bash
310
- # Explicitly pick the active tracker for this repo
311
- santree issue switch linear
312
- santree issue switch github
313
-
314
- # Or let an auth command set it as a side effect:
315
- santree linear auth # Sets _tracker.kind = "linear" after OAuth
316
- santree github auth # Sets _tracker.kind = "github" after `gh auth login`
317
- ```
318
-
319
- For a one-off override (testing, scripting):
320
-
321
- ```bash
322
- SANTREE_TRACKER=github santree dashboard
323
- ```
324
-
325
- #### Linear
326
-
327
- Santree fetches Linear ticket data via the GraphQL API (OAuth PKCE).
328
-
329
- ```bash
330
- # Authenticate with Linear (opens browser for OAuth)
331
- santree linear auth
332
-
333
- # Check auth status
334
- santree linear auth --status
335
-
336
- # Verify a ticket is fetched correctly
337
- santree linear auth --test TEAM-123
338
-
339
- # Log out
340
- santree linear auth --logout
341
-
342
- # Switch between authenticated workspaces
343
- santree linear switch
344
- ```
345
-
346
- On first run, `santree linear auth` opens your browser to authorize the app with your Linear workspace. Tokens are stored in `$XDG_CONFIG_HOME/santree/auth.json` (defaults to `~/.config/santree/auth.json`) and auto-refresh transparently.
347
-
348
- If you have multiple workspaces authenticated, running `santree linear auth` in a new repo will let you pick which one to link. Images from tickets are downloaded to a temp directory and cleaned up after Claude exits.
349
-
350
- #### GitHub Issues
351
-
352
- Santree uses the existing `gh` CLI — no separate OAuth flow.
353
-
354
- ```bash
355
- # Verify gh is authenticated; flips this repo's tracker to GitHub
356
- santree github auth
357
- ```
358
-
359
- Issues are listed via `gh search issues --assignee=@me --state=open --repo <owner>/<name>` (current repo only — cross-repo issues aren't surfaced today). Priority is derived from labels matching `P0`/`P1`/`P2`/`P3`/`urgent`/`critical`/`high`/`medium`/`low`, falling back to `No priority`. Attached images on `user-images.githubusercontent.com` and `github.com/.../assets/` are downloaded so Claude can read them when filling PR templates.
360
-
361
- ### Claude Code Statusline (Optional)
362
-
363
- Santree provides a custom statusline for Claude Code showing git info, model, context usage, and cost.
364
-
365
- Add to `~/.claude/settings.json`:
366
-
367
- ```json
368
- {
369
- "statusLine": {
370
- "type": "command",
371
- "command": "santree helpers statusline"
372
- }
373
- }
374
- ```
375
-
376
- The statusline displays: `repo | branch | S: staged | U: unstaged | A: untracked | Model | Context% | $Cost`
377
-
378
- ### Claude Code Remote Control (Optional)
379
-
380
- Enable [Remote Control](https://code.claude.com/docs/en/remote-control) to continue local Claude Code sessions from your phone, tablet, or any browser. This lets you kick off work with `santree worktree work` and monitor or steer the session remotely.
381
-
382
- Enable it for all sessions by running `/config` inside Claude Code and setting **Enable Remote Control for all sessions** to `true`. This writes `remoteControlAtStartup: true` to `~/.claude.json`. Run `santree doctor` to verify.
383
-
384
- ### Session State Signaling (Optional)
385
-
386
- Surfaces the current Claude Code session state in the dashboard, statusline, and tmux window names. Shows whether a session is actively working, waiting for permission approval, idle, or exited.
387
-
388
- **States:**
389
- | State | Meaning |
390
- |-------|---------|
391
- | `active` | User submitted a prompt, Claude is working |
392
- | `waiting` | Claude needs permission approval |
393
- | `idle` | Claude finished and is waiting for next prompt |
394
- | `exited` | Session ended |
395
-
396
- **Install:**
397
-
398
- ```bash
399
- santree helpers session-signal install
400
- ```
401
-
402
- This adds hooks for `Notification`, `Stop`, `UserPromptSubmit`, and `SessionEnd` to `~/.claude/settings.json`. Existing hooks are preserved.
403
-
404
- To preview the JSON without writing: `santree helpers session-signal install --dry`
405
-
406
- Verify with `santree doctor` — look for the "Session Signal Hooks" row under Claude Code.
407
-
408
- ### English Tutor (Optional)
409
-
410
- When enabled, Claude Code spots grammar mistakes in your prompts and replies with a one-line correction (`original -> correction (reason)`) before doing the actual work. Mistake-free prompts are ignored. Useful if English isn't your first language and you'd like ambient feedback while you code.
411
-
412
- **Install:**
413
-
414
- ```bash
415
- santree helpers english-tutor install
416
- ```
417
-
418
- This adds two hooks (`UserPromptSubmit`, `SessionStart`) and a scoped `Edit` permission for the practice log to `~/.claude/settings.json`. Existing hooks and permissions are preserved.
419
-
420
- To preview the JSON without writing: `santree helpers english-tutor install --dry`
421
-
422
- Corrections are appended to `~/.config/santree/english-practice-log.md` (or `$XDG_CONFIG_HOME/santree/english-practice-log.md`). The `SessionStart` hook replays this log into context on new sessions so Claude can spot recurring patterns.
423
-
424
- Remove with `santree helpers english-tutor uninstall`.
425
-
426
- ### Environment Variables
427
-
428
- | Variable | Effect |
429
- |---|---|
430
- | `SANTREE_TRACKER` | Override the active issue tracker for a single invocation: `linear` or `github`. Takes precedence over the per-repo `_tracker.kind`. If unset, falls back to repo config → legacy `_linear.org` → auto-detect. |
431
- | `SANTREE_EDITOR` | Editor used by `e` (open in editor) actions in the dashboard. Defaults to `code`. Examples: `cursor`, `zed`, `code`, `nvim`. |
432
- | `SANTREE_DIFF_TOOL` | Pager used by `worktree diff` (CLI) and the dashboard diff overlay. Passed to git as `-c core.pager=<tool>` for the CLI, and used to pipe content for the overlay. Examples: `delta`, `diff-so-fancy`. Must accept a unified diff on stdin. Names are restricted to `[A-Za-z0-9_\-/.+]`. |
433
- | `SANTREE_THEME` | Dashboard color theme: `light`, `dark`, or `auto` (default). In `auto` mode, santree queries the terminal's background via OSC 11 and re-detects on each refresh cycle (≤30s) so theme switches propagate automatically. Set explicitly when your terminal doesn't respond to OSC 11. |
434
-
435
- Santree always launches Claude with `--permission-mode auto` (Claude Code's auto mode), or `plan` when invoked in plan mode. Worktree-scoped automation is the default — there is no opt-in flag.
436
-
437
- ---
438
-
439
- ## Command Options
440
-
441
- ### worktree create
442
-
443
- | Option | Description |
444
- | ----------------- | ------------------------------------------------- |
445
- | `--base <branch>` | Base branch to create from (default: main/master) |
446
- | `--work` | Launch Claude after creating |
447
- | `--plan` | With --work, only create implementation plan |
448
- | `--no-pull` | Skip pulling latest changes |
449
- | `--tmux` | Open worktree in new tmux window |
450
- | `--name <name>` | Custom tmux window name |
451
-
452
- ### worktree sync
453
-
454
- | Option | Description |
455
- | ---------- | --------------------------- |
456
- | `--rebase` | Use rebase instead of merge |
457
-
458
- ### worktree remove
459
-
460
- Removes the worktree and deletes the branch. Uses force mode by default (removes even with uncommitted changes).
461
-
462
- ### worktree clean
463
-
464
- Shows worktrees with merged/closed PRs and prompts for confirmation before removing.
465
-
466
- ### worktree open
467
-
468
- | Option | Description |
469
- | ---------------- | --------------------------------------------------------------------------------------- |
470
- | `--editor <cmd>` | Editor command to use (default: `code`). Also configurable via `SANTREE_EDITOR` env var |
471
-
472
- ### worktree diff
473
-
474
- Shows a branch-only unified diff against the base branch's merge-base — same scope as a GitHub PR diff (upstream commits you haven't pulled don't leak in). Includes both committed and uncommitted work by default. Honors `SANTREE_DIFF_TOOL` (e.g. `delta`).
475
-
476
- | Option | Description |
477
- | ------------ | ------------------------------------------------- |
478
- | `--commits` | Show only committed changes (`merge-base..HEAD`) |
479
- | `--staged` | Show only staged changes |
480
- | `--unstaged` | Show only unstaged (working tree vs index) |
481
- | `--base <branch>` | Override the base branch |
482
-
483
- ### worktree work
484
-
485
- | Option | Description |
486
- | -------- | ------------------------------- |
487
- | `--plan` | Only create implementation plan |
488
-
489
- Automatically fetches issue data from the active tracker (Linear or GitHub Issues) if authenticated. Degrades gracefully if not.
490
-
491
- ### pr create
492
-
493
- | Option | Description |
494
- | -------- | --------------------------------------------- |
495
- | `--fill` | Use AI to fill the PR template before opening |
496
-
497
- Automatically pushes, detects existing PRs, and uses the first commit message as the title. If a closed PR exists for the branch, prompts before creating a new one.
498
-
499
- ### linear auth
500
-
501
- | Option | Description |
502
- | ------------- | ------------------------------------------------ |
503
- | `--status` | Show current auth status (org, token expiry) |
504
- | `--test <id>` | Fetch a ticket by ID to verify integration works |
505
- | `--logout` | Revoke tokens and log out |
506
-
507
- ---
508
-
509
- ## Requirements
510
-
511
- | Tool | Purpose |
512
- | ------------------------------------ | ------------------------------------ |
513
- | Node.js >= 20 | Runtime |
514
- | Git | Worktree operations |
515
- | GitHub CLI (`gh`) | PR integration |
516
- | Claude Code (`claude`) | AI agent for `work`, `fix`, `review` |
517
- | tmux | Optional: new window support |
518
- | VSCode (`code`) or Cursor (`cursor`) | Optional: workspace editor |
519
- | delta (`git-delta`) | Optional: syntax-highlighted diffs (used by `worktree diff` and the dashboard `v` overlay when `SANTREE_DIFF_TOOL=delta` is set) |
520
-
521
- ---
522
-
523
- ## Provider Support
524
-
525
- Santree currently locks in to specific providers for some integrations and is interchangeable for others. The list below is a snapshot of what's supported today — contributions welcome.
526
-
527
- ### Single-provider (no swap-out today)
528
-
529
- | Area | Supported | Not yet supported |
530
- | --- | --- | --- |
531
- | **Source control / forge** | GitHub (via `gh` CLI) | GitLab, Bitbucket, Gitea, Codeberg, self-hosted Forgejo |
532
- | **Coding agent** | Claude Code (`claude` CLI) | OpenAI Codex, OpenCode, Cursor agent, Aider, others |
533
-
534
- These are hardwired in `lib/github.ts` and `lib/ai.ts` respectively. Adding a second provider means abstracting an interface (similar to `lib/trackers/` or `lib/multiplexer/`) and wiring a selection mechanism.
535
-
536
- ### Multi-provider (already interchangeable)
537
-
538
- | Area | How to switch | Examples |
539
- | --- | --- | --- |
540
- | **Issue tracker** | `santree issue switch <linear\|github>` (or `SANTREE_TRACKER` env var, or as a side effect of `santree linear auth` / `santree github auth`) | Linear (OAuth + GraphQL), GitHub Issues (via `gh` CLI). Adding a third tracker = one new directory under `lib/trackers/`. |
541
- | **Editor** | `SANTREE_EDITOR` env var (or `--editor` flag on `worktree open`) | `code`, `cursor`, `zed`, `nvim`, `subl`, `webstorm` — any executable that takes a path argument |
542
- | **Terminal multiplexer** | Auto-detected via `$TMUX` / `$CMUX_SURFACE_ID`. No config needed — each adapter's `isActive()` declares its own runtime check. | `tmux` (all platforms), `cmux` (macOS only — see [#1472](https://github.com/manaflow-ai/cmux/issues/1472)). Zellij is planned but not implemented. |
543
- | **Diff renderer** | `SANTREE_DIFF_TOOL` env var | `delta`, `diff-so-fancy`, or any pager that accepts a unified diff. Falls back to plain `git diff` colorization when unset. |
544
- | **Color theme** | `SANTREE_THEME` env var (`light`/`dark`/`auto`) | Auto-detects via OSC 11; manual override available. Re-detects every refresh so theme switches show up within 30s. |
545
- | **Shell integration** | `santree helpers shell-init` detects the shell | `zsh`, `bash` (templates in `shell/init.{zsh,bash}.njk`) |
22
+ <p align="center">
23
+ <strong>📚 <a href="https://santiagotoscanini.github.io/santree/">Read the docs</a></strong>
24
+ </p>
546
25
 
547
26
  ---
548
27
 
549
- ## Development
28
+ <!-- TODO screenshot: dashboard hero shot — Issues tab with a few projects, at least one issue expanded into · diff / · pr / · session sub-rows, right pane showing the description + git status + actions footer. Wide aspect ratio (terminal at ~140 cols). -->
550
29
 
551
- ### Setup
30
+ ## Install
552
31
 
553
32
  ```bash
554
- git clone https://github.com/santiagotoscanini/santree.git
555
- cd santree
556
- npm install
557
- ```
558
-
559
- ### Build & Run
560
-
561
- ```bash
562
- # Compile TypeScript
563
- npm run build
564
-
565
- # Run the local build
566
- node dist/cli.js <command>
567
-
568
- # Watch mode (recompiles on save)
569
- npm run dev
33
+ npm install -g santree
34
+ eval "$(santree helpers shell-init zsh)" # or bash
35
+ santree doctor
570
36
  ```
571
37
 
572
- During development, use `node dist/cli.js` instead of `santree` to run the local version:
38
+ Full setup: [Installation](https://santiagotoscanini.github.io/santree/installation.html).
573
39
 
574
- ```bash
575
- node dist/cli.js worktree list
576
- node dist/cli.js worktree work
577
- node dist/cli.js linear auth --test TEAM-123
578
- ```
40
+ <!-- TODO screenshot: diff overlay (`v` key) with delta enabled — file tree on the left, syntax-highlighted diff on the right. Pick a colorful change (TS file with type changes works well). -->
579
41
 
580
- ### Link globally (optional)
42
+ <!-- TODO screenshot (optional): a tmux window split — dashboard on one pane, a Claude `worktree work` session running in another. Shows the "AI in the loop" story visually. -->
581
43
 
582
- To use `santree` as a global command pointing to your local build:
44
+ ## Where to next
583
45
 
584
- ```bash
585
- npm link
586
- ```
46
+ - **[Quickstart](https://santiagotoscanini.github.io/santree/quickstart.html)** — 5-minute end-to-end walkthrough
47
+ - **[Concepts](https://santiagotoscanini.github.io/santree/concepts.html)** — the mental model (worktrees, trackers, multiplexers, AI)
48
+ - **[Dashboard](https://santiagotoscanini.github.io/santree/dashboard.html)** — the TUI tour
49
+ - **[Commands](https://santiagotoscanini.github.io/santree/commands.html)** — full CLI reference
587
50
 
588
- Now `santree` runs your local `dist/cli.js`. Unlink with `npm unlink -g santree`.
51
+ Everything else configuration, integrations, contributing is in the **[docs site](https://santiagotoscanini.github.io/santree/)**.
589
52
 
590
- ### Code Quality
53
+ ## License
591
54
 
592
- ```bash
593
- npm run lint # Check for lint + formatting errors
594
- npm run lint:fix # Auto-fix lint + formatting errors
595
- npm run format # Format all source files with Prettier
596
- ```
597
-
598
- A pre-commit hook (via husky + lint-staged) automatically runs ESLint and Prettier on staged files.
599
-
600
- ### Project Structure
601
-
602
- ```
603
- source/
604
- ├── cli.tsx # Entry point (Pastel app runner)
605
- ├── lib/
606
- │ ├── ai.ts # Shared AI logic (context, prompt, launch)
607
- │ ├── git.ts # Git helpers (worktrees, branches); extractTicketId is a tracker shim
608
- │ ├── github.ts # GitHub CLI wrapper (PR info, auth, push, checks, reviews)
609
- │ ├── exec.ts # Shell command helpers
610
- │ ├── metadata.ts # .santree/metadata.json r/w (extracted to break import cycles)
611
- │ ├── prompts.ts # Nunjucks template renderer
612
- │ ├── trackers/ # Issue tracker abstraction (Linear, GitHub Issues)
613
- │ │ ├── types.ts # IssueTracker interface + generic Issue/AssignedIssue types
614
- │ │ ├── index.ts # getIssueTracker(repoRoot) factory
615
- │ │ ├── linear/ # OAuth PKCE + GraphQL + image rewriter
616
- │ │ └── github/ # `gh` CLI wrappers; priority derived from labels
617
- │ ├── multiplexer/ # tmux/cmux/none abstraction (windows/sessions)
618
- │ └── dashboard/ # Dashboard UI components
619
- │ ├── types.ts # State types, action types, phase enums
620
- │ ├── IssueList.tsx # Left pane — issue list with priority, session, PR, CI columns
621
- │ └── DetailPanel.tsx # Right pane — issue detail, git status, context-aware actions
622
- └── commands/ # One React (Ink) component per CLI command
623
- ├── doctor.tsx # Top-level: system check
624
- ├── dashboard.tsx # Top-level: interactive dashboard
625
- ├── worktree/ # Worktree management (create, list, switch, etc.)
626
- ├── pr/ # PR lifecycle (create, open, fix, review)
627
- ├── linear/ # Linear-specific OAuth (auth, switch)
628
- ├── github/ # GitHub-specific auth (gh wrapper)
629
- ├── issue/ # Tracker-agnostic actions (switch, open)
630
- └── helpers/ # Shell init, statusline
631
- prompts/ # Nunjucks templates: implement, plan, review, fix-pr, fill-pr, ticket
632
- shell/ # Shell integration templates: init.zsh.njk, init.bash.njk
633
- ```
55
+ MIT
@@ -137,6 +137,45 @@ function runPipedDiff(cwd, gitArgs, tool, themeMode) {
137
137
  });
138
138
  });
139
139
  }
140
+ /**
141
+ * Pipe an in-memory unified diff string through the configured pager. Used by
142
+ * the reviews-tab PR diff path, which already has per-file content from
143
+ * `gh pr diff` parsed into a map — no `git diff` to spawn here.
144
+ */
145
+ function runPagerOnString(input, tool, themeMode) {
146
+ return new Promise((resolve, reject) => {
147
+ const pagerArgs = tool === "delta" ? [themeMode === "light" ? "--light" : "--dark"] : [];
148
+ const pagerEnv = tool === "delta"
149
+ ? {
150
+ ...process.env,
151
+ GIT_CONFIG_PARAMETERS: "'delta.hyperlinks=false' 'delta.line-numbers=false'",
152
+ }
153
+ : process.env;
154
+ const pager = spawn(tool, pagerArgs, {
155
+ stdio: ["pipe", "pipe", "pipe"],
156
+ env: pagerEnv,
157
+ });
158
+ let out = "";
159
+ let err = "";
160
+ pager.stdout.on("data", (d) => {
161
+ out += d.toString();
162
+ });
163
+ pager.stderr.on("data", (d) => {
164
+ err += d.toString();
165
+ });
166
+ pager.on("error", reject);
167
+ pager.on("close", (code) => {
168
+ if (code !== 0 && !out) {
169
+ reject(new Error(err || `${tool} exited with code ${code}`));
170
+ }
171
+ else {
172
+ resolve(splitCombinedSgr(out));
173
+ }
174
+ });
175
+ pager.stdin.write(input);
176
+ pager.stdin.end();
177
+ });
178
+ }
140
179
  function parseNameStatus(raw) {
141
180
  const files = [];
142
181
  for (const line of raw.split("\n")) {
@@ -739,7 +778,9 @@ export default function Dashboard() {
739
778
  }, [state.overlay, state.diffWorktreePath, state.diffBaseBranch, state.diffRefreshTick]);
740
779
  // ── Diff overlay: select content for current file (gh pr diff path) ─
741
780
  // Per-file slices were parsed up front by the file-list effect above —
742
- // just pull the right entry out of the map. No subprocess per file.
781
+ // just pull the right entry out of the map. When SANTREE_DIFF_TOOL is set,
782
+ // pipe the slice through the pager so reviews-tab diffs get the same
783
+ // syntax highlighting as worktree diffs.
743
784
  useEffect(() => {
744
785
  if (state.overlay !== "diff" || state.diffPRNumber == null)
745
786
  return;
@@ -748,14 +789,30 @@ export default function Dashboard() {
748
789
  dispatch({ type: "DIFF_CONTENT_LOADED", content: "" });
749
790
  return;
750
791
  }
751
- const content = state.diffPRContentByPath[file.path] ?? "";
752
- dispatch({ type: "DIFF_CONTENT_LOADED", content });
792
+ const raw = state.diffPRContentByPath[file.path] ?? "";
793
+ const tool = getDiffTool();
794
+ if (!tool || !raw) {
795
+ dispatch({ type: "DIFF_CONTENT_LOADED", content: raw });
796
+ return;
797
+ }
798
+ dispatch({ type: "DIFF_CONTENT_LOADING" });
799
+ void (async () => {
800
+ try {
801
+ const content = await runPagerOnString(raw, tool, theme.mode);
802
+ dispatch({ type: "DIFF_CONTENT_LOADED", content });
803
+ }
804
+ catch (err) {
805
+ const msg = err instanceof Error ? err.message : String(err);
806
+ dispatch({ type: "DIFF_CONTENT_LOADED", content: `Error rendering diff: ${msg}` });
807
+ }
808
+ })();
753
809
  }, [
754
810
  state.overlay,
755
811
  state.diffPRNumber,
756
812
  state.diffFileIndex,
757
813
  state.diffFiles,
758
814
  state.diffPRContentByPath,
815
+ theme.mode,
759
816
  ]);
760
817
  // ── Diff overlay: load content for selected file ──────────────────
761
818
  // If SANTREE_DIFF_TOOL is set, pipe `git diff` output through the tool so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "santree",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Git worktree manager",
5
5
  "license": "MIT",
6
6
  "author": "Santiago Toscanini",