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.
- package/CHANGELOG.md +24 -1
- package/README.ko.md +26 -574
- package/README.md +25 -545
- package/dist/agents.d.ts +15 -0
- package/dist/codexHooks.d.ts +23 -0
- package/dist/codexMeta.d.ts +87 -0
- package/dist/config.d.ts +120 -2
- package/dist/cursorVisibility.d.ts +33 -1
- package/dist/deck.service.d.ts +182 -19
- package/dist/devReload.service.d.ts +48 -0
- package/dist/devReload.stub.d.ts +19 -0
- package/dist/dock.d.ts +15 -0
- package/dist/i18n/af.d.ts +83 -0
- package/dist/i18n/bg.d.ts +83 -0
- package/dist/i18n/cs.d.ts +83 -0
- package/dist/i18n/da.d.ts +83 -0
- package/dist/i18n/de.d.ts +83 -0
- package/dist/i18n/en.d.ts +83 -0
- package/dist/i18n/es.d.ts +83 -0
- package/dist/i18n/fr.d.ts +83 -0
- package/dist/i18n/hr.d.ts +83 -0
- package/dist/i18n/id.d.ts +83 -0
- package/dist/i18n/it.d.ts +83 -0
- package/dist/i18n/ja.d.ts +83 -0
- package/dist/i18n/ko.d.ts +83 -0
- package/dist/i18n/pl.d.ts +83 -0
- package/dist/i18n/pt.d.ts +83 -0
- package/dist/i18n/ru.d.ts +83 -0
- package/dist/i18n/sr.d.ts +83 -0
- package/dist/i18n/sv.d.ts +83 -0
- package/dist/i18n/tr.d.ts +83 -0
- package/dist/i18n/uk.d.ts +83 -0
- package/dist/i18n/zh-CN.d.ts +83 -0
- package/dist/i18n/zh-TW.d.ts +83 -0
- package/dist/i18n.d.ts +3618 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -2
- package/dist/keybind.d.ts +121 -0
- package/dist/meta.d.ts +83 -0
- package/dist/nav.d.ts +29 -0
- package/dist/notify.service.d.ts +149 -1
- package/dist/project-root.d.ts +1 -1
- package/dist/reload.service.d.ts +57 -0
- package/dist/sessionLedger.d.ts +11 -6
- package/dist/settings.component.d.ts +133 -8
- package/dist/status.service.d.ts +30 -2
- package/dist/statusLine.d.ts +27 -0
- package/dist/update.d.ts +19 -5
- package/dist/update.service.d.ts +29 -20
- package/dist/viewPanel.d.ts +13 -3
- package/hooks/agentdeck-codex-notify.ps1 +5 -2
- package/hooks/agentdeck-label.js +1 -1
- package/hooks/agentdeck-notify.ps1 +32 -12
- package/hooks/agentdeck-statusline.mjs +351 -0
- package/package.json +4 -2
- package/updater/agentdeck-update.ps1 +50 -27
- package/dist/index.js.map +0 -1
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AfterViewInit, ElementRef, NgZone, OnDestroy } from '@angular/core';
|
|
2
|
+
import { ConfigService, HotkeysService, LocaleService, PlatformService } from 'tabby-core';
|
|
3
|
+
import { KeyItem, BindingPlan } from './keybind';
|
|
2
4
|
import { AgentDeckRecoveryService } from './recovery.service';
|
|
3
5
|
import { AgentDeckUpdateService } from './update.service';
|
|
6
|
+
import { AgentDeckDevReloadService } from './devReload.service';
|
|
4
7
|
/**
|
|
5
8
|
* 설정 > AgentDeck 탭.
|
|
6
9
|
*
|
|
7
10
|
* 사용자가 실제로 바꿀 일이 있는 것만 노출한다 — 레이아웃 on/off, 기동 시 탭 복원 끄기,
|
|
8
|
-
*
|
|
11
|
+
* 지난 세션 이어받기, 에이전트 상태 연동, 작업 루트 프로필, 문제 신고, (개발 설치) 개발자 옵션.
|
|
12
|
+
* 2026-09-14 유저 지시로 자동 업데이트·이어받기 개수·사이드바 목록·세션 그룹·미리보기 패널 절을
|
|
13
|
+
* 템플릿에서 주석 처리했다 — "알아서 세팅해 두고, 고치고 싶으면 설정 파일을 직접 고칠 일".
|
|
14
|
+
* 같은 날 `"지금 이 탭" 줄`(metaLine)과 `통보 채널`(notifyChannel) 스위치도 걷어냈다 —
|
|
15
|
+
* 앞의 것은 statusLine 을 연결해 놓고 줄만 끄는 조합에 뜻이 없고, 뒤의 것은 훅 설치/제거가
|
|
16
|
+
* 정답을 이미 알고 있다(`syncNotifyChannel`). 두 값 모두 config 에는 남아 있다.
|
|
17
|
+
* 상태 훅과 statusLine 을 따로 걸던 줄 셋도 `Claude 연동` · `Codex 연동` 둘로 합쳤다
|
|
18
|
+
* (`toggleClaude`) — 사용자가 정하는 것은 "이 에이전트를 사이드바에 붙일까" 하나뿐이다.
|
|
19
|
+
* 값은 `config.ts` 기본값이 그대로 쓰인다. 아래 옛 설명은 그 절들이 있던 시절 기준이다.
|
|
9
20
|
* 나머지(화면비, 사이드바 폭, 투명도, 상태 감지, 붙여넣기/줄바꿈 가로채기, 렌더 보정)는
|
|
10
21
|
* 손댈 이유가 거의 없고 잘못 만지면 화면이 깨지는 값이라 `config.ts` 의 기본값으로 고정하고
|
|
11
22
|
* 설정 창에서는 뺐다. 정말 필요하면 설정 파일의 `agentDeck.*` 를 직접 고친다.
|
|
@@ -28,19 +39,37 @@ import { AgentDeckUpdateService } from './update.service';
|
|
|
28
39
|
* (deck.service.ts `navCanFocus`/`moveNav`, notify.service `subagentEnabled`)
|
|
29
40
|
* 토글하면 바로 듣는다 — 그래서 재시작 안내를 붙이지 않았다.
|
|
30
41
|
*/
|
|
31
|
-
export declare class AgentDeckSettingsTabComponent {
|
|
42
|
+
export declare class AgentDeckSettingsTabComponent implements AfterViewInit, OnDestroy {
|
|
32
43
|
config: ConfigService;
|
|
33
44
|
recovery: AgentDeckRecoveryService;
|
|
34
45
|
private update;
|
|
35
46
|
private platform;
|
|
47
|
+
devReload: AgentDeckDevReloadService;
|
|
48
|
+
private host;
|
|
49
|
+
private locale;
|
|
50
|
+
private hotkeys;
|
|
51
|
+
private zone;
|
|
36
52
|
/** 이 환경에서 훅을 걸 수 있나 (통보 스크립트가 PowerShell 이라 Windows 전용) */
|
|
37
53
|
hooksSupported: boolean;
|
|
38
54
|
/** 지금 걸려 있나 — 파일을 매번 읽지 않도록 화면용으로 들고 있는다 */
|
|
39
55
|
hooksOn: boolean;
|
|
40
56
|
codexHooksOn: boolean;
|
|
41
57
|
codexHookError: string;
|
|
58
|
+
/**
|
|
59
|
+
* Codex 가 `/hooks` 에서 **꺼 둔** 우리 훅 (`config.toml` 의 `[hooks.state]`).
|
|
60
|
+
*
|
|
61
|
+
* 설치 여부와 따로 읽는다 — `hooks.json` 에 우리 항목이 있어도 Codex 가 꺼 두면
|
|
62
|
+
* 한 건도 오지 않는데, 그때 화면이 `연동됨` 이라고 말하면 사용자는 원인을 볼 수 없다
|
|
63
|
+
* (2026-09-14 실측: 상태 보고 폴더에 codex 파일 0건).
|
|
64
|
+
*/
|
|
65
|
+
codexOff: string[];
|
|
42
66
|
/** 훅을 쓰다 실패한 이유 — 조용히 삼키면 사용자는 버튼이 먹통인 줄 안다 */
|
|
43
67
|
hookError: string;
|
|
68
|
+
/** statusLine 래퍼를 걸 수 있나 (스크립트가 패키지에 들어 있나) */
|
|
69
|
+
statusLineSupported: boolean;
|
|
70
|
+
/** 지금 감싸져 있나 */
|
|
71
|
+
statusLineOn: boolean;
|
|
72
|
+
statusLineError: string;
|
|
44
73
|
/** 신고 안내에 그대로 보여줄 진단 로그 경로 */
|
|
45
74
|
diagPath: string;
|
|
46
75
|
/** 신고 본문에 적을 버전 — 사용자가 어느 버전을 쓰는지 스스로 알 방법이 없었다 */
|
|
@@ -51,11 +80,92 @@ export declare class AgentDeckSettingsTabComponent {
|
|
|
51
80
|
diagBundlePath: string;
|
|
52
81
|
/** 묶음을 만들다 실패한 이유 */
|
|
53
82
|
diagError: string;
|
|
83
|
+
/**
|
|
84
|
+
* Claude 연동이 **온전히** 걸려 있나 — 상태 훅 + statusLine 둘 다.
|
|
85
|
+
*
|
|
86
|
+
* 반쪽(훅만 걸림)은 `false` 다. 그래야 버튼이 `설치` 로 남아 한 번 더 누르면 모자란 쪽이
|
|
87
|
+
* 채워진다 — `연동됨` 이라고 해 놓고 막대가 안 뜨면 사용자가 손댈 곳이 없어진다.
|
|
88
|
+
* statusLine 스크립트가 없는 설치본에서는 훅만으로 판정한다.
|
|
89
|
+
*/
|
|
90
|
+
get claudeOn(): boolean;
|
|
91
|
+
/** 꺼진 훅 이름을 사람이 `/hooks` 에서 찾을 수 있는 표기로 */
|
|
92
|
+
get codexOffLabels(): string;
|
|
93
|
+
/** 두 갈래(훅·statusLine) 중 실패한 것만 모아 한 줄로 */
|
|
94
|
+
get claudeError(): string;
|
|
54
95
|
/** `지금 확인` 이 도는 동안 버튼을 잠근다 (레지스트리 응답까지 몇 초) */
|
|
55
96
|
updateBusy: boolean;
|
|
56
97
|
/** 마지막 확인 결과 한 줄 */
|
|
57
98
|
updateMsg: string;
|
|
58
|
-
|
|
99
|
+
/** 늘어놓는 항목 — README 첫 화면의 표와 같은 순서 */
|
|
100
|
+
keyItems: KeyItem[];
|
|
101
|
+
/** 지금 키를 받고 있는 항목 id */
|
|
102
|
+
capturing: string | null;
|
|
103
|
+
/** 겹침이 나와 확인을 기다리는 계획 */
|
|
104
|
+
pending: {
|
|
105
|
+
plan: BindingPlan;
|
|
106
|
+
names: string;
|
|
107
|
+
} | null;
|
|
108
|
+
/** 한 줄 안내(숫자로 끝나야 한다 등) 와 그 대상 */
|
|
109
|
+
keyMsg: string;
|
|
110
|
+
keyMsgFor: string;
|
|
111
|
+
/** id → 사람이 읽는 이름. Tabby 순정·다른 플러그인 것까지 HotkeysService 가 모아 준다 */
|
|
112
|
+
private hotkeyNames;
|
|
113
|
+
private captureHandler;
|
|
114
|
+
private readonly keyPlatform;
|
|
115
|
+
/** 600px 제한을 풀어 둔 Tabby 의 본문 엘리먼트 (나갈 때 되돌린다) */
|
|
116
|
+
private widened;
|
|
117
|
+
/** 풀기 전의 값 — 인라인으로 없던 상태면 빈 문자열이라 그대로 되돌려진다 */
|
|
118
|
+
private prevMaxWidth;
|
|
119
|
+
/** 지금 그릴 언어 — Tabby 의 `설정 > 응용 프로그램 > 언어` 를 따라간다 */
|
|
120
|
+
private lang;
|
|
121
|
+
private localeSub;
|
|
122
|
+
constructor(config: ConfigService, recovery: AgentDeckRecoveryService, update: AgentDeckUpdateService, platform: PlatformService, devReload: AgentDeckDevReloadService, host: ElementRef<HTMLElement>, locale: LocaleService | null, hotkeys: HotkeysService | null, zone: NgZone);
|
|
123
|
+
/**
|
|
124
|
+
* 문구 하나. 표는 `i18n.ts` 에 있고 여기서는 언어만 얹는다.
|
|
125
|
+
*
|
|
126
|
+
* 템플릿에서 매 변경감지마다 불리는 함수다 — 표 조회 + 정규식 치환뿐이라 값이 싸고,
|
|
127
|
+
* 캐시를 두면 언어가 바뀌었을 때 무효화할 자리가 하나 더 생긴다.
|
|
128
|
+
*/
|
|
129
|
+
t(key: string, params?: Record<string, string | number>): string;
|
|
130
|
+
/**
|
|
131
|
+
* 설정 본문의 600px 제한을 **이 탭에서만** 푼다.
|
|
132
|
+
*
|
|
133
|
+
* Tabby 는 모든 설정 탭 본문에 `settings-tab-body { max-width: 600px }` 를 건다
|
|
134
|
+
* (tabby-settings 컴포넌트 styles). 넓은 창에서는 오른쪽이 통째로 놀게 되는데,
|
|
135
|
+
* 그 규칙은 컴포넌트 스타일이라 우리 스타일시트로는 못 이긴다(우선순위가 아니라 `:host`
|
|
136
|
+
* 범위 문제다 — 우리 CSS 는 그 엘리먼트를 선택할 수는 있어도 어느 탭의 것인지 모른다).
|
|
137
|
+
* 그래서 **우리 엘리먼트에서 위로 찾아** 인라인으로 푼다. 화면을 어떻게 채울지는
|
|
138
|
+
* `styles.scss` 의 `agentdeck-settings-tab` 격자가 정한다.
|
|
139
|
+
*
|
|
140
|
+
* 되돌리기가 필요한 이유 — 이 엘리먼트는 다른 설정 탭과 **돌려 쓰일 수 있다**.
|
|
141
|
+
* 풀어 둔 채 나가면 남의 탭이 우리 때문에 넓어진다.
|
|
142
|
+
*/
|
|
143
|
+
ngAfterViewInit(): void;
|
|
144
|
+
ngOnDestroy(): void;
|
|
145
|
+
/** 이 항목이 지금 쓰는 키들 (사람 표기) */
|
|
146
|
+
keysOf(it: KeyItem): string[];
|
|
147
|
+
isPending(it: KeyItem): boolean;
|
|
148
|
+
isDefault(it: KeyItem): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* 키 받기 시작. **window 캡처 단계**에 건다 — 그래야 document 에 걸린 Tabby 의 HotkeysService
|
|
151
|
+
* 리스너와 우리 deck.service 의 캡처 리스너(Ctrl+V · Shift+Enter)보다 먼저 받고, 전파를 끊어
|
|
152
|
+
* 그 키가 진짜로 실행되는 일(검색창이 열리거나 터미널에 붙여지는 일)을 막는다.
|
|
153
|
+
* 수식키만 눌린 이벤트는 그냥 흘린다 — 아직 조합 중이고, 막으면 Shift 추적이 어긋난다.
|
|
154
|
+
*/
|
|
155
|
+
startCapture(it: KeyItem): void;
|
|
156
|
+
stopCapture(): void;
|
|
157
|
+
/** 겹침을 알고도 쓰기 — 겹치는 쪽에서 그 키를 뗀다 (한 번 눌러 둘이 도는 상태를 남기지 않는다) */
|
|
158
|
+
confirmPending(): void;
|
|
159
|
+
resetKeys(it: KeyItem): void;
|
|
160
|
+
private commit;
|
|
161
|
+
/**
|
|
162
|
+
* 기본값 표 — 순정 항목(split-right · split-bottom …)은 Tabby 의 플랫폼 기본표에서,
|
|
163
|
+
* 우리 항목과 close-pane 은 `defaultHotkeys()` 에서. 순정 기본표는 ConfigService 가 들고 있다
|
|
164
|
+
* (`getDefaults()`), 없으면 우리 표만으로 간다(그때 순정 항목의 `기본값` 은 비운다).
|
|
165
|
+
*/
|
|
166
|
+
private keyDefaults;
|
|
167
|
+
private nameOf;
|
|
168
|
+
private loadHotkeyNames;
|
|
59
169
|
/**
|
|
60
170
|
* 사람이 직접 누른 확인. `force` 라 간격과 `다시 묻지 않기` 를 건너뛴다 —
|
|
61
171
|
* 눌렀다는 것 자체가 "지금 알고 싶다" 는 뜻이다.
|
|
@@ -63,13 +173,28 @@ export declare class AgentDeckSettingsTabComponent {
|
|
|
63
173
|
checkUpdate(): Promise<void>;
|
|
64
174
|
private refreshHooks;
|
|
65
175
|
/**
|
|
66
|
-
*
|
|
176
|
+
* Claude 연동을 걸거나 뗀다 — **상태 훅과 statusLine 을 한 덩이로** 다룬다.
|
|
67
177
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
178
|
+
* 따로 두었더니 "훅만 걸고 statusLine 은 안 건 상태" 가 흔하게 나왔고, 그 화면은
|
|
179
|
+
* 상태는 뜨는데 모델·한도 막대만 없어서 사용자가 원인을 못 짚는다 (2026-09-14 유저 지시:
|
|
180
|
+
* "status line 은 같은 취급 해"). 사용자가 정하는 것은 **이 에이전트를 사이드바에 붙일까**
|
|
181
|
+
* 하나뿐이고, 그걸 이루는 파일이 둘인 것은 우리 사정이다.
|
|
182
|
+
*
|
|
183
|
+
* 해제는 **원래 statusLine 을 제자리에 돌려놓는다** — 우리 것이 아니면 아무것도 건드리지
|
|
184
|
+
* 않는다(`unwrapStatusLine`). 되돌릴 것이 없던 사용자는 statusLine 키 자체가 지워진다.
|
|
185
|
+
*
|
|
186
|
+
* 한쪽만 실패해도 나머지는 계속 간다 — 둘 다 시도한 뒤 모아서 한 줄로 알린다.
|
|
187
|
+
* 그래야 "훅은 걸렸는데 statusLine 이 안 걸린" 반쪽 상태를 사용자가 볼 수 있다.
|
|
70
188
|
*/
|
|
71
|
-
|
|
189
|
+
toggleClaude(): void;
|
|
72
190
|
toggleCodexHooks(): void;
|
|
191
|
+
/**
|
|
192
|
+
* 통보 채널을 훅 설치 상태에 맞춘다 — 하나라도 걸려 있으면 열고, 하나도 없으면 닫는다.
|
|
193
|
+
*
|
|
194
|
+
* `refreshHooks()` **뒤에** 부른다. 실패해서 설치가 안 됐을 수도 있으므로 버튼을 누른
|
|
195
|
+
* 의도가 아니라 **파일을 다시 읽은 결과**를 기준으로 삼는다.
|
|
196
|
+
*/
|
|
197
|
+
private syncNotifyChannel;
|
|
73
198
|
/**
|
|
74
199
|
* OS 폴더 선택 창을 띄워 작업 폴더를 고른다.
|
|
75
200
|
*
|
package/dist/status.service.d.ts
CHANGED
|
@@ -62,6 +62,34 @@ export declare class WorkStatusService {
|
|
|
62
62
|
* 탭 목록 변화는 사람이 만드는 빈도(초당 몇 번이 최대)라 문제가 되지 않는다.
|
|
63
63
|
*/
|
|
64
64
|
sweep(alive: readonly BaseTabComponent[]): number;
|
|
65
|
-
/**
|
|
66
|
-
|
|
65
|
+
/**
|
|
66
|
+
* 사이드바가 매 틱 호출 — idle 전환만 담당하고 화면 갱신 필요 여부를 돌려준다.
|
|
67
|
+
*
|
|
68
|
+
* `staleAfterMs` 는 **고정(pinned)된 진행중 탭의 유예 시간**이다. 0 이면 예전과 같다.
|
|
69
|
+
*
|
|
70
|
+
* 왜 필요한가 — 훅에는 "세션이 끝났다" 가 없다. 설치하는 이벤트는 `UserPromptSubmit`
|
|
71
|
+
* `PostToolUse` `Notification` `Stop` 뿐이라(`claudeHooks.ts` PLAN), 세션이 정상 종료
|
|
72
|
+
* 밖으로 사라지면(Ctrl+C 로 끊음 · 창을 닫음 · CLI 크래시 · Tabby 가 탭을 복원했는데 그
|
|
73
|
+
* 세션은 이미 죽음) **마지막으로 온 `running` 이 영원히 남는다.** `setManual` 이 pinned 를
|
|
74
|
+
* 세우고 여기서 pinned 를 그냥 건너뛰었으므로, 그 탭에는 배지가 내려올 길이 한 개도 없었다
|
|
75
|
+
* (`setAuto` 는 pinned 를 무시하고 `resume` 은 waiting 전용이며 `unpin` 은 사람이 메뉴에서
|
|
76
|
+
* 고르는 것뿐이다). 사이드바에 "작업중" 이 박혀 있는 탭이 실은 아무것도 안 하고 있는 모양이
|
|
77
|
+
* 이 구멍이다.
|
|
78
|
+
*
|
|
79
|
+
* **살아 있다는 증거는 셋 다 인정한다** — 훅 보고(`since` 는 `setManual` 이 매 보고마다
|
|
80
|
+
* 새로 찍는다) · PTY 출력(`lastOutput`) · 작업중 신호(`lastBusy`). 셋 중 가장 최근 것에서
|
|
81
|
+
* 잰다. 그래서 오래 도는 빌드처럼 화면이 조용해 보이는 작업도, 스피너든 훅이든 하나만
|
|
82
|
+
* 살아 있으면 내려가지 않는다.
|
|
83
|
+
*
|
|
84
|
+
* **대상은 `running`·`done`·`error` 뿐이다**(`STALE_STATUSES`). 푸는 것은 **고정**이고,
|
|
85
|
+
* `진행중` 일 때만 이어서 `idle` 로 내린다 — 기척이 끊긴 진행중은 거짓말이지만, `완료`·`오류`
|
|
86
|
+
* 는 실제로 있었던 일이라 글자를 지울 이유가 없다. 그 둘에서 필요한 것은 **다음 출력이
|
|
87
|
+
* 배지를 되살릴 수 있게 하는 것**뿐이다: CLI 를 끄고 같은 탭에서 셸을 쓰면 `setAuto` 가
|
|
88
|
+
* 계속 들어오는데 pinned 라 전부 무시돼 배지가 `완료` 에 박혀 있었다.
|
|
89
|
+
*
|
|
90
|
+
* 고정을 반드시 푸는 이유도 같다. idle 로만 내리고 pinned 를 남기면 그 탭은 이어지는
|
|
91
|
+
* 자동 감지를 계속 무시한다 — 구멍을 반만 막는 셈이다. 다음 훅 보고가 오면 `setManual`
|
|
92
|
+
* 이 다시 고정한다.
|
|
93
|
+
*/
|
|
94
|
+
tickIdle(tab: BaseTabComponent, idleAfterMs: number, staleAfterMs?: number): boolean;
|
|
67
95
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** 원래 걸려 있던 statusLine 을 옮겨 두는 자리 (스크립트도 같은 경로를 읽는다) */
|
|
2
|
+
export declare function innerConfigPath(): string;
|
|
3
|
+
/** statusLine 스크립트의 실제 경로. dist/index.js 기준으로 한 단계 위가 패키지 뿌리다 */
|
|
4
|
+
export declare function statusLineScriptPath(): string;
|
|
5
|
+
export declare function statusLineSupported(): boolean;
|
|
6
|
+
/** 우리 래퍼가 걸려 있나 — 파일을 읽지 않는 순수 판정 */
|
|
7
|
+
export declare function isWrapped(settings: any): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* 설정 객체의 statusLine 을 우리 래퍼로 갈아 끼운 새 객체를 돌려준다 (원본은 건드리지 않는다).
|
|
10
|
+
*
|
|
11
|
+
* 이미 우리 것이면 명령줄만 지금 경로로 갱신한다 — 그때 안쪽 설정은 **그대로 둔다**
|
|
12
|
+
* (다시 옮기면 안쪽에 우리 자신이 들어가 고리가 된다).
|
|
13
|
+
*/
|
|
14
|
+
export declare function wrapStatusLine(settings: any, script: string): {
|
|
15
|
+
next: any;
|
|
16
|
+
inner: any | null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* 우리 래퍼를 걷어내고 안쪽 설정을 제자리에 돌려놓은 새 객체.
|
|
20
|
+
* 안쪽이 없었으면(원래 statusLine 이 없던 사용자) 키 자체를 지운다.
|
|
21
|
+
*/
|
|
22
|
+
export declare function unwrapStatusLine(settings: any, inner: any | null): any;
|
|
23
|
+
export declare function statusLineInstalled(): boolean;
|
|
24
|
+
/** 지금 안쪽에 무엇이 있는지 (설정 창이 사용자에게 보여 준다) */
|
|
25
|
+
export declare function innerCommand(): string;
|
|
26
|
+
export declare function installStatusLine(): void;
|
|
27
|
+
export declare function uninstallStatusLine(): void;
|
package/dist/update.d.ts
CHANGED
|
@@ -4,11 +4,17 @@
|
|
|
4
4
|
* 네트워크·fs·타이머는 여기 없다 (`group.ts`·`sessionLedger.ts` 와 같은 이유). 이 파일은
|
|
5
5
|
* "무엇을 근거로 결정하는가" 만 정하고, 실제로 묻고 설치하는 것은 `update.service.ts` 다.
|
|
6
6
|
*
|
|
7
|
-
* ##
|
|
7
|
+
* ## 적용은 **창만 새로 고쳐서** 한다 (Tabby 를 닫지 않는다)
|
|
8
8
|
*
|
|
9
|
-
* Tabby 는 플러그인을
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Tabby 는 플러그인을 기동할 때 `dist/index.js` 를 한 번 읽는다. 그래서 예전에는
|
|
10
|
+
* "닫았다가 설치하고 다시 띄운다" 였는데, 그 사이 열린 탭과 돌고 있는 세션이 전부 끊겼다.
|
|
11
|
+
* 지금은 `reload.service.ts` 가 **renderer 만 리로드**하면서 탭을 복구 토큰으로 되살린다 —
|
|
12
|
+
* pty 는 main 프로세스가 쥐고 있어 창이 다시 떠도 살아 있다. 리로드는 디스크를 다시 읽으므로
|
|
13
|
+
* 새로 깔린 코드가 그대로 뜬다.
|
|
14
|
+
*
|
|
15
|
+
* 그래서 설치도 **Tabby 가 떠 있는 채로** 한다. 로드된 `.js` 는 Windows 가 잡고 있지 않으므로
|
|
16
|
+
* (require 는 읽고 바로 닫는다 — 2026-09-14 실측: 요구된 파일이 든 폴더를 그대로 지울 수 있었다)
|
|
17
|
+
* npm 이 플러그인 폴더를 갈아끼워도 돌고 있는 코드는 메모리에 그대로다.
|
|
12
18
|
*/
|
|
13
19
|
/** npm 레지스트리 기본값. 사내 미러를 쓰는 사람은 설정으로 바꾼다 */
|
|
14
20
|
export declare const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -58,7 +64,15 @@ export declare function canSelfUpdate(marks: {
|
|
|
58
64
|
hasWebpackConfig: boolean;
|
|
59
65
|
isLink: boolean;
|
|
60
66
|
}): boolean;
|
|
61
|
-
|
|
67
|
+
/**
|
|
68
|
+
* 설치가 실제로 끝났나 — **디스크의 버전**으로 판정한다.
|
|
69
|
+
*
|
|
70
|
+
* npm 이 0 으로 끝나도 폴더가 옛것 그대로일 수 있다(캐시·권한·프록시). 그 상태로 창을
|
|
71
|
+
* 리로드하면 "업데이트했다" 고 해 놓고 같은 버전이 다시 뜬다. 그래서 갈아끼우기 직전에
|
|
72
|
+
* `<plugins>/node_modules/<pkg>/package.json` 의 버전을 읽어 기대값과 맞춰 본다.
|
|
73
|
+
*/
|
|
74
|
+
export declare function installSucceeded(expected: string, onDisk: unknown): boolean;
|
|
75
|
+
export type UpdateOutcome = 'disabled' | 'too-soon' | 'dev-install' | 'unknown' | 'up-to-date' | 'update-ready' | 'skipped' | 'postponed' | 'installing' | 'installed' | 'failed';
|
|
62
76
|
/**
|
|
63
77
|
* 확인 결과로 무엇을 할지 — 순수 결정.
|
|
64
78
|
*
|
package/dist/update.service.d.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { AppService, ConfigService, NotificationsService, PlatformService } from 'tabby-core';
|
|
2
|
+
import { AgentDeckReloadService } from './reload.service';
|
|
2
3
|
import { UpdateOutcome } from './update';
|
|
3
4
|
/**
|
|
4
|
-
* npm 레지스트리를 보고 **Tabby 를
|
|
5
|
+
* npm 레지스트리를 보고 **Tabby 를 띄운 채로** 새 버전을 깐다.
|
|
5
6
|
*
|
|
6
7
|
* 흐름은 넷이다 —
|
|
7
8
|
* ① 기동하고 잠시 뒤 레지스트리에 묻는다
|
|
8
9
|
* ② 새 버전이 있으면 **물어본다** (`지금 업데이트` / `나중에` / `다시 묻지 않기`)
|
|
9
|
-
* ③ 승낙하면 설치 스크립트를
|
|
10
|
-
* ④
|
|
10
|
+
* ③ 승낙하면 설치 스크립트를 임시 폴더에서 돌린다 — **Tabby 는 그대로 떠 있다**
|
|
11
|
+
* ④ 디스크 버전을 확인하고 **창만 새로 고친다**(`reload.service`) — 탭과 세션은 살아남는다
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* **예전에는 닫았다 켰다.** 돌고 있는 플러그인은 자기를 갈아끼울 수 없고(모듈이 이미
|
|
14
|
+
* 메모리에 있다) Windows 가 파일을 잡고 있을까 걱정했기 때문인데, 둘 다 지금은 해결됐다 —
|
|
15
|
+
* 적용은 renderer 리로드가 하고(pty 는 main 이 쥐고 있어 살아남는다), 로드된 `.js` 는
|
|
16
|
+
* 잠기지 않는다(2026-09-14 실측: require 한 파일이 든 폴더를 그대로 지울 수 있었다).
|
|
17
|
+
* 남은 조건은 하나 — **설치 스크립트는 패키지 밖에서 돌려야 한다**(제 발밑을 npm 이 갈아엎는다).
|
|
16
18
|
*
|
|
17
19
|
* **개발 설치에는 설치하지 않는다.** 플러그인 폴더의 `tabby-agentdeck` 은 개발 중에는 소스
|
|
18
20
|
* 트리를 가리키는 링크이고(`plugins/package.json` 에 `file:D:/Project/tabby-agentdeck` 로 적힌다),
|
|
@@ -23,7 +25,8 @@ export declare class AgentDeckUpdateService {
|
|
|
23
25
|
private config;
|
|
24
26
|
private platform;
|
|
25
27
|
private notifications;
|
|
26
|
-
|
|
28
|
+
private reloader;
|
|
29
|
+
constructor(app: AppService, config: ConfigService, platform: PlatformService, notifications: NotificationsService, reloader: AgentDeckReloadService);
|
|
27
30
|
init(): void;
|
|
28
31
|
/**
|
|
29
32
|
* 한 번 확인하고, 새 버전이면 물어본 뒤 설치를 건다.
|
|
@@ -32,26 +35,32 @@ export declare class AgentDeckUpdateService {
|
|
|
32
35
|
* (사람이 직접 눌렀으니 그 순간만큼은 묻는 게 맞다). 기능 자체를 끈 것은 존중한다.
|
|
33
36
|
*/
|
|
34
37
|
run(reason: string, force?: boolean): Promise<UpdateOutcome>;
|
|
35
|
-
/** ② 물어보고 ③
|
|
38
|
+
/** ② 물어보고 ③ 제자리에 깐 뒤 ④ 창만 새로 고친다 */
|
|
36
39
|
private confirmAndInstall;
|
|
37
40
|
/**
|
|
38
|
-
*
|
|
41
|
+
* ③ 제자리에 깔고 ④ 창을 새로 고친다 — **묻는 단계는 이미 지났다.**
|
|
39
42
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
43
|
+
* 따로 떼어 둔 이유는 회귀 때문이다. `showMessageBox` 는 OS 의 모달이라 CDP 로 누를 수
|
|
44
|
+
* 없어서, 묻기와 한 덩어리면 이 길을 자동으로 한 번도 못 밟아 본다
|
|
45
|
+
* (`tools/regress-update.js` 가 여기를 부른다).
|
|
42
46
|
*
|
|
43
|
-
* `
|
|
44
|
-
*
|
|
47
|
+
* `spec` 은 npm 에 건넬 대상을 바꾼다 — **회귀 전용**이다. 기본값(`tabby-agentdeck@<버전>`)은
|
|
48
|
+
* 레지스트리를 보지만, 회귀는 *지금 이 소스로 만든 tgz* 를 깔아야 한다. 이미 배포된 버전을
|
|
49
|
+
* 깔면 그 안에는 이 코드가 없어서, 리로드 뒤 복구를 맡을 쪽이 옛 코드가 된다.
|
|
45
50
|
*/
|
|
46
|
-
|
|
51
|
+
installAndReload(current: string, latest: string, spec?: string): Promise<UpdateOutcome>;
|
|
47
52
|
/**
|
|
48
|
-
*
|
|
53
|
+
* 설치 스크립트를 **패키지 밖에서** 돌리고 끝날 때까지 기다린다.
|
|
54
|
+
*
|
|
55
|
+
* 임시 폴더로 복사하는 이유: 이 스크립트는 곧 npm 이 통째로 갈아엎을 폴더 안에 산다.
|
|
56
|
+
* 제자리에서 돌리면 자기 발밑이 사라진다.
|
|
49
57
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* `ppid` 를 못 읽는 환경(테스트 하네스 등)에서는 0 을 주고, 스크립트가 옛 방식으로 물러난다.
|
|
58
|
+
* `-InPlace` 는 "Tabby 종료를 기다리지도, 다시 띄우지도 말라" 는 뜻이다. 기다릴 이유가
|
|
59
|
+
* 없어졌고(파일이 잠기지 않는다), 다시 띄울 이유도 없다(닫지 않는다).
|
|
53
60
|
*/
|
|
54
|
-
private
|
|
61
|
+
private runUpdater;
|
|
62
|
+
/** 플러그인 폴더에 **실제로 깔려 있는** 버전 — 설치 성공 판정의 근거 */
|
|
63
|
+
private installedVersion;
|
|
55
64
|
/** `<Tabby 설정폴더>/plugins` — npm 이 `--prefix` 로 받을 경로 */
|
|
56
65
|
private pluginsDir;
|
|
57
66
|
/**
|
package/dist/viewPanel.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export declare class ViewPanel {
|
|
|
77
77
|
* `변경` 목록의 범위. `session` = 이번 세션이 만진 파일만, `all` = 저장소의 커밋 안 된 전부.
|
|
78
78
|
*
|
|
79
79
|
* **기본이 `session` 인 이유** — `git diff HEAD` 는 저장소 사정에 따라 수백 개가 나온다
|
|
80
|
-
* (
|
|
80
|
+
* (큰 워크스페이스 위의 git 오버레이에서는 흔한 일이다). 그 안에서 방금 에이전트가
|
|
81
81
|
* 고친 것을 찾을 수는 없다. 설정에 저장하지 않는다 — 모드(`file`/`diff`)와 같은 이유로
|
|
82
82
|
* 기동 때는 늘 기본값에서 시작한다.
|
|
83
83
|
*/
|
|
@@ -204,8 +204,18 @@ export declare class ViewPanel {
|
|
|
204
204
|
* (사이드바 `dock.ts` 가 절대 거리로 재는 것은 거기서만 맞다 — 사이드바는 창에 딱 붙는다.)
|
|
205
205
|
*/
|
|
206
206
|
private installResize;
|
|
207
|
-
/**
|
|
208
|
-
|
|
207
|
+
/**
|
|
208
|
+
* deck 의 relayout 이 정해 준 자리에 앉는다 (사이드바와 같은 방식으로 절대 배치).
|
|
209
|
+
*
|
|
210
|
+
* 네 값 모두 **컨테이닝 블록 좌표로 이미 풀린 실좌표**다 — 예전처럼 `right: outerOffset` 으로
|
|
211
|
+
* 가장자리에 붙이면 그 가장자리가 화면 밖일 때 패널도 같이 밀려난다 (deck.service anchorOrigin).
|
|
212
|
+
*/
|
|
213
|
+
layout(side: ViewSide, rect: {
|
|
214
|
+
left: number;
|
|
215
|
+
top: number;
|
|
216
|
+
width: number;
|
|
217
|
+
height: number;
|
|
218
|
+
}): void;
|
|
209
219
|
private togglePathRow;
|
|
210
220
|
private setPathNote;
|
|
211
221
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Codex lifecycle -> existing AgentDeck file/TCP transport. Never emit model context.
|
|
1
|
+
# Codex lifecycle -> existing AgentDeck file/TCP transport. Never emit model context.
|
|
2
2
|
$ErrorActionPreference = 'Stop'
|
|
3
3
|
try {
|
|
4
4
|
$reader = New-Object System.IO.StreamReader([Console]::OpenStandardInput(), (New-Object System.Text.UTF8Encoding($false)))
|
|
@@ -18,7 +18,10 @@ try {
|
|
|
18
18
|
if ($status -eq 'waiting') {
|
|
19
19
|
$hook | Add-Member -NotePropertyName message -NotePropertyValue ('Approval: ' + [string]$hook.tool_name) -Force
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
# JSON is passed as -HookJson, not piped: a script that accepts pipeline input makes the host pre-read
|
|
22
|
+
# stdin in the console codepage (CP949), which corrupts non-ASCII text for external callers.
|
|
23
|
+
$json = $hook | ConvertTo-Json -Depth 30 -Compress
|
|
24
|
+
& (Join-Path $PSScriptRoot 'agentdeck-notify.ps1') -Status $status -Agent codex -HookJson $json
|
|
22
25
|
} catch {
|
|
23
26
|
# Status reporting must not block the agent if Tabby is unavailable.
|
|
24
27
|
exit 0
|
package/hooks/agentdeck-label.js
CHANGED
|
@@ -33,10 +33,13 @@
|
|
|
33
33
|
# "Stop": -Status done 답이 끝났다
|
|
34
34
|
# "StopFailure": -Status error 턴이 실패로 끝났다 (rate_limit 이면 limited)
|
|
35
35
|
#>
|
|
36
|
-
param(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
param(
|
|
37
|
+
# 어느 CLI 가 보냈나. **이 값이 에이전트 판정의 1순위 근거다** — 플러그인이 프로세스 이름·
|
|
38
|
+
# 명령줄·탭 제목·화면 문구로 추측하던 것(deck.service `detectAgentApp`)을 이 한 줄이 대신한다.
|
|
39
|
+
# 자기가 누구인지는 훅을 부르는 쪽이 아는 사실이지 추측할 일이 아니다 (cwd·tabId 와 같은 이치).
|
|
40
|
+
[ValidateSet('claude', 'codex', 'gemini')]
|
|
41
|
+
[string]$Agent = 'claude',
|
|
42
|
+
|
|
40
43
|
[ValidateSet('running', 'waiting', 'limited', 'done', 'error', 'idle')]
|
|
41
44
|
[string]$Status,
|
|
42
45
|
|
|
@@ -59,17 +62,30 @@ param(
|
|
|
59
62
|
# 없앤 순간 "알 수 없는 인자" 로 훅이 통째로 실패한다
|
|
60
63
|
[switch]$PromptAsLabel,
|
|
61
64
|
|
|
62
|
-
[int]$MaxLabel = 40
|
|
65
|
+
[int]$MaxLabel = 40,
|
|
66
|
+
|
|
67
|
+
# 같은 PowerShell 안에서 부르는 래퍼(agentdeck-codex-notify.ps1)가 훅 JSON 을 넘기는 자리.
|
|
68
|
+
# 파이프(`$json | & notify.ps1`)로 넘기지 않는 이유는 아래 stdin 주석 — 스크립트가 파이프 입력을
|
|
69
|
+
# 받는 순간 외부 호출(Claude Code)의 stdin 이 CP949 로 깨진다
|
|
70
|
+
[string]$HookJson
|
|
63
71
|
)
|
|
64
72
|
|
|
65
73
|
$ErrorActionPreference = 'SilentlyContinue'
|
|
66
74
|
|
|
67
75
|
try {
|
|
68
76
|
# PS 5.1 은 stdin 을 시스템 ANSI(CP949) 로 읽어 UTF-8 JSON 의 한글을 깨뜨린다.
|
|
69
|
-
# 표준입력 스트림을 UTF-8 로 직접 열어서
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
# 표준입력 스트림을 UTF-8 로 직접 열어서 읽는다.
|
|
78
|
+
#
|
|
79
|
+
# **이 파일 어디에도 파이프 입력 자동변수를 쓰지 말 것.** 스크립트가 그걸 참조하기만 해도
|
|
80
|
+
# PowerShell 호스트가 실행 전에 stdin 을 콘솔 인코딩(CP949)으로 먼저 다 읽어 가서, 아래 원본 스트림은
|
|
81
|
+
# 비고 파이프 입력 쪽은 이미 깨져 있다. 실측(2026-09-14): 참조하는 스크립트는 15/15 원본 스트림 0자,
|
|
82
|
+
# 참조하지 않는 스크립트는 15/15 2606자 온전 — 한도 문구의 `·` 가 `쨌` 로 깨지던 HK2·HK6 의 원인이었다
|
|
83
|
+
$raw = $HookJson
|
|
84
|
+
if (-not $raw) {
|
|
85
|
+
$reader = New-Object System.IO.StreamReader([Console]::OpenStandardInput(), (New-Object System.Text.UTF8Encoding($false)))
|
|
86
|
+
$raw = $reader.ReadToEnd()
|
|
87
|
+
$reader.Close()
|
|
88
|
+
}
|
|
73
89
|
$hook = if ($raw) { $raw | ConvertFrom-Json } else { $null }
|
|
74
90
|
} catch {
|
|
75
91
|
$hook = $null
|
|
@@ -121,7 +137,7 @@ if ($hook -and [string]$hook.hook_event_name -eq 'StopFailure') {
|
|
|
121
137
|
|
|
122
138
|
# --- 이번 도구가 고친 파일 ---
|
|
123
139
|
# `변경` 탭이 "이 세션이 만진 것만" 을 그리는 근거다. 저장소 전체 diff 는 P4 워크스페이스 위에
|
|
124
|
-
# git 을 얹은 곳에서
|
|
140
|
+
# git 을 얹은 곳에서 수백 개씩 나와 쓸모가 없다.
|
|
125
141
|
# Edit/Write/NotebookEdit 은 전부 `tool_input.file_path` 를 쓴다. 다른 도구에는 없으므로 그냥 비고,
|
|
126
142
|
# 훅이 아예 없는 에이전트는 이 키가 영영 안 와서 플러그인이 전체 목록으로 떨어진다(그게 폴백이다).
|
|
127
143
|
$touchedFile = ''
|
|
@@ -215,7 +231,7 @@ if ($tabId) {
|
|
|
215
231
|
# 그래서 Stop(done) 뒤에 유휴 알림이 오면 완료된 탭이 '승인대기' 로 뒤집힌다 (2026-08-28 실측:
|
|
216
232
|
# status 폴더의 두 세션이 작업을 끝냈는데도 waiting 으로 박혀 있었다).
|
|
217
233
|
# 승인 요청이 아닌 알림이면 상태를 건드리지 않는다.
|
|
218
|
-
if ($Status -eq 'waiting' -and $Agent -eq 'claude') {
|
|
234
|
+
if ($Status -eq 'waiting' -and $Agent -eq 'claude') {
|
|
219
235
|
$msg = if ($hook -and $hook.message) { [string]$hook.message } else { '' }
|
|
220
236
|
$idleNotice = $msg -match '(?i)waiting for your input|idle'
|
|
221
237
|
$alreadyFinished = $prevStatus -eq 'done' -or $prevStatus -eq 'idle' -or $prevStatus -eq 'limited'
|
|
@@ -260,6 +276,10 @@ $effectiveStatus = if ($Status) { $Status } elseif ($prevStatus) { $prevStatus }
|
|
|
260
276
|
$payload = [ordered]@{
|
|
261
277
|
sessionId = $targetId
|
|
262
278
|
ts = [int64](([datetime]::UtcNow - [datetime]'1970-01-01').TotalMilliseconds)
|
|
279
|
+
# 누가 보냈나. 상태·서브에이전트와 달리 **매번 같은 값**이지만 그래도 매번 싣는다 —
|
|
280
|
+
# 빼먹은 보고가 하나라도 있으면 플러그인이 그 탭을 다시 추측 경로로 떨어뜨린다.
|
|
281
|
+
# (서브에이전트 이벤트처럼 status 를 안 싣는 보고에도 들어가야 하므로 아래 분기 밖에 둔다)
|
|
282
|
+
agent = $Agent
|
|
263
283
|
}
|
|
264
284
|
# **서브에이전트 이벤트는 상태를 싣지 않는다.** 이 훅에는 `-Status` 가 없어서 직전 상태를
|
|
265
285
|
# 되쓰게 되는데, 그러면 이미 `done` 인 세션에 백그라운드 에이전트의 stop 이 도착할 때
|
|
@@ -327,7 +347,7 @@ if ($pids.Count -gt 0) {
|
|
|
327
347
|
# 뭉개 버린다(중첩 한 겹이 더 생겼다). 원소가 하나일 때 배열이 스칼라로 접히는 문제는 받는 쪽에서
|
|
328
348
|
# 배열/단일을 모두 받아 푼다 — PowerShell 의 JSON 변환은 그 접힘을 끌 수 없다
|
|
329
349
|
$json = $payload | ConvertTo-Json -Compress -Depth 5
|
|
330
|
-
$tmp = "$file.$PID.tmp"
|
|
350
|
+
$tmp = "$file.$PID.tmp"
|
|
331
351
|
$enc = New-Object System.Text.UTF8Encoding($false)
|
|
332
352
|
[System.IO.File]::WriteAllText($tmp, $json, $enc)
|
|
333
353
|
[System.IO.File]::Copy($tmp, $file, $true)
|