tabby-agentdeck 1.1.1 → 1.1.3
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/CHANGELOG.md +127 -0
- package/README.ko.md +590 -0
- package/README.md +449 -438
- package/dist/codexHooks.d.ts +6 -0
- package/dist/codexWheel.d.ts +6 -0
- package/dist/cursorVisibility.d.ts +9 -0
- package/dist/deck.service.d.ts +3 -0
- package/dist/gitDiff.d.ts +11 -0
- package/dist/index.js +1 -19575
- package/dist/index.js.map +1 -1
- package/dist/notify.service.d.ts +48 -0
- package/dist/screenAgent.d.ts +24 -0
- package/dist/sessionLedger.d.ts +4 -1
- package/dist/settings.component.d.ts +3 -0
- package/dist/subagents.d.ts +31 -0
- package/dist/synchronizedOutput.d.ts +10 -0
- package/dist/terminalInput.d.ts +6 -0
- package/dist/update.service.d.ts +8 -0
- package/dist/viewPanel.d.ts +50 -0
- package/dist/viewer.d.ts +4 -0
- package/hooks/agentdeck-codex-notify.ps1 +25 -0
- package/hooks/agentdeck-notify.ps1 +89 -9
- package/package.json +20 -5
- package/updater/agentdeck-update.ps1 +27 -5
package/README.md
CHANGED
|
@@ -1,62 +1,71 @@
|
|
|
1
1
|
# tabby-agentdeck
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/tabby-agentdeck)
|
|
4
|
+
[](https://www.npmjs.com/package/tabby-agentdeck)
|
|
5
|
+
[](https://github.com/jghwang2/tabby-agentdeck/actions/workflows/ci.yml)
|
|
6
|
+
[](LICENSE)
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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)**
|
|
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**.
|
|
11
|
+
|
|
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.
|
|
9
16
|
|
|
10
17
|
```
|
|
11
18
|
┌────────────────────┬──────────────────┬──────────────────┐
|
|
12
19
|
│ │ note.md + ⟳ ✕ │ AGENT DECK 4 │
|
|
13
|
-
│
|
|
14
|
-
│ │ note.md shot.png│ ●
|
|
15
|
-
│ Tabby
|
|
16
|
-
│
|
|
17
|
-
│ │
|
|
18
|
-
│ │ [
|
|
19
|
-
│ │ 742 B ·
|
|
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│
|
|
20
27
|
└────────────────────┴──────────────────┴──────────────────┘
|
|
21
|
-
|
|
28
|
+
terminal preview panel session deck
|
|
22
29
|
```
|
|
23
30
|
|
|
24
|
-
|
|
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.
|
|
25
34
|
|
|
26
|
-
##
|
|
35
|
+
## Features
|
|
27
36
|
|
|
28
|
-
| |
|
|
37
|
+
| | What it does |
|
|
29
38
|
|---|---|
|
|
30
|
-
| **4:3
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
| **Claude Code
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
##
|
|
56
|
-
|
|
57
|
-
**Tabby
|
|
58
|
-
|
|
59
|
-
|
|
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**:
|
|
60
69
|
|
|
61
70
|
```powershell
|
|
62
71
|
npm install
|
|
@@ -64,479 +73,465 @@ npm run build
|
|
|
64
73
|
powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1
|
|
65
74
|
```
|
|
66
75
|
|
|
67
|
-
`install.ps1`
|
|
68
|
-
|
|
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.
|
|
69
78
|
|
|
70
|
-
###
|
|
79
|
+
### Auto-update
|
|
71
80
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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:
|
|
75
84
|
|
|
76
85
|
```
|
|
77
|
-
Tabby
|
|
86
|
+
Tabby closes → (outside) npm install → Tabby starts itself back up
|
|
78
87
|
```
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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`.
|
|
83
92
|
|
|
84
|
-
|
|
|
93
|
+
| Setting | Default | |
|
|
85
94
|
|---|---|---|
|
|
86
|
-
| `autoUpdate` | `true` |
|
|
87
|
-
| `updateCheckIntervalHours` | `6` |
|
|
88
|
-
| `updateRegistry` | `https://registry.npmjs.org` |
|
|
89
|
-
| `updateSkipVersion` | — |
|
|
90
|
-
|
|
91
|
-
> **왜 Tabby 를 닫고 까나.** 두 가지 때문이다. ① 돌고 있는 플러그인은 자기를 갈아끼울 수 없다 —
|
|
92
|
-
> Tabby 는 기동할 때 `dist/index.js` 를 한 번 읽어 메모리에 올리므로, 디스크를 바꿔도 돌던 코드는
|
|
93
|
-
> 옛것 그대로다. ② Windows 는 로드된 파일을 잡고 있어서, 떠 있는 동안 npm 이 그 폴더를 지우려 들면
|
|
94
|
-
> 반쯤 지운 채 실패할 수 있다 — 플러그인이 깨진 채 남는 것이 가장 나쁜 결과다.
|
|
95
|
-
> 그래서 설치는 임시 폴더로 떼어낸 `updater/agentdeck-update.ps1` 이 **Tabby 종료를 확인한 뒤** 한다.
|
|
96
|
-
|
|
97
|
-
> **개발 설치에는 설치하지 않는다.** 플러그인 폴더의 `tabby-agentdeck` 이 소스 트리를 가리키는
|
|
98
|
-
> 링크일 때(`install.ps1` 로 깐 개발 환경), 거기에 `npm install` 이 돌면 **작업 중인 소스를 덮어쓴다.**
|
|
99
|
-
> `src/`·`.git`·`webpack.config.js` 중 하나라도 보이거나 항목이 링크면 새 버전이 있다는 것만 알리고
|
|
100
|
-
> 손대지 않는다 (판정은 `update.ts` 의 `canSelfUpdate`).
|
|
101
|
-
|
|
102
|
-
### 버전은 한 곳에만 적는다
|
|
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 |
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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.
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
npm
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`test/version.test.js` 가 어긋남을 잡으므로 빌드를 안 거친 수정도 `npm test` 에서 걸린다.
|
|
115
|
-
**문서·주석의 `0.18.0 부터 …` 같은 이력 표기는 대상이 아니다** — 그건 "지금 버전" 이 아니라
|
|
116
|
-
그 기능이 들어온 시점이라 갱신하면 오히려 거짓이 된다.
|
|
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`).
|
|
117
111
|
|
|
118
|
-
|
|
119
|
-
> (`package.json` 1.0.0 / `package-lock.json` 0.3.0 / 사용설명서 0.17.0 / 커밋된 값 0.17.0).
|
|
112
|
+
## Task names and status
|
|
120
113
|
|
|
121
|
-
|
|
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.
|
|
122
116
|
|
|
123
|
-
|
|
124
|
-
둘 다 **설정 없이 플러그인만으로 돈다.** 더 정확하게 하려면 Claude Code 훅을 건다.
|
|
117
|
+
### Task names
|
|
125
118
|
|
|
126
|
-
|
|
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.
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
붙여넣기·↑ 히스토리·IME 로 들어온 글자도 화면에 그려진 대로 잡힌다.
|
|
130
|
-
Shift+Enter / Ctrl+Enter 는 줄바꿈이라 라벨을 바꾸지 않고, 입력창 머리를 못 찾으면(일반 셸, y/n 확인 등) 라벨을 그대로 둔다.
|
|
123
|
+
### Status
|
|
131
124
|
|
|
132
|
-
|
|
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).
|
|
133
139
|
|
|
134
|
-
|
|
135
|
-
`Do you want to`, `❯ 1. Yes`, `(y/n)` 같은 패턴이 보이면 `승인대기`. Claude Code 가 제목에 넣는 스피너도 진행중으로 읽는다.
|
|
136
|
-
`승인대기` 인 탭에 `esc to interrupt` 같은 작업 중 문구가 다시 보이면 사람이 승인한 것으로 보고 `진행중` 으로 올린다 —
|
|
137
|
-
훅이 고정한 탭에도 적용된다 (승인 뒤 첫 훅은 도구가 끝난 뒤라, 긴 빌드를 승인하면 그동안 승인대기에 박혀 있었다).
|
|
138
|
-
- **Claude Code 훅(권장)**: 설정 → AgentDeck → "상태 통보 훅" 설치. `~/.claude/settings.json` 의
|
|
139
|
-
UserPromptSubmit / Notification / PostToolUse / PostToolUseFailure / PermissionDenied / Stop / StopFailure 에 통보 스크립트를 건다.
|
|
140
|
-
다른 훅은 건드리지 않고 쓰기 전에 백업을 남긴다. 0.3.0 에서 이벤트가 늘었으므로 옛 설치는 "설치 안 됨" 으로 보인다 — 설치를 다시 누르면 된다.
|
|
141
|
-
에이전트가 "지시를 받았다 / 승인을 기다린다 / 도구가 돌았다(=승인됐다) / 끝났다 / 실패로 끝났다" 를 직접 알려주므로 판정이 흔들리지 않는다. 설정 창의 "제거" 로 되돌린다.
|
|
142
|
-
승인대기에는 이유(예: `Bash 권한`, `입력 대기`)가 함께 표시된다 — `⏸ 승인대기 · Bash 권한`. 상태가 바뀌면 이유는 사라진다.
|
|
143
|
-
- **한도 도달**: Claude Code 가 사용량 한도(세션/주간)에 걸려 턴이 끊기면 `StopFailure` 훅이 `error: rate_limit` 로 알려주고
|
|
144
|
-
사이드바는 `⛔ 한도 도달 · 12pm 리셋` 처럼 리셋 시각과 함께 보여준다. 승인할 것이 없는 상태라 승인대기와 구분한다.
|
|
145
|
-
한도 외의 실패(overloaded, server_error …)는 `✖ 오류 · overloaded` 로 표시된다. 한도가 풀려 세션이 이어지면(다음 프롬프트·도구 실행) 진행중으로 돌아온다.
|
|
146
|
-
훅 보고는 플러그인이 탭 셸에 심은 `AGENTDECK_TAB` 환경변수로 어느 탭인지 확정한다
|
|
147
|
-
(0.2.2 이전에 열린 탭·재시작으로 복원된 탭은 프로세스 계보로 폴백).
|
|
140
|
+
### Working out which agent is running
|
|
148
141
|
|
|
149
|
-
|
|
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.
|
|
150
144
|
|
|
151
|
-
|
|
152
|
-
|
|
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.
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
|
|
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.
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
> 상태 판정에 합집합(= 예전과 같은 규칙)을 쓴다. 프로필을 붙였다는 이유로 판정이 예전보다 나빠지면
|
|
159
|
-
> 개선이 아니기 때문이다. 실측이 생기면 그 프로필의 `patternsProven` 을 켜면 된다.
|
|
153
|
+
### Setting it by hand
|
|
160
154
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
- 사이드바에서 **우클릭 → 상태 고르기**, **더블클릭 → 이름 편집**.
|
|
164
|
-
- 셸 스크립트 안에서라면 콘솔 제목을 `[AD:<status>] <라벨>` 로 바꾼다. 접두사는 표시에서 지워진다.
|
|
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.
|
|
165
157
|
|
|
166
158
|
```powershell
|
|
167
|
-
$host.UI.RawUI.WindowTitle = "[AD:waiting]
|
|
159
|
+
$host.UI.RawUI.WindowTitle = "[AD:waiting] waiting for deploy approval"
|
|
168
160
|
```
|
|
169
161
|
|
|
170
|
-
status
|
|
171
|
-
|
|
172
|
-
|
|
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.
|
|
173
166
|
|
|
174
|
-
###
|
|
167
|
+
### How many subagents are running
|
|
175
168
|
|
|
176
|
-
Claude Code
|
|
177
|
-
|
|
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.
|
|
178
171
|
|
|
179
|
-
|
|
180
|
-
(`~/.claude/projects
|
|
181
|
-
|
|
182
|
-
|
|
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).
|
|
183
176
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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.
|
|
187
180
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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.
|
|
191
184
|
|
|
192
|
-
###
|
|
185
|
+
### Alerts
|
|
193
186
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
OS
|
|
198
|
-
|
|
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.
|
|
199
192
|
|
|
200
|
-
##
|
|
193
|
+
## Resuming past sessions
|
|
201
194
|
|
|
202
|
-
|
|
203
|
-
`claude --resume
|
|
204
|
-
**
|
|
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**.
|
|
205
198
|
|
|
206
|
-
agentdeck
|
|
207
|
-
|
|
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:
|
|
208
201
|
|
|
209
202
|
```
|
|
210
203
|
▾ Root ⏸1 ●2
|
|
211
|
-
● Agent Root
|
|
212
|
-
|
|
213
|
-
⟲
|
|
204
|
+
● Agent Root running ❖2
|
|
205
|
+
rework season pass payout path 3m
|
|
206
|
+
⟲ Past sessions 5 ▸ ← collapsed by default
|
|
214
207
|
```
|
|
215
208
|
|
|
216
|
-
|
|
209
|
+
Expanded:
|
|
217
210
|
|
|
218
211
|
```
|
|
219
|
-
⟲
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|
224
217
|
```
|
|
225
218
|
|
|
226
|
-
|
|
|
219
|
+
| Action | What it does |
|
|
227
220
|
|---|---|
|
|
228
|
-
|
|
|
229
|
-
|
|
|
230
|
-
|
|
|
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 |
|
|
231
224
|
|
|
232
|
-
|
|
225
|
+
Worth knowing:
|
|
233
226
|
|
|
234
|
-
-
|
|
235
|
-
-
|
|
236
|
-
|
|
237
|
-
-
|
|
238
|
-
-
|
|
239
|
-
|
|
240
|
-
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
-
|
|
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.
|
|
244
237
|
|
|
245
|
-
|
|
246
|
-
|
|
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.
|
|
247
240
|
|
|
248
|
-
##
|
|
241
|
+
## Preview panel
|
|
249
242
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
`agentdeck-view`
|
|
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.
|
|
253
246
|
|
|
254
|
-
|
|
247
|
+
**What it renders**
|
|
255
248
|
|
|
256
|
-
|
|
|
249
|
+
| Kind | How |
|
|
257
250
|
|---|---|
|
|
258
|
-
| `md` `markdown` `mdx` |
|
|
259
|
-
| `png` `jpg` `gif` `webp` `svg` `bmp` `ico` `avif` |
|
|
260
|
-
| `csv` `tsv` |
|
|
261
|
-
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
1.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
`Esc`
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
-
|
|
326
|
-
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
`
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
>
|
|
348
|
-
>
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
| 키 | 동작 |
|
|
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.
|
|
342
|
+
|
|
343
|
+
## Keys
|
|
344
|
+
|
|
345
|
+
| Key | Action |
|
|
354
346
|
|---|---|
|
|
355
|
-
| `Ctrl+Enter` / `Shift+Enter` |
|
|
356
|
-
| `Ctrl+V` |
|
|
357
|
-
|
|
|
358
|
-
| `Ctrl+Shift+L` |
|
|
359
|
-
| `Ctrl+W` |
|
|
360
|
-
| (
|
|
361
|
-
| (
|
|
362
|
-
| (
|
|
363
|
-
| (
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
`Ctrl+V`
|
|
367
|
-
`
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
###
|
|
376
|
-
|
|
377
|
-
`Ctrl+Shift+L`
|
|
378
|
-
|
|
379
|
-
|
|
|
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
|
+
| Key | What it does |
|
|
380
372
|
|---|---|
|
|
381
|
-
| `↑` `↓` |
|
|
382
|
-
| `Home` `End` |
|
|
383
|
-
| `Enter` |
|
|
384
|
-
| `Esc` |
|
|
385
|
-
| `Tab` `Shift+Tab` |
|
|
386
|
-
| `Ctrl+W` |
|
|
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 |
|
|
387
379
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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.
|
|
391
383
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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.
|
|
395
387
|
|
|
396
|
-
|
|
397
|
-
|
|
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`.
|
|
398
390
|
|
|
399
|
-
`Ctrl+W`
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
`keyboardCloseTab: false`(
|
|
404
|
-
|
|
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.
|
|
405
397
|
|
|
406
|
-
###
|
|
398
|
+
### IME input — keeping a composing syllable from following the cursor
|
|
407
399
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
(
|
|
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**.
|
|
412
405
|
|
|
413
|
-
|
|
406
|
+
So every write to the pty is funnelled through one place, and just before writing it looks at the composition state:
|
|
414
407
|
|
|
415
|
-
-
|
|
416
|
-
-
|
|
417
|
-
-
|
|
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)
|
|
418
411
|
|
|
419
|
-
|
|
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.
|
|
420
413
|
|
|
421
|
-
>
|
|
422
|
-
>
|
|
423
|
-
>
|
|
424
|
-
>
|
|
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.
|
|
425
418
|
|
|
426
|
-
|
|
419
|
+
Turn it off with `imeOrderGuard: false`.
|
|
427
420
|
|
|
428
|
-
###
|
|
421
|
+
### Screen repair (`↻`)
|
|
429
422
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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.
|
|
433
426
|
|
|
434
|
-
|
|
435
|
-
Claude Code
|
|
436
|
-
(2026-09-11
|
|
437
|
-
|
|
438
|
-
|
|
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.
|
|
439
432
|
|
|
440
|
-
##
|
|
433
|
+
## Settings
|
|
441
434
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
`agentDeck`
|
|
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`).
|
|
445
439
|
|
|
446
|
-
|
|
|
440
|
+
| Key | Default | Description |
|
|
447
441
|
|---|---|---|
|
|
448
|
-
| `enabled` | `true` |
|
|
449
|
-
| `aspectW` / `aspectH` | `4` / `3` |
|
|
450
|
-
| `sidebarDock` | `right` |
|
|
451
|
-
| `sidebarWidth` | `0` |
|
|
452
|
-
| `sidebarHeight` | `200` |
|
|
453
|
-
| `sidebarMin` / `sidebarMax` | `200` / `560` |
|
|
454
|
-
| `sortByStatus` | `false` |
|
|
455
|
-
| `collapsedGroups` | `[]` |
|
|
456
|
-
| `viewerOpen` | `false` |
|
|
457
|
-
| `viewerDock` | `right` |
|
|
458
|
-
| `viewerWidth` | `420` |
|
|
459
|
-
| `searchBox` | `true` |
|
|
460
|
-
| `keyboardNav` | `true` | `Ctrl+Shift+L`
|
|
461
|
-
| `keyboardCloseTab` | `true` | `Ctrl+W`
|
|
462
|
-
| `keyboardNavWrap` | `false` |
|
|
463
|
-
| `viewerScrape` | `true` |
|
|
464
|
-
| `viewerDropOpen` | `ask` |
|
|
465
|
-
| `viewerPreload` | `true` |
|
|
466
|
-
| `viewerAutoOpen` | `false` |
|
|
467
|
-
| `viewerFollowMode` | `''` | (
|
|
468
|
-
| `viewerFollow` | `true` | (
|
|
469
|
-
| `viewerRecentMax` | `12` |
|
|
470
|
-
| `imeOrderGuard` | `true` |
|
|
471
|
-
| `releaseHomeEnd` | `true` | Home / End
|
|
472
|
-
| `idleAfterMs` | `2500` |
|
|
473
|
-
| `autoDetect` | `true` |
|
|
474
|
-
| `showElapsed` | `true` |
|
|
475
|
-
| `
|
|
476
|
-
| `
|
|
477
|
-
| `
|
|
478
|
-
| `
|
|
479
|
-
| `
|
|
480
|
-
| `
|
|
481
|
-
| `
|
|
482
|
-
| `
|
|
483
|
-
| `
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
486
|
-
| `
|
|
487
|
-
| `
|
|
488
|
-
| `
|
|
489
|
-
| `
|
|
490
|
-
| `
|
|
491
|
-
| `
|
|
492
|
-
| `
|
|
493
|
-
| `
|
|
494
|
-
| `
|
|
495
|
-
| `
|
|
496
|
-
| `
|
|
497
|
-
| `
|
|
498
|
-
| `
|
|
499
|
-
| `
|
|
500
|
-
| `
|
|
501
|
-
| `
|
|
502
|
-
| `
|
|
503
|
-
| `
|
|
504
|
-
| `
|
|
505
|
-
| `
|
|
506
|
-
| `
|
|
507
|
-
| `
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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 |
|
|
518
513
|
|---|---|
|
|
519
|
-
| `report.txt` |
|
|
520
|
-
| `agentdeck-diag.log` | `~/.agentdeck-diag.log` —
|
|
521
|
-
| `agentdeck-diag.log.1` |
|
|
522
|
-
| `agentdeck-screen.log` | **"
|
|
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 |
|
|
523
518
|
|
|
524
|
-
|
|
519
|
+
Worth knowing
|
|
525
520
|
|
|
526
|
-
-
|
|
527
|
-
-
|
|
528
|
-
-
|
|
529
|
-
|
|
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`.
|
|
530
525
|
|
|
531
|
-
##
|
|
526
|
+
## Known issues
|
|
532
527
|
|
|
533
|
-
### Tabby 1.0.235
|
|
528
|
+
### New tabs will not open on Tabby 1.0.235
|
|
534
529
|
|
|
535
530
|
```
|
|
536
531
|
Error: ENOENT, node_modules\node-pty\lib\conpty_console_list_agent not found in ...\resources\app.asar
|
|
537
532
|
```
|
|
538
533
|
|
|
539
|
-
Tabby
|
|
534
|
+
A packaging bug in the Tabby build, unrelated to this plugin. Turning ConPTY off avoids that path.
|
|
540
535
|
|
|
541
536
|
```yaml
|
|
542
537
|
# %APPDATA%\tabby\config.yaml
|
|
@@ -544,8 +539,24 @@ terminal:
|
|
|
544
539
|
useConPTY: false
|
|
545
540
|
```
|
|
546
541
|
|
|
547
|
-
##
|
|
542
|
+
## Development
|
|
548
543
|
|
|
549
|
-
|
|
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).
|
|
550
559
|
|
|
551
|
-
|
|
560
|
+
## License
|
|
561
|
+
|
|
562
|
+
MIT
|