tabby-agentdeck 1.1.3 → 1.2.0

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.ko.md +26 -574
  3. package/README.md +25 -545
  4. package/dist/agents.d.ts +15 -0
  5. package/dist/codexHooks.d.ts +23 -0
  6. package/dist/codexMeta.d.ts +87 -0
  7. package/dist/config.d.ts +120 -2
  8. package/dist/cursorVisibility.d.ts +33 -1
  9. package/dist/deck.service.d.ts +182 -19
  10. package/dist/devReload.service.d.ts +48 -0
  11. package/dist/devReload.stub.d.ts +19 -0
  12. package/dist/dock.d.ts +15 -0
  13. package/dist/i18n/af.d.ts +83 -0
  14. package/dist/i18n/bg.d.ts +83 -0
  15. package/dist/i18n/cs.d.ts +83 -0
  16. package/dist/i18n/da.d.ts +83 -0
  17. package/dist/i18n/de.d.ts +83 -0
  18. package/dist/i18n/en.d.ts +83 -0
  19. package/dist/i18n/es.d.ts +83 -0
  20. package/dist/i18n/fr.d.ts +83 -0
  21. package/dist/i18n/hr.d.ts +83 -0
  22. package/dist/i18n/id.d.ts +83 -0
  23. package/dist/i18n/it.d.ts +83 -0
  24. package/dist/i18n/ja.d.ts +83 -0
  25. package/dist/i18n/ko.d.ts +83 -0
  26. package/dist/i18n/pl.d.ts +83 -0
  27. package/dist/i18n/pt.d.ts +83 -0
  28. package/dist/i18n/ru.d.ts +83 -0
  29. package/dist/i18n/sr.d.ts +83 -0
  30. package/dist/i18n/sv.d.ts +83 -0
  31. package/dist/i18n/tr.d.ts +83 -0
  32. package/dist/i18n/uk.d.ts +83 -0
  33. package/dist/i18n/zh-CN.d.ts +83 -0
  34. package/dist/i18n/zh-TW.d.ts +83 -0
  35. package/dist/i18n.d.ts +3618 -0
  36. package/dist/index.d.ts +3 -1
  37. package/dist/index.js +1 -2
  38. package/dist/keybind.d.ts +121 -0
  39. package/dist/meta.d.ts +83 -0
  40. package/dist/nav.d.ts +29 -0
  41. package/dist/notify.service.d.ts +149 -1
  42. package/dist/project-root.d.ts +1 -1
  43. package/dist/reload.service.d.ts +57 -0
  44. package/dist/sessionLedger.d.ts +11 -6
  45. package/dist/settings.component.d.ts +133 -8
  46. package/dist/status.service.d.ts +30 -2
  47. package/dist/statusLine.d.ts +27 -0
  48. package/dist/update.d.ts +19 -5
  49. package/dist/update.service.d.ts +29 -20
  50. package/dist/viewPanel.d.ts +13 -3
  51. package/hooks/agentdeck-codex-notify.ps1 +5 -2
  52. package/hooks/agentdeck-label.js +1 -1
  53. package/hooks/agentdeck-notify.ps1 +32 -12
  54. package/hooks/agentdeck-statusline.mjs +351 -0
  55. package/package.json +4 -2
  56. package/updater/agentdeck-update.ps1 +50 -27
  57. package/dist/index.js.map +0 -1
package/README.md CHANGED
@@ -5,558 +5,38 @@
5
5
  [![CI](https://github.com/jghwang2/tabby-agentdeck/actions/workflows/ci.yml/badge.svg)](https://github.com/jghwang2/tabby-agentdeck/actions/workflows/ci.yml)
6
6
  [![license](https://img.shields.io/npm/l/tabby-agentdeck)](LICENSE)
7
7
 
8
- **[Website](https://jghwang2.github.io/tabby-agentdeck/) · [User guide](https://jghwang2.github.io/tabby-agentdeck/guide/) · [한국어 README](README.ko.md) · [한국어 사용 설명서](https://jghwang2.github.io/tabby-agentdeck/guide/ko.html)**
8
+ **[Website](https://jghwang2.github.io/tabby-agentdeck/) · [User guide](https://jghwang2.github.io/tabby-agentdeck/guide/) · [Full reference](docs/REFERENCE.md) · [한국어](README.ko.md)**
9
9
 
10
- A [Tabby](https://tabby.sh) terminal plugin for **people who keep Claude Code, Codex and Gemini CLI open in several tabs at once**.
10
+ A [Tabby](https://tabby.sh) plugin for people who keep **Claude Code, Codex and Gemini CLI open in several tabs at once**.
11
+ A 4:3 terminal, a session deck with live status, and a preview panel for what the agents produce.
11
12
 
12
- It pins the terminal viewport to **4:3** and spends the leftover space on a **session deck with live status**.
13
- You see which agent is running and which one is waiting for you without clicking through tabs.
14
- Documents and images the agents produce open in a **preview panel** beside the terminal.
15
- On top of that it fixes the things agent CLIs get wrong inside a terminal — image paste, newline keys, pty width drift.
16
-
17
- ```
18
- ┌────────────────────┬──────────────────┬──────────────────┐
19
- │ │ note.md + ⟳ ✕ │ AGENT DECK 4 │
20
- │ terminal (4:3) ├──────────────────┼──────────────────┤
21
- │ │ note.md shot.png│ ● running refactor│
22
- │ Tabby's own tab ├──────────────────┤ ⏸ waiting rebuild │
23
- │ bar is hidden │ # Title │ ✔ done tests │
24
- │ │ body is rendered │ ○ idle │
25
- │ │ [image] ├──────────────────┤
26
- │ │ 742 B · just now │ + new tab │ config│
27
- └────────────────────┴──────────────────┴──────────────────┘
28
- terminal preview panel session deck
29
- ```
30
-
31
- Built and verified on Windows. The status-notification hook is a PowerShell script and is therefore Windows-only;
32
- everything else is platform-agnostic but has not been exercised elsewhere.
33
- If you run Tabby on macOS or Linux, an [issue](https://github.com/jghwang2/tabby-agentdeck/issues) saying what broke is genuinely useful.
34
-
35
- ## Features
36
-
37
- | | What it does |
38
- |---|---|
39
- | **4:3 width** | Derives terminal width from window height and fixes it. Leftover width goes to the sidebar; whatever is still left is absorbed by the terminal |
40
- | **Sidebar tab management** | Click to select, `×` to close, double-click to rename the task, right-click to set status by hand |
41
- | **Search · status filter** | The search box narrows by title, task name and working folder; status chips keep only one state. `Esc` clears |
42
- | **Reordering** | Drag a row to move it (Tabby's own tab order follows). Hold at the edge and the list **scrolls by itself**, so off-screen positions are one gesture away. Blocked with an explanation while status sorting is on |
43
- | **Keyboard control** | `Ctrl+Shift+L` enters the list; `↑↓` walks it and Enter picks. **The tab does not change while you walk** — focus and selection are separate so passing output does not flood the screen. `Ctrl+W` closes the current tab (the focused row when the list has the keyboard) |
44
- | **Output preview** | Markdown, images, tables (csv/tsv) and code the agent touched, in a panel beside the terminal. It picks up paths printed on screen, so Claude Code, Codex and Gemini all work |
45
- | **Drag and drop** | Drop a file on the panel and it opens. **Drop it on the terminal** and you are asked: open in panel / paste the path |
46
- | **Diff view** | The `Changes` tab of the same panel shows `git diff` for the working tree — file list, `+N -M`, unified diff with line numbers. It holds regardless of who made the change |
47
- | **`file:line` reference** | Click a line in a diff or code view and that position is typed into the terminal (Shift+click for a range). "Fix this line" without typing it out |
48
- | **Stage · commit** | Click a file tag to `git add`/unstage, write a message and commit. **It takes two presses** (the first one asks to confirm) |
49
- | **Session groups** | Tabs group by project. The boundary is found by walking up for a repository root (`.git` and friends), so there is nothing to configure. With a single group no header is drawn and order is left alone |
50
- | **Resume a past session** | `⟲ Past sessions` in a group lists **closed Claude Code sessions by the prompt you typed**. Click one and the conversation comes back with `claude --resume` in its own working folder |
51
- | **IME composition guard** | Waits for composition to settle before writing to the pty, so a half-composed syllable no longer follows the cursor or drops onto a new line |
52
- | **Agent profiles** | Works out which CLI is running in a tab (process → title) and applies that agent's rules. Supporting a new CLI is one profile block |
53
- | **Mouse docking** | Drag the header to any edge (left/right/top/bottom); drag the divider to resize |
54
- | **Status** | running / waiting / rate limited / done / error / idle, as colour, badge and elapsed time. It judges from screen text even with no hook, and a badge says **why** it stopped (what needs approval, which limit) |
55
- | **Per-status counts** | The header shows counts in urgency order — `⏸ 2 ● 3 ○ 1`. Turn on `sortByStatus` and tabs sort the same way |
56
- | **Subagent count** | How many background agents that session is running, as `❖3`. Counted by **matching launches against completions** in the transcript, so it is right even when no hook reports |
57
- | **Task names** | The moment you press Enter, the text in the input box becomes the row's label |
58
- | **Claude Code hook** | Install once from the settings window and the agent reports status directly (optional) |
59
- | **Root profile** | Always open new tabs in a chosen working root |
60
- | **Auto-update** | On startup it asks npm for a newer version, and with your consent closes Tabby, installs, and **starts itself back up**. Development installs (source links) are left alone |
61
- | **Input fixes** | `Ctrl+V` image paste, `Ctrl+Enter`/`Shift+Enter` newline, right-click copy/paste |
62
- | **Screen repair** | Clears **only the input-box remnant** of a broken TUI and re-syncs the size — the conversation you were reading is untouched. Sidebar `↻`, tab right-click, or the `agentdeck-repair` hotkey |
63
-
64
- ## Install
65
-
66
- **Inside Tabby**: Settings → Plugins → search `agentdeck` → Install → restart Tabby.
67
-
68
- **From source**:
69
-
70
- ```powershell
71
- npm install
72
- npm run build
73
- powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1
74
- ```
75
-
76
- `install.ps1` puts a junction in Tabby's plugin directory (`%APPDATA%\tabby\plugins\node_modules\tabby-agentdeck`)
77
- pointing at this folder. Edit the source, run `npm run build`, restart Tabby.
78
-
79
- ### Auto-update
80
-
81
- Eight seconds after startup it asks the npm registry for the latest `tabby-agentdeck`.
82
- If there is a newer one it **asks** — `Update now` / `Later` / `Skip this version`.
83
- Say yes and it goes:
84
-
85
- ```
86
- Tabby closes → (outside) npm install → Tabby starts itself back up
87
- ```
88
-
89
- **The only human step is that first click.** It comes back on its own when the install finishes,
90
- and it comes back even if the install fails — losing your terminals to an update must never happen.
91
- The record is in `%LOCALAPPDATA%\tabby-agentdeck\update.log`.
92
-
93
- | Setting | Default | |
94
- |---|---|---|
95
- | `autoUpdate` | `true` | Off means it does not even check |
96
- | `updateCheckIntervalHours` | `6` | So it does not ask on every startup |
97
- | `updateRegistry` | `https://registry.npmjs.org` | Change for an internal mirror |
98
- | `updateSkipVersion` | — | The version you pressed `Skip` on. **Only that one** is skipped; the next release asks again |
99
-
100
- > **Why close Tabby to install.** Two reasons. ① A running plugin cannot swap itself out — Tabby reads
101
- > `dist/index.js` once at startup, so changing the disk leaves the running code as it was. ② Windows holds
102
- > loaded files open, so an npm install against that folder while Tabby is up can fail half-deleted — a
103
- > broken plugin is the worst outcome. So the install is done by `updater/agentdeck-update.ps1`, copied out
104
- > to a temp folder, **after it confirms Tabby has exited**. It waits on the main process PID of the instance
105
- > that started the update — counting by name would let one other Tabby window block updates forever.
106
-
107
- > **Development installs are left alone.** When `tabby-agentdeck` in the plugin folder is a link to a source
108
- > tree (an `install.ps1` setup), running `npm install` there would **overwrite the source you are working on**.
109
- > If `src/`, `.git` or `webpack.config.js` is visible, or the entry is a link, it only reports that a new
110
- > version exists (the check is `canSelfUpdate` in `update.ts`).
111
-
112
- ## Task names and status
113
-
114
- A sidebar row is a **task name (label)** and a **status badge**.
115
- Both work **with the plugin alone, no configuration**. Install the Claude Code hook to make them exact.
116
-
117
- ### Task names
118
-
119
- The moment you press Enter, it reads the input box drawn on screen (`❯ ...`, `> ...`) and takes 40 characters as the label.
120
- Text that arrived by paste, `↑` history or IME is caught as drawn.
121
- Shift+Enter / Ctrl+Enter are newlines and do not change the label; if no input-box head is found (a plain shell, a y/n prompt) the label is left as it was.
122
-
123
- ### Status
124
-
125
- - **Auto-detection (default)**: PTY output flowing means `running`; silence for `idleAfterMs` (2.5s default) means `idle`.
126
- Patterns like `Do you want to`, `❯ 1. Yes` and `(y/n)` mean `waiting`. The spinner Claude Code puts in the title also reads as running.
127
- If a `waiting` tab shows working text again (`esc to interrupt`), it is taken as approved and lifted back to `running` —
128
- this applies to hook-pinned tabs too, because the first hook after an approval only arrives when the tool finishes, so approving a long build used to leave the row stuck on waiting.
129
- - **Claude Code hook (recommended)**: Settings → AgentDeck → "status notification hook". It wires a notify script into
130
- UserPromptSubmit / Notification / PostToolUse / PostToolUseFailure / PermissionDenied / Stop / StopFailure in `~/.claude/settings.json`.
131
- Other hooks are untouched and a backup is written first. The event set grew in 0.3.0, so older installs read as "not installed" — press install again.
132
- The agent then states directly that it was given an instruction, is waiting for approval, ran a tool (= was approved), finished, or failed — so the reading never wobbles. "Remove" in the settings window reverts it.
133
- Waiting rows carry the reason: `⏸ waiting · Bash permission`. The reason disappears when the status changes.
134
- - **Rate limited**: when Claude Code hits a session or weekly usage limit the turn is cut short, `StopFailure` reports `error: rate_limit`,
135
- and the sidebar shows the reset time — `⛔ rate limited · resets 12pm`. There is nothing to approve, so it is kept distinct from waiting.
136
- Other failures (overloaded, server_error …) show as `✖ error · overloaded`. When the limit lifts and the session continues, it returns to running.
137
- Hook reports are tied to a tab by the `AGENTDECK_TAB` environment variable the plugin plants in the tab's shell
138
- (tabs opened before 0.2.2, and tabs restored on restart, fall back to the process ancestry).
139
-
140
- ### Working out which agent is running
141
-
142
- For each tab it works out **which CLI is running** (child process name, falling back to the tab title) and applies that agent's rules.
143
- It knows Claude Code, Codex CLI and Gemini CLI; a new CLI is one more profile block.
144
-
145
- A profile decides: status phrases (idle/working), the title spinner, the **image-paste key**
146
- (Claude = `ESC v`, Codex = `0x16`), and the input-box head character.
147
- A tab with only a shell in it is `unknown`, and there every known rule is combined.
148
-
149
- > **Only observed values are used narrowly.** Codex and Gemini screen phrases have not been observed in this
150
- > repository, so those two agents use the union (= the old rules) for status. Attaching a profile must not make
151
- > detection worse than it was. When real measurements exist, turn on `patternsProven` for that profile.
152
-
153
- ### Setting it by hand
154
-
155
- - In the sidebar, **right-click → pick a status**, **double-click → rename**.
156
- - From inside a shell script, set the console title to `[AD:<status>] <label>`. The prefix is stripped from the display.
157
-
158
- ```powershell
159
- $host.UI.RawUI.WindowTitle = "[AD:waiting] waiting for deploy approval"
160
- ```
161
-
162
- `status` is one of `running` / `waiting` / `done` / `error` / `idle`.
163
- A status set this way is **pinned** and auto-detection will not overwrite it (the badge gets a dotted underline).
164
- Right-click → `↺ back to auto` releases it.
165
- This route only works when run directly in the shell — console titles from agent hooks never reach the terminal.
166
-
167
- ### How many subagents are running
168
-
169
- When Claude Code launches background agents, the count appears on the row as `❖3`.
170
- Hover the row for the list and how long the oldest has been going.
171
-
172
- It counts by **matching launches against completions in that session's transcript**
173
- (`~/.claude/projects/*/<session id>.jsonl`). The evidence is the transcript, not what a hook says,
174
- so the count stays right while no hook is reporting. The session-to-tab link is the hook's status file, so
175
- **without the hook installed this indicator does not appear** either (the same holds if `notifyChannel` is off).
176
-
177
- Transcripts grow to tens of megabytes, so it reads **only the growth** — it checks the size every two seconds
178
- and reads the tail only when it grew. A session being picked up for the first time has to be scanned once,
179
- so its count can appear a few seconds late.
180
-
181
- One thing to know: if you cancel an agent with Esc, or the CLI dies, **no completion is recorded** and the
182
- count may not come down. That is not hidden with a timeout (that would erase genuinely long-running ones).
183
- The elapsed time in the tooltip keeps climbing instead, which is how you spot it.
184
-
185
- ### Alerts
186
-
187
- When the window is behind another app (unfocused) and a tab moves to `waiting`, `error` or `rate limited`, the OS is told —
188
- the taskbar icon flashes (`alertFlash`) and a badge with the count of such tabs is drawn on it (`alertBadge`).
189
- The badge follows the real count and disappears at zero. Flashing stops when you bring the window forward.
190
- OS toasts (`alertToast`) are off by default — turn them on and clicking one jumps straight to that tab.
191
- When the window is in front, all three do nothing: the sidebar is already visible.
192
-
193
- ## Resuming past sessions
194
-
195
- **Claude Code conversations survive on disk** even when Tabby is closed (`~/.claude/projects/<project>/<session id>.jsonl`).
196
- `claude --resume <session id>` picks one back up — but what actually blocks you is not the command,
197
- it is **finding your work in a list of UUIDs**.
198
-
199
- agentdeck already holds the answer: the prompt text it picked out of the input box when you pressed Enter.
200
- So there is a drawer at the end of each group:
201
-
202
- ```
203
- ▾ Root ⏸1 ●2
204
- ● Agent Root running ❖2
205
- rework season pass payout path 3m
206
- ⟲ Past sessions 5 ▸ ← collapsed by default
207
- ```
208
-
209
- Expanded:
210
-
211
- ```
212
- ⟲ Past sessions 5 ▾
213
- what is that thing running in the background
214
- done 14:12
215
- move unclaimed season pass rewards to mail
216
- rate limited yesterday
217
- ```
218
-
219
- | Action | What it does |
220
- |---|---|
221
- | Click | Opens a new tab **in that session's working folder** and sends `claude --resume <session id>` |
222
- | Right-click → `resume as a branch` | `--fork-session`. Leaves the original transcript at that point and continues in a **copy** — for trying a different direction from the same spot. Used every time, it just multiplies transcripts |
223
- | Right-click → `copy session id` · `hide from list` | Hides it from the list only. The transcript file is untouched |
224
-
225
- Worth knowing:
226
-
227
- - **A different working folder means it is not in the list.** `--resume` keys sessions by cwd, so resuming opens there too.
228
- - **Already-open sessions** show as `● open` and clicking goes to that tab instead of resuming —
229
- resuming the same session in two tabs would have two processes appending to one transcript.
230
- - **Only the conversation comes back.** Local servers and background processes you had running do not.
231
- - Dead rows are distinguished by **shape** (dotted bar + dimmed), not colour. The six status colours are already
232
- spoken for by live tabs, and a seventh would blur what a badge means.
233
- - Labels come from two places: the prompt picked up while it was alive (the ledger,
234
- `%LOCALAPPDATA%\tabby-agentdeck\sessions.json`) and the first prompt read from the head of the transcript.
235
- The former wins; sessions from before the plugin was installed, or started in another terminal, get the latter.
236
- - Sessions whose transcript is gone are dropped from both the list and the ledger — there is nothing left to resume.
237
-
238
- This exists as a pair with `noTabRecovery` below — Tabby's own restore brings back **only the shell**, and the
239
- session inside it is already dead, leaving a row of empty prompts. So that is turned off and the real restore lives here.
240
-
241
- ## Preview panel
242
-
243
- If what an agent produced is a document or an image, you cannot check it inside the terminal. So you go out to the
244
- file explorer, and because of that round trip you end up not looking at the output at all. This panel fills that gap —
245
- open it with the **▤** button in the sidebar or the `agentdeck-view` shortcut. Its open/closed state is restored on next start.
246
-
247
- **What it renders**
248
-
249
- | Kind | How |
250
- |---|---|
251
- | `md` `markdown` `mdx` | Headings, tables, lists, code, quotes, checkboxes and images. Images inside the document render too |
252
- | `png` `jpg` `gif` `webp` `svg` `bmp` `ico` `avif` | At original size (fit to width when larger than the panel) |
253
- | `csv` `tsv` | As a table. A quoted cell (`"a,b"`) is read as one cell |
254
- | Code and text (`.ts` `.cs` `.py` `.json` `.log` …) | With line numbers |
255
-
256
- **Three ways a file gets into the panel**
257
-
258
- 1. **Paths printed on screen are picked up** — `Edited src/hero.tsx`, `Wrote D:\a\b.md`, `Read build.py`.
259
- Whatever the agent touched is stacked into the recent list (the chips at the top). **This works for any agent** —
260
- Codex and Gemini print paths just like Claude Code, hook or no hook.
261
- Relative paths are resolved against that tab's working folder, and only files that actually exist are shown.
262
- The recent list is **per tab**.
263
- 2. **Drag and drop** — from the file explorer or your editor onto the panel.
264
- 3. **Type a path** — press **+** and enter one (`Enter` opens, `Esc` collapses).
265
- **If the file does not exist it is created** — press `Enter` once more and it is created (parent folders included)
266
- and opened for editing. No trip to the file explorer to start a note. It takes two presses because a typo'd path
267
- silently creating an empty file costs more later than the extra keystroke does now.
268
-
269
- An open file that changes is **re-read by itself** — the panel follows along while the agent edits.
270
- Even while closed it **still reads** (`preload into the panel`, on by default) — it does not draw, but it keeps the
271
- recent chips and "the file to show next time" current, so pressing `▤` mid-task gives you **the files touched so far
272
- as chips with the most recent one in the body** (if you were on `Changes`, the diff is re-read in place).
273
- The same file is re-read on open, because file watching is off while the panel is closed.
274
- **Opening and closing is yours to do by default** — if you want the panel to open by itself the moment output appears,
275
- turn on **Settings → Preview panel → `open the panel automatically`** (off by default).
276
- Turn `preload` off instead and nothing happens automatically: paths only stack as chips and you choose what to look at.
277
- Links inside a document open in the browser if they are web links, in this panel if they are local files.
278
-
279
- **Switching tabs switches the panel** — not just the recent chips and the `Changes` tab, but **the body**, back to
280
- the file that session last had open. A tab that has looked at nothing shows an empty panel. Otherwise another
281
- session's document sitting open beside you reads as if it belonged to this one. Switching tabs alone does **not open**
282
- the panel — if it is closed, it is brought up to date the next time you open it.
283
-
284
- **Edit and save — `✎`**
285
-
286
- Read-only means a single typo sends you to a separate editor, and that round trip is exactly what this panel was meant to remove.
287
- Press `✎` and the body becomes an editor. `Save` (`Ctrl+S`) · `Cancel` (`Esc`).
288
-
289
- - Only text files (`md`, code, text, `csv`) are editable. The button dims on images and binaries.
290
- - **Very large files are not opened** (over 1.5 MB). Viewing can read just the head, but saving rewrites the whole
291
- file — saving while holding only the head would drop the tail. So it opens only when it can read all of it.
292
- - **BOM and line endings (CRLF/LF) are restored exactly as they were.** Otherwise a one-line edit makes P4 and git
293
- diff see the whole file as changed.
294
- - **Nothing overwrites the screen while you edit** — neither the watcher's re-read nor following a new file.
295
- If the file changed outside in the meantime, saving asks once more (the second press overwrites).
296
- - **Read-only files are announced on open** (a `read-only` marker). Finding out at save time wastes everything you typed.
297
- Pressing save stops once with `this is read-only (p4 edit first, if P4) — press again to clear the attribute and save`.
298
- **Why it is not cleared silently** — in this environment read-only usually means a file not checked out from P4,
299
- and anything written without a checkout is quietly lost on the next `p4 sync`.
300
- - With unsaved edits, it will not move to another file or to the `Changes` tab.
301
-
302
- **Find — `🔍` / `Ctrl+F`**
303
-
304
- Case-insensitive, highlights matches, `Enter` (next) and `Shift+Enter` (previous) move between them.
305
- `Esc` closes it and only the highlighting goes away. While editing it searches inside the editor and selects in place.
306
- **`Ctrl+F` is heard only inside the panel** — it must not steal the terminal's `Ctrl+F` (less, vim) — so click the body once first.
307
-
308
- Panel width is changed by dragging the divider it shares with the terminal. **Opening the panel relaxes the 4:3 rule and
309
- the terminal narrows by that much**; the sidebar does not shrink. On ultrawide displays 4:3 holds even with the panel open.
310
-
311
- > Raw HTML inside a document is shown **as text** (never turned into tags). Reference links (`[a][1]`) and footnotes are not supported.
312
-
313
- ### The `Changes` tab — the working tree's diff
314
-
315
- Switch with `Files` / `Changes` under the header. `Changes` reads `git diff HEAD` in that tab's working folder and shows
316
-
317
- - a `3 files · +716 -15` summary and the file list (status tag + `+N -M`). Click a filename to scroll to its diff
318
- - per-file unified diff — old and new line numbers on both sides, with additions, deletions, context and hunks coloured apart
319
- - untracked files by name only (no content diff)
320
-
321
- **It is agent-agnostic for the same reason the preview is** — the evidence is the git working tree, not a hook, so it
322
- holds no matter who made the change. If this is not a repository, git is missing, or the working folder is not known yet, it says so in one line.
323
-
324
- #### Stage · commit
325
-
326
- The tag at the left of a file row is its **staging state** — `unstaged` / `staged` / `both` (partially staged) /
327
- `untracked` / `conflict`. Click it to `git add` the file or take it back (`git reset`). Below is a
328
- `staged N · unstaged M` summary, a message box and the commit button.
329
-
330
- **Four layers against an accidental commit —**
331
-
332
- 1. An empty message locks the button
333
- 2. Whitespace-only is rejected with a reason
334
- 3. The first press does not commit; it turns into `confirm commit` (in a warning colour). The **second press** runs it
335
- 4. Editing the message while confirmation is pending cancels that pending state (so it cannot commit under a different message)
336
-
337
- With nothing staged it does not commit — it never widens to `git commit -a`.
338
-
339
- > The working folder is **the cwd the tab was opened with**. `cd` in the shell does not change what Tabby reports
340
- > (measured on local Windows sessions), so open a new tab in that folder to look at another repository.
341
- > `git` walks up looking for a repository — if your home directory is one, it is found from anywhere.
13
+ ![tabby-agentdeck — terminal, preview panel and session deck](docs/guide/img/00-full.png)
342
14
 
343
15
  ## Keys
344
16
 
345
- | Key | Action |
346
- |---|---|
347
- | `Ctrl+Enter` / `Shift+Enter` | Newline (sends `0x0A`). `agentdeck-newline` |
348
- | `Ctrl+V` | Paste. If the clipboard holds **only an image**, the image-paste key is handed to the app instead of text — Claude Code and Codex read the clipboard themselves. `agentdeck-paste` |
349
- | Right-click | Copy with a selection, paste without one. Hold (250 ms default) for the context menu |
350
- | `Ctrl+Shift+L` | Focus the sidebar list / same key returns to the terminal. `agentdeck-focus-list` |
351
- | `Ctrl+W` | Close the current tab — the **active tab** from the terminal, the **focused row** when the list has the keyboard. Only while `keyboardNav` + `keyboardCloseTab` are on; off, it flows through as the shell's delete-previous-word (`0x17`) |
352
- | (unbound) | `agentdeck-toggle` — sidebar / 4:3 on-off |
353
- | (unbound) | `agentdeck-repair` — screen repair |
354
- | (unbound) | `agentdeck-view` — open/close the preview panel (same as the sidebar `▤`) |
355
- | (unbound) | `agentdeck-view-mode` — switch the panel between `Files` and `Changes` |
356
-
357
- Bind keys under Tabby Settings → Hotkeys, in the `agentdeck-*` entries.
358
- `Ctrl+V` is handled **in the capture phase, not as a hotkey**. On startup it is removed from **both** Tabby's own
359
- `hotkeys.paste` and `agentdeck-paste` — leaving it in the hotkey table let Tabby push the same key down two paths,
360
- so one press pasted twice (the cause is written up in `docs/DEVELOPMENT.md`).
361
-
362
- If you need a literal `Ctrl+V` (0x16, quoted-insert) to reach the terminal app, set `claimCtrlV: false`.
363
- Then neither we nor the hotkey table claim it and xterm passes 0x16 straight to the PTY —
364
- **but `Ctrl+V` paste stops working in that state** (that is the point of the setting).
365
- Paste with `Ctrl+Shift+V` / `Shift+Insert` (Tabby's own) or right-click instead.
366
-
367
- ### Driving the sidebar from the keyboard
368
-
369
- `Ctrl+Shift+L` enters the list. It exists for looking through tabs without the mouse, and the rules are these.
370
-
371
17
  | Key | What it does |
372
18
  |---|---|
373
- | `↑` `↓` | Move between rows. **The active tab does not change** |
374
- | `Home` `End` | First row / last row |
375
- | `Enter` | On a tab row, switch to it and hand the keys back to the terminal. On a group header, collapse/expand |
376
- | `Esc` | With a search or filter set, **clear that first**. With nothing to clear, leave for the terminal |
377
- | `Tab` `Shift+Tab` | To the search box |
378
- | `Ctrl+W` | Close the focused row's tab (nothing on a group header). Outside the list — in the terminal — it is the active tab |
379
-
380
- **Why focus and selection are separate** — if the tab changed on every row you passed, that tab's output would cover
381
- the screen and the terminal width would be re-applied each time, making the display jump. Looking through your tabs
382
- would be impossible. So `↑↓` move only the outline (keyboard focus) and `Enter` is the single thing that opens a tab.
383
-
384
- The search box and the list are joined by `↓` (box → first result) and `↑` (first row → box).
385
- `↑` inside the search box is not intercepted — in a single-line input that key moves the cursor to the start,
386
- and you really do use it when fixing a search term.
387
-
388
- While focus is not on the sidebar it **intercepts not one key** (the listener is on the list element, so the events
389
- never arrive). If you want it off anyway, `keyboardNav: false`.
390
-
391
- `Ctrl+W` is the one exception — it has to be received from the terminal too, so it is caught in the **document capture phase**.
392
- In exchange it only swallows the key **when there is a tab to close**: on a group header, or in a settings input, it flows through.
393
- That is also why it is not in the hotkey table — a Tabby hotkey swallows the key merely by being registered, so turning the
394
- setting off would still kill the terminal's `Ctrl+W` (delete previous word). To keep that key, set
395
- `keyboardCloseTab: false` (`Close tab with Ctrl+W` in the settings window) and list navigation stays.
396
- Closing goes through the same path as the sidebar `✕`, so Tabby asks as usual when a process is still running.
397
-
398
- ### IME input — keeping a composing syllable from following the cursor
399
-
400
- Typing Korean (or any IME language) and pressing `Home`, `End` or `Shift+Enter` used to make **the last composing
401
- syllable follow the cursor to its new position**. Arrow keys were fine, and that difference is the cause — xterm
402
- **commits the composition first** for keys that come through its own key path, but when bytes are written straight
403
- to the pty without that path (terminal hotkeys, our newline), the commit is deferred by a `setTimeout`, so
404
- **the bytes go first and the syllable after**.
405
-
406
- So every write to the pty is funnelled through one place, and just before writing it looks at the composition state:
407
-
408
- - composing → **wait for composition to end**, then write
409
- - a commit is already scheduled → step back one tick
410
- - state unreadable → just write (this guard must never block normal input)
411
-
412
- If composition does not end, it is forced after 0.4 s and written — input not going out at all is the worst outcome.
413
-
414
- > **Why wait (0.6.3).** It used to force the commit and write immediately. But even after we forced it, the IME
415
- > announced its own commit on its own schedule, and the syllable went out **again** — that second one landed after
416
- > the newline, so **the character dropped to the next line**. Now the IME's ordering is untouched, so the syllable
417
- > goes out once, before the newline.
418
-
419
- Turn it off with `imeOrderGuard: false`.
420
-
421
- ### Screen repair (`↻`)
422
-
423
- Press it when the screen is broken — the statusline gone, the input-box divider cut off mid-screen.
424
- It recalculates width → syncs the pty size → clears **only the input box's remnant** → nudges to deliver a SIGWINCH.
425
- The app redraws the cleared area on its own render cycle.
426
-
427
- **It does not erase what you were reading.** It used to send `Ctrl+L` at the end to mean "redraw everything", but
428
- Claude Code takes that as **clear screen** and pushed the whole conversation into the scrollback
429
- (captured 2026-09-11: repairing a healthy screen emptied rows 0–72 of 78 and left only the input box).
430
- So `repairSendRedrawKey` is off by default — what actually fixes things is clearing the remnant and matching the size,
431
- and it works without it. `repairHard: true`, which also empties the scrollback, is off by default too.
432
-
433
- ## Settings
434
-
435
- The **settings window (Settings → AgentDeck)** holds only what you would actually change — layout on/off, resetting a
436
- mouse-dragged arrangement, disabling tab restore on startup, the preview panel (open · path scraping · follow new files),
437
- installing/removing the Claude Code hook, and the working-root profile. The rest live under `agentDeck` in Tabby's
438
- config file (`%APPDATA%\tabby\config.yaml`).
439
-
440
- | Key | Default | Description |
441
- |---|---|---|
442
- | `enabled` | `true` | Sidebar + 4:3 layout on/off |
443
- | `aspectW` / `aspectH` | `4` / `3` | Terminal viewport ratio |
444
- | `sidebarDock` | `right` | Sidebar position `left` / `right` / `top` / `bottom`. Also changed by dragging the header |
445
- | `sidebarWidth` | `0` | Width in px when docked left/right. 0 = derived from the aspect ratio |
446
- | `sidebarHeight` | `200` | Height in px when docked top/bottom |
447
- | `sidebarMin` / `sidebarMax` | `200` / `560` | Range for the derived width (px). For exactly 4:3 on a wide monitor, set `sidebarMax` to `window width − window height × 4/3` |
448
- | `sortByStatus` | `false` | Sort by urgency (waiting → error → rate limited → running → done → idle). Turning it on **blocks drag reordering** — a moved row would snap back on the next render |
449
- | `collapsedGroups` | `[]` | Keys of collapsed groups. Clicking a header adds one here. `Expand all` in the settings window empties it |
450
- | `viewerOpen` | `false` | Whether the preview panel is open. Toggled by `▤` / `agentdeck-view`, or set when **it opens itself on a new file**, and restored on next start. Not a value you set by hand |
451
- | `viewerDock` | `right` | Which side the panel attaches to, `left` / `right`. On the same side as the sidebar, the sidebar takes the outer edge |
452
- | `viewerWidth` | `420` | Panel width in px. Changed by dragging the divider. In a narrow window, capped at `window width − 320 − 140` |
453
- | `searchBox` | `true` | Show the search row in the sidebar. Turning it off also clears any active filter (no hidden filters) |
454
- | `keyboardNav` | `true` | `Ctrl+Shift+L` into the list, then `↑↓`/Enter/Esc. Nothing is intercepted while focus is elsewhere, so leaving it on costs nothing |
455
- | `keyboardCloseTab` | `true` | `Ctrl+W` closes a tab — the active one from the terminal, the focused row when the list has the keyboard. Off (or with `keyboardNav` off) `Ctrl+W` goes to the terminal as delete-previous-word |
456
- | `keyboardNavWrap` | `false` | `↑↓` wrap around at the ends. Off, they stop — you can see you are at the bottom and the list does not jump wholesale. End-to-end is Home/End |
457
- | `viewerScrape` | `true` | Pick up file paths printed on screen into the recent list. Off, only drag-drop and typed paths |
458
- | `viewerDropOpen` | `ask` | When a file is dropped on the terminal — `ask` / `always` open in panel / `paste` the path / `never` (stock behaviour) |
459
- | `viewerPreload` | `true` | Keep files the agent touched loaded **even while the panel is closed** (recent chips + what to show on next open). Off, nothing happens automatically. `Preload into the panel` in the settings window |
460
- | `viewerAutoOpen` | `false` | **Open the closed panel by itself** on a new file. Off by default — opening is yours to do, and the contents are kept current by `viewerPreload` above. `Open the panel automatically` in the settings window |
461
- | `viewerFollowMode` | `''` | (old value) Split into the two toggles above in 0.18.0. An `open`/`show`/`manual` left here is migrated once on startup and cleared |
462
- | `viewerFollow` | `true` | (older value) Moved to `viewerFollowMode` in 0.17.0. A `false` is read as `viewerPreload: false` by the migration and reset to the default |
463
- | `viewerRecentMax` | `12` | How many files stay in the recent list |
464
- | `imeOrderGuard` | `true` | Commit a composing syllable before writing bytes to the pty (see "IME input") |
465
- | `releaseHomeEnd` | `true` | Take Home / End out of Tabby's hotkeys and hand them back to xterm's own path |
466
- | `idleAfterMs` | `2500` | No output for this long means `idle` |
467
- | `autoDetect` | `true` | Infer status from output and title patterns |
468
- | `showElapsed` | `true` | Show elapsed time |
469
- | `dragAutoScroll` | `true` | The list scrolls by itself at the edges while dragging. Off, you have to drop, scroll and drag again — but **dropping past the last row stops slipping** |
470
- | `keepRenderingWhenHidden` | `true` | Keep the sidebar updating while the window is covered. Off, a status change can show up to a minute late |
471
- | `subagentCount` | `true` | Show the running subagent count on a row. Nothing is drawn at zero, so leaving it on costs nothing. Off, transcripts are **not read at all** |
472
- | `enterAsLabel` | `true` | Take the input box text as the task label when Enter sends a prompt |
473
- | `labelAsTitle` | `true` | Use the task label instead of the tab title on the sidebar title row. Tabby's own tab title is untouched |
474
- | `noTabRecovery` | `false` | Do not restore previous tabs on startup — forces Tabby's `recoverTabs` to false. Only the shell is restored and the Claude session inside it is dead, so it is meaningless (turn it on in the settings window) |
475
- | `notifyChannel` | `true` | The channel hook reports arrive on (local TCP + status file). Installing the hook turns it on automatically |
476
- | `notifyPort` | `47500` | That TCP port (127.0.0.1 only). If taken, it falls back to a random port and the real number is written to `%LOCALAPPDATA%\tabby-agentdeck\port` |
477
- | `hookPromptDismissed` | `false` | Do not show the hook-install prompt on startup again |
478
- | `claimCtrlV` | `true` | Handle `Ctrl+V` in the capture phase. On startup, `Ctrl-V` is removed from **both** `hotkeys.paste` and `agentdeck-paste` (this is what stops the double paste). `false` sends a literal 0x16 to the app and **paste stops working** |
479
- | `pasteImageWithCtrlV` | `true` | With only an image on the clipboard, send the image-paste key instead of pasting text |
480
- | `imagePasteKey` | `alt-v` | Which key that is. `ctrl-v` (0x16) covers Claude Code and Codex; `alt-v` (ESC v) is Claude Code only |
481
- | `claimRightClick` | `true` | The plugin handles right-click (Tabby's `terminal.rightClick` is set to `off`) |
482
- | `rightClickMenuMs` | `250` | Hold longer than this for the context menu instead of paste |
483
- | `terminalOpacity` | `75` | Terminal background opacity (%). Below 100 lets a `tabby-background` image show through. 100 = stock |
484
- | `sidebarOpacity` | `75` | Sidebar / window margin opacity (%) |
485
- | `clipBackgroundToTerminal` | `true` | Clip the `tabby-background` image to the terminal area so it does not show under the sidebar |
486
- | `screenWatch` | `true` | Judge every two seconds whether the screen is broken and **only record** it to `~/.agentdeck-screen.log` (no repair). Rolls to `.1` past 4 MB |
487
- | `autoRepairScreen` | `false` | Quietly run screen repair when it is judged broken |
488
- | `autoRepairMinStreak` | `2` | Require this many consecutive broken readings before repairing (so a half-drawn frame is not mistaken for one) |
489
- | `autoRepairCooldownMs` | `10000` | Do not repair again for this long after an automatic repair |
490
- | `autoRepairMaxPerTab` | `3` | Maximum automatic repairs per tab. 0 = unlimited |
491
- | `repairHard` | `false` | Screen repair also runs `xterm.reset()` (which clears the scrollback) |
492
- | `repairSendRedrawKey` | `false` | Send `Ctrl+L` at the end of a repair. **Off is the default** — Claude Code takes it as a **screen clear**, not a redraw, and pushes the conversation you were reading into the scrollback (captured 2026-09-11). On, it applies to automatic repairs too |
493
- | `rootProfile` | `false` | Create a working-root profile and make it the default (turn it on in the settings window) |
494
- | `rootProfileName` | `Agent Root` | That profile's name |
495
- | `rootProfileCwd` | `''` | Where new tabs open. Empty means the profile is not created. Write it with forward slashes (e.g. `D:/Project`) |
496
- | `rootProfileCommand` | `powershell.exe` | The shell |
497
- | `rootProfileArgs` | `-NoLogo -ExecutionPolicy Bypass` | Shell arguments. `Bypass` keeps global npm `.ps1` wrappers (`claude` and friends) from being blocked by execution policy — for that session only |
498
- | `rootProfileEnv` | `TERM=xterm-256color`, `COLORTERM=truecolor` | Shell environment variables (only missing ones are filled in) |
499
- | `alertFlash` | `true` | Flash the taskbar icon when a tab moves to `waiting`/`error`/`rate limited` while the window is behind. Stops when the window is focused |
500
- | `alertBadge` | `true` | Draw a badge (circle + number) on the taskbar icon with the count of `waiting` + `error` + `rate limited` tabs. Cleared at zero |
501
- | `alertToast` | `false` | Also raise an OS toast. Clicking it brings the window forward and selects that tab |
502
-
503
- ## Reporting a problem
504
-
505
- **[Open an issue](https://github.com/jghwang2/tabby-agentdeck/issues/new/choose)** — a bug report or a feature request.
506
-
507
- Before you write it up, press **Settings → AgentDeck → Report a problem → `collect`**.
508
- It bundles the diagnostics into a single zip and opens the folder it is in, so you can attach that zip to the issue.
509
-
510
- What goes in
511
-
512
- | File | Contents |
513
- |---|---|
514
- | `report.txt` | Plugin/Tabby/Electron version, OS, settings (docking, aspect ratio, `useConPTY` …), the last 200 diagnostic lines of this session |
515
- | `agentdeck-diag.log` | `~/.agentdeck-diag.log` — session header, batching/input/pty decisions, uncaught exceptions with stacks, swallowed failures (`CATCH …`) |
516
- | `agentdeck-diag.log.1` | The previous rollover (pushed aside past 2 MB) |
517
- | `agentdeck-screen.log` | **Only when "include screen contents" is on.** The terminal screen, verbatim, at the moment it broke |
518
-
519
- Worth knowing
520
-
521
- - Logs **are not cleared on startup.** They roll one generation to `.1` past 2 MB, so yesterday's problem is usually still there.
522
- - The logs contain **tab titles and the paths of files you opened**. Read it before sending if you like; screen contents are excluded by default.
523
- - Too broken to open the settings window? From the devtools console, `__agentdeck.collectDiag()` builds the same zip and returns its path.
524
- For just the recent diagnostic lines, `__agentdeck.kickLog`.
525
-
526
- ## Known issues
527
-
528
- ### New tabs will not open on Tabby 1.0.235
19
+ | `Ctrl+Shift+T` (⌘+T) | New tab in the work root. Replaces Tabby's own new-tab |
20
+ | `Ctrl+1` … `Ctrl+9` | Jump to the Nth session in sidebar order |
21
+ | `Ctrl+Shift+L` | Focus the session list / same key returns to the terminal. Then `↑↓` walk, `Enter` picks, `Esc` leaves |
22
+ | `Ctrl+W` | Close the current tab (the focused row when the list has the keyboard) |
23
+ | `Ctrl+Shift+O` | Open / close the preview panel |
24
+ | `Ctrl+Shift+R` | Screen repair (same as `↻`). Replaces Tabby's own rename-tab — rename by double-clicking the row |
25
+ | `Ctrl+Shift+S` | Split side by side (Tabby's own) |
26
+ | `Ctrl+Shift+D` | Split top / bottom (Tabby's own) |
27
+ | `Ctrl+Shift+Q` | Close the focused split pane |
28
+ | `Ctrl+Enter` / `Shift+Enter` | Newline without sending |
29
+ | `Ctrl+V` | Paste. An image-only clipboard is handed to the agent as an image |
30
+ | Right-click | Copy with a selection, paste without one. Hold for the context menu |
31
+ | `Ctrl+F` / `Ctrl+S` | Find / save inside the preview panel |
32
+
33
+ Two actions ship unbound: `agentdeck-toggle` (sidebar / 4:3 on-off) and `agentdeck-view-mode` (Files ↔ Changes).
34
+ Change any of them under **Settings → AgentDeck → Shortcuts** — a key already used by Tabby or AgentDeck is flagged before it is applied. Tabby Settings → Hotkeys → `agentdeck-*` works too.
529
35
 
530
- ```
531
- Error: ENOENT, node_modules\node-pty\lib\conpty_console_list_agent not found in ...\resources\app.asar
532
- ```
533
-
534
- A packaging bug in the Tabby build, unrelated to this plugin. Turning ConPTY off avoids that path.
535
-
536
- ```yaml
537
- # %APPDATA%\tabby\config.yaml
538
- terminal:
539
- useConPTY: false
540
- ```
541
-
542
- ## Development
543
-
544
- ```powershell
545
- npm install
546
- npm run build # runs tools/sync-version.js first (prebuild)
547
- npm test # unit tests + Codex regression
548
- npm run typecheck
549
- ```
550
-
551
- **`version` in `package.json` is the single source of truth** — `package-lock.json` and the four web pages under
552
- `docs/` are derived from it by `tools/sync-version.js`, and `test/version.test.js` fails on drift.
553
- Releases go through one command (`npm run release -- 1.1.4`), which stamps the version, derives, tests, commits and tags.
554
- `test/tags.test.js` checks that every committed version has a matching tag.
555
-
556
- Development procedure, architecture, design rationale and the measured pitfalls are in
557
- [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md); the regression checklist is in [`docs/REGRESSION.md`](docs/REGRESSION.md);
558
- npm publishing is in [`docs/NPM-PUBLISH.md`](docs/NPM-PUBLISH.md).
36
+ ## Install
559
37
 
560
- ## License
38
+ Inside Tabby: Settings → Plugins → search `agentdeck` → Install → restart Tabby.
39
+ Everything else — status, groups, preview panel, diff and commit, resuming sessions, settings — is in the
40
+ [user guide](https://jghwang2.github.io/tabby-agentdeck/guide/) and the [full reference](docs/REFERENCE.md).
561
41
 
562
- MIT
42
+ Built and verified on Windows. MIT.
package/dist/agents.d.ts CHANGED
@@ -25,6 +25,21 @@ export interface AgentProfile {
25
25
  id: AgentId;
26
26
  /** 사람에게 보여줄 이름 (사이드바·진단에 쓴다) */
27
27
  label: string;
28
+ /**
29
+ * 사이드바 줄 머리에 붙일 표식 (인라인 SVG 원문).
30
+ *
31
+ * **문자 글리프가 아니라 SVG 인 이유** — 에이전트들이 스스로 쓰는 글리프는
32
+ * Claude `✳`·Gemini `✦` 처럼 둘 다 별 모양이라 12px 에서 형태로 갈리지 않는다
33
+ * (색만 다르면 색각 이상에서 구분이 사라진다). 형태(육각·별·6방 별표)와 색을
34
+ * 둘 다 다르게 주려면 도형을 직접 그려야 한다.
35
+ *
36
+ * 이 필드는 **상수 문자열이다** — 사용자 입력이 절대 섞이지 않으므로 읽는 쪽이
37
+ * `innerHTML` 로 넣어도 안전하다(`deck.service.ts` `renderTab`). 그래서 여기에는
38
+ * 상수만 넣고, 어떤 경우에도 런타임 값을 끼워 만들지 않는다.
39
+ *
40
+ * 빈 문자열이면 표식을 그리지 않는다(= 에이전트를 못 알아본 보통 셸 탭).
41
+ */
42
+ icon: string;
28
43
  /** 프로세스 이름/명령줄에서 이 에이전트를 알아보는 조각 (소문자 비교) */
29
44
  processHints: string[];
30
45
  /** 탭 제목에서 알아보는 조각 (소문자 비교) */