vibe-coding-master 0.0.9 → 0.0.11

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.
@@ -17,7 +17,7 @@ V1 is implemented as a local GUI app with:
17
17
  - API-driven message bus.
18
18
  - Translation panel based on Claude transcript JSONL tailing.
19
19
  - npm packaging with built `dist` and `dist-frontend` output.
20
- - Task creation creates one `feature/<task>` branch and one `.ai/vcm/worktrees/<task>` git worktree.
20
+ - Task creation creates one `feature/<task>` branch and one `.ai/vcm/worktrees/<task>` git worktree by default; users may clear `Create worktree and branch` to create an inline task in the connected repository/current branch.
21
21
 
22
22
  ## 2. Package And Build
23
23
 
@@ -28,7 +28,7 @@ File:
28
28
  Current package facts:
29
29
 
30
30
  - package name: `vibe-coding-master`
31
- - current version: `0.0.7`
31
+ - current version: `0.0.10`
32
32
  - type: ESM
33
33
  - `bin.vcm`: `dist/main.js`
34
34
  - `bin.vcmctl`: `dist/cli/vcmctl.js`
@@ -179,6 +179,20 @@ Important fields:
179
179
  - `claudeCommand`
180
180
  - `isDirty`
181
181
 
182
+ ### `src/shared/types/app-settings.ts`
183
+
184
+ Defines:
185
+
186
+ - `ThemeMode`
187
+ - `AppPreferences`
188
+ - `UpdateAppPreferencesRequest`
189
+
190
+ Theme modes:
191
+
192
+ - `system`
193
+ - `light`
194
+ - `dark`
195
+
182
196
  ### `src/shared/types/task.ts`
183
197
 
184
198
  Defines:
@@ -187,16 +201,16 @@ Defines:
187
201
  - `TaskRecord`
188
202
  - `CreateTaskRequest`
189
203
 
190
- Current UI sends only `taskSlug`, although the API type still permits optional `title` and `specPath`.
204
+ Current UI sends `taskSlug` and `createWorktree`; the API type still permits optional `title` and `specPath`.
191
205
 
192
206
  Worktree fields:
193
207
 
194
- - `worktreePath: string`
195
- - `branch: feature/<taskSlug>`
208
+ - `worktreePath?: string`
209
+ - `branch: feature/<taskSlug>` when worktree creation is selected, otherwise the connected repo's current branch
196
210
  - `cleanupStatus?: "active" | "cleaned"`
197
211
  - `cleanedAt?: string`
198
212
 
199
- `CreateTaskRequest` creates a worktree and branch by default. The UI shows a checked `Create worktree and branch` indicator, but task worktree creation is the normal VCM task path and does not require a separate later action or a user-visible off switch.
213
+ `CreateTaskRequest` supports `createWorktree?: boolean`. It creates a worktree and branch by default, and skips both when `createWorktree === false`.
200
214
 
201
215
  ### `src/shared/types/session.ts`
202
216
 
@@ -244,7 +258,10 @@ Defines:
244
258
  - `SendTranslatedInputRequest`
245
259
  - `TranslationProviderTestResult`
246
260
  - `TranslationPromptPreview`
247
- - `TranslationWsMessage`
261
+ - `TranslationSessionStatus`
262
+ - `TranslationSessionEvent`
263
+ - `StartTranslationSessionResult`
264
+ - `PollTranslationSessionResult`
248
265
 
249
266
  Prompt keys:
250
267
 
@@ -396,8 +413,8 @@ Worktree methods:
396
413
  Required safety:
397
414
 
398
415
  - all Git commands keep command-scoped `safe.directory`
399
- - refuse worktree paths outside `<baseRepoRoot>/.ai/vcm/worktrees/`
400
- - refuse branch names that do not match `feature/<taskSlug>` for VCM-created tasks
416
+ - `TaskService` verifies Close Task worktree paths are under `<baseRepoRoot>/.ai/vcm/worktrees/`
417
+ - VCM-created task branches are derived from validated task slugs as `feature/<taskSlug>`
401
418
 
402
419
  ### `src/backend/adapters/claude-adapter.ts`
403
420
 
@@ -520,39 +537,44 @@ Create flow:
520
537
  ```text
521
538
  createTask(baseRepoRoot, { taskSlug })
522
539
  -> assertValidTaskSlug(taskSlug)
523
- -> branch = feature/<taskSlug>
524
- -> worktreePath = <baseRepoRoot>/.ai/vcm/worktrees/<taskSlug>
525
540
  -> assert .ai/vcm/ is ignored
526
- -> assert base repo has no uncommitted changes
527
- -> assert branch does not exist
528
- -> assert worktree path does not exist
529
- -> git.createWorktree({ baseRepoRoot, branch, worktreePath, baseRef: HEAD })
530
- -> artifactService.ensureHandoffStructure({ repoRoot: worktreePath, handoffDir })
531
- -> artifactService.createArtifactTemplates({ repoRoot: worktreePath, handoffDir })
541
+ -> if createWorktree is not false:
542
+ -> branch = feature/<taskSlug>
543
+ -> worktreePath = <baseRepoRoot>/.ai/vcm/worktrees/<taskSlug>
544
+ -> assert base repo has no uncommitted changes
545
+ -> assert branch does not exist
546
+ -> assert worktree path does not exist
547
+ -> git.createWorktree({ baseRepoRoot, branch, worktreePath, baseRef: HEAD })
548
+ -> taskRepoRoot = worktreePath
549
+ -> otherwise:
550
+ -> branch = current base repo branch
551
+ -> worktreePath = undefined
552
+ -> taskRepoRoot = baseRepoRoot
553
+ -> artifactService.ensureHandoffStructure({ repoRoot: taskRepoRoot, handoffDir })
554
+ -> artifactService.createArtifactTemplates({ repoRoot: taskRepoRoot, handoffDir })
555
+ -> ensure task runtime state dirs under <taskRepoRoot>/.ai/vcm/
532
556
  -> write central task record under <baseRepoRoot>/.ai/vcm/tasks/<task>.json
533
557
  ```
534
558
 
535
- Cleanup flow:
559
+ Close Task flow:
536
560
 
537
561
  ```text
538
562
  cleanupTask(baseRepoRoot, taskSlug, options)
539
- -> require all role sessions stopped
540
563
  -> load central task record
541
- -> verify worktreePath is under <baseRepoRoot>/.ai/vcm/worktrees/
542
- -> inspect git status in worktree
543
- -> refuse uncommitted changes unless options.force
544
- -> git.removeWorktree(baseRepoRoot, worktreePath)
545
- -> delete .ai/vcm/tasks/<task>.json
546
- -> delete .ai/vcm/sessions/<task>.json
547
- -> delete .ai/vcm/messages/<task>.jsonl
548
- -> delete .ai/vcm/orchestration/<task>.json
564
+ -> route layer lists role sessions
565
+ -> route layer stops each VCM-managed role session with status running
566
+ -> route layer stops translation tailers and clears task translation cache
567
+ -> if worktreePath exists, verify it is under <baseRepoRoot>/.ai/vcm/worktrees/
568
+ -> if worktreePath exists, git.removeWorktree(baseRepoRoot, worktreePath, force=true)
569
+ -> if worktreePath exists, git.deleteBranch(baseRepoRoot, task.branch, force=true) by default
570
+ -> delete <baseRepoRoot>/.ai/vcm/tasks/<task>.json
571
+ -> delete <taskRepoRoot>/.ai/vcm/sessions/<task>.json
572
+ -> delete <taskRepoRoot>/.ai/vcm/messages/<task>.jsonl
573
+ -> delete <taskRepoRoot>/.ai/vcm/orchestration/<task>.json
574
+ -> delete <taskRepoRoot>/.ai/vcm/translation/<task>/
549
575
  ```
550
576
 
551
- Branch cleanup:
552
-
553
- - keep `feature/<taskSlug>` by default
554
- - optional `deleteBranch` only with explicit confirmation
555
- - prefer requiring merged branch unless force-confirmed
577
+ The UI labels this operation `Close Task`, styles it as a red destructive action, and shows a browser confirmation that names running role-session shutdown, the worktree, branch, and metadata that will be deleted. VCM actively stops VCM-managed running role sessions, but it does not preflight running sessions or uncommitted changes before closing. Tasks created without a worktree remove VCM metadata only.
556
578
 
557
579
  ### `src/backend/services/artifact-service.ts`
558
580
 
@@ -621,7 +643,7 @@ Responsibilities:
621
643
  Persistence:
622
644
 
623
645
  ```text
624
- <baseRepoRoot>/.ai/vcm/sessions/<task>.json
646
+ <taskRepoRoot>/.ai/vcm/sessions/<task>.json
625
647
  ```
626
648
 
627
649
  Environment passed to Claude Code:
@@ -634,7 +656,7 @@ Environment passed to Claude Code:
634
656
  In task-worktree mode:
635
657
 
636
658
  - session cwd is `task.worktreePath`
637
- - session persistence remains central under `baseRepoRoot/.ai/vcm/sessions`
659
+ - session persistence is written under `task.worktreePath/.ai/vcm/sessions`
638
660
  - raw logs and handoff artifacts are written under the task worktree
639
661
 
640
662
  ### `src/backend/services/message-service.ts`
@@ -658,7 +680,8 @@ Responsibilities:
658
680
 
659
681
  In task-worktree mode:
660
682
 
661
- - message snapshots remain central under `baseRepoRoot/.ai/vcm/messages`
683
+ - message snapshots live under `task.worktreePath/.ai/vcm/messages`
684
+ - orchestration state lives under `task.worktreePath/.ai/vcm/orchestration`
662
685
  - message body files live under `task.worktreePath/.ai/handoffs/<task>/messages`
663
686
  - terminal delivery uses the runtime session for the role, whose cwd is the task worktree
664
687
 
@@ -700,6 +723,12 @@ Exports:
700
723
  - `AppSettingsServiceDeps`
701
724
  - `createAppSettingsService(deps)`
702
725
 
726
+ Settings responsibilities:
727
+
728
+ - persist UI theme mode: `system`, `light`, or `dark`
729
+ - persist translation settings and translation secrets
730
+ - persist up to five recent repository paths
731
+
703
732
  Storage:
704
733
 
705
734
  ```text
@@ -763,21 +792,24 @@ Exports:
763
792
  - `TranslationEventListener`
764
793
  - `TranslationServiceDeps`
765
794
  - `createTranslationService(deps)`
766
- - `formatTerminalSubmit(text)`
767
795
 
768
796
  Responsibilities:
769
797
 
770
798
  - load/update translation settings
771
799
  - expose prompt previews
772
800
  - test provider
801
+ - start backend transcript listening for a role session
802
+ - poll cached translation events by cursor
773
803
  - translate user input
774
804
  - send English text to active terminal
775
- - subscribe to session translation events
776
- - clear session entries
805
+ - clear session entries and cached events
806
+ - stop session/task translation listeners
777
807
  - retry failed output translation
778
808
  - subscribe to Claude transcript service
779
809
  - translate prose output and preserve tool output
780
810
 
811
+ Terminal submission is delegated to `src/backend/runtime/terminal-submit.ts`, which bracket-pastes text, waits briefly, then sends Enter separately.
812
+
781
813
  ## 9. Backend API
782
814
 
783
815
  ### `src/backend/server.ts`
@@ -792,17 +824,18 @@ Exports:
792
824
  - `createDefaultServerDeps(options)`
793
825
  - `getDefaultStaticDir()`
794
826
 
795
- Registers all routes and WebSockets.
827
+ Registers HTTP routes and the terminal WebSocket.
796
828
 
797
829
  ### Route files
798
830
 
831
+ - `src/backend/api/app-settings-routes.ts`: UI preferences
799
832
  - `src/backend/api/project-routes.ts`: health, recent paths, connect/current project
800
833
  - `src/backend/api/harness-routes.ts`: harness status/apply
801
- - `src/backend/api/task-routes.ts`: tasks, task status, task cleanup
834
+ - `src/backend/api/task-routes.ts`: tasks, task status, and Close Task cleanup endpoint; Close Task stops running role sessions before translation/task cleanup
802
835
  - `src/backend/api/session-routes.ts`: session lifecycle and dispatch compatibility endpoint
803
836
  - `src/backend/api/artifact-routes.ts`: artifact, role command, and log reads/writes
804
837
  - `src/backend/api/message-routes.ts`: messages and orchestration
805
- - `src/backend/api/translation-routes.ts`: settings, prompt previews, provider test, input/send, clear/retry
838
+ - `src/backend/api/translation-routes.ts`: settings, prompt previews, provider test, start/poll, input/send, clear/retry
806
839
 
807
840
  Worktree task API:
808
841
 
@@ -815,11 +848,10 @@ Do not add a "switch task worktree" endpoint. Worktree assignment happens only d
815
848
  ### WebSocket files
816
849
 
817
850
  - `src/backend/ws/terminal-ws.ts`
818
- - `src/backend/ws/translation-ws.ts`
819
851
 
820
852
  Terminal WebSocket forwards PTY output/input/resize.
821
853
 
822
- Translation WebSocket subscribes to translation entries/status for a runtime session id.
854
+ Translation does not use WebSocket. The backend writes cached translation events under `<taskRepoRoot>/.ai/vcm/translation/<task>/<role>/<session-id>.jsonl`; the frontend polls `GET /api/translation/sessions/:sessionId/events?after=<cursor>`. The cursor is the next expected seq, so `after=18` means seq `1..17` can be removed and seq `18+` should be returned.
823
855
 
824
856
  ## 10. Backend Templates
825
857
 
@@ -878,12 +910,12 @@ It calls:
878
910
  - orchestration endpoints
879
911
  - translation endpoints
880
912
 
881
- Target additions:
913
+ Implemented task cleanup method:
882
914
 
883
- - `listGitWorktrees()`
884
- - `listGitBranches()`
885
915
  - `cleanupTask(taskSlug, options)`
886
916
 
917
+ There are no branch/worktree switching APIs in the current frontend client.
918
+
887
919
  ### `src/frontend/state/app-store.ts`
888
920
 
889
921
  Exports:
@@ -931,9 +963,8 @@ Responsibilities:
931
963
  - messages modal
932
964
  - events modal
933
965
  - harness panel
934
- - task creation with one task-name field, checked non-optional worktree/branch indicator, branch preview, and worktree path preview
966
+ - task creation with one task-name field, `Create worktree and branch` checkbox selected by default, branch preview, and worktree path preview
935
967
  - task navigation
936
- - completed-task cleanup action
937
968
 
938
969
  ### `src/frontend/routes/task-workspace.tsx`
939
970
 
@@ -945,6 +976,7 @@ Exports:
945
976
  Responsibilities:
946
977
 
947
978
  - task header with role tabs and refresh
979
+ - red `Close Task` action with destructive confirmation
948
980
  - show branch and immutable worktree path for the active task
949
981
  - status/message/orchestration refresh
950
982
  - periodic polling
@@ -1003,7 +1035,7 @@ Exports:
1003
1035
  - `getMessageCounts(messages)`
1004
1036
  - `MessageTimeline(props)`
1005
1037
 
1006
- Used inside the Messages modal. Can show stage/reject/open-role actions.
1038
+ Used inside the Messages modal. Current UI rows show sequence, timestamp, route, type, status, body preview, body path, and a `Copy` button. Stage/approve/reject backend APIs remain compatibility paths, but those buttons are not displayed in the current modal.
1007
1039
 
1008
1040
  ### `src/frontend/components/event-log.tsx`
1009
1041
 
@@ -1068,6 +1100,8 @@ Important current behavior:
1068
1100
  - no `Original` buttons
1069
1101
  - tool output is preserved, dim, one-line
1070
1102
  - prose source is replaced by translated text after completion
1103
+ - prose renders Markdown with GFM support
1104
+ - user-input translation entries add a thick divider and larger top spacing to mark question/answer boundaries
1071
1105
  - no separate translated-English textarea
1072
1106
 
1073
1107
  ### `src/frontend/components/translation-settings-modal.tsx`
@@ -1079,18 +1113,15 @@ Exports:
1079
1113
 
1080
1114
  Settings:
1081
1115
 
1082
- - enable translation
1083
1116
  - base URL
1084
1117
  - API key as text input
1085
1118
  - model
1086
1119
  - target language
1087
- - input mode
1088
1120
  - context
1089
- - translate output
1090
- - translate user input
1091
1121
  - timeout
1092
1122
  - temperature
1093
- - prompt slot overrides
1123
+ - direct editors for `zh-to-en`, `zh-to-en-with-context`, and `en-to-zh`
1124
+ - reset prompts to built-in defaults
1094
1125
  - provider test
1095
1126
 
1096
1127
  ### `src/frontend/components/status-badge.tsx`
@@ -1114,7 +1145,8 @@ Sidebar:
1114
1145
 
1115
1146
  - all groups default collapsed
1116
1147
  - `Repository Path` default open only when no task is selected
1117
- - `Settings` includes `Messages`, `Events`, and `Auto orchestration`
1148
+ - `Settings` includes `Theme`, `Messages`, `Events`, and `Auto orchestration`
1149
+ - `Theme` cycles through `System`, `Light`, and `Dark`; `System` follows the browser/OS color-scheme preference
1118
1150
 
1119
1151
  Task workspace:
1120
1152
 
@@ -1139,6 +1171,8 @@ App settings:
1139
1171
  ~/.vcm/settings.json
1140
1172
  ```
1141
1173
 
1174
+ Contains UI theme preference, translation settings/secrets, and recent repository paths.
1175
+
1142
1176
  Project config:
1143
1177
 
1144
1178
  ```text
@@ -1149,26 +1183,32 @@ Project config:
1149
1183
  Task state:
1150
1184
 
1151
1185
  ```text
1152
- .ai/vcm/tasks/<task>.json
1186
+ <baseRepoRoot>/.ai/vcm/tasks/<task>.json
1153
1187
  ```
1154
1188
 
1155
1189
  Session state:
1156
1190
 
1157
1191
  ```text
1158
- .ai/vcm/sessions/<task>.json
1192
+ <taskRepoRoot>/.ai/vcm/sessions/<task>.json
1159
1193
  ```
1160
1194
 
1161
1195
  Messages:
1162
1196
 
1163
1197
  ```text
1164
- .ai/vcm/messages/<task>.jsonl
1165
- .ai/handoffs/<task>/messages/<message-id>.md
1198
+ <taskRepoRoot>/.ai/vcm/messages/<task>.jsonl
1199
+ <taskRepoRoot>/.ai/handoffs/<task>/messages/<message-id>.md
1166
1200
  ```
1167
1201
 
1168
1202
  Orchestration:
1169
1203
 
1170
1204
  ```text
1171
- .ai/vcm/orchestration/<task>.json
1205
+ <taskRepoRoot>/.ai/vcm/orchestration/<task>.json
1206
+ ```
1207
+
1208
+ Translation cache:
1209
+
1210
+ ```text
1211
+ <taskRepoRoot>/.ai/vcm/translation/<task>/
1172
1212
  ```
1173
1213
 
1174
1214
  Task worktrees:
@@ -1217,7 +1257,7 @@ For frontend layout changes, also verify manually:
1217
1257
  - Auto orchestration toggles on/off
1218
1258
  - `Enter` in translation composer translates/sends
1219
1259
  - `Shift+Enter` inserts newline
1220
- - mark task complete and verify cleanup removes the worktree and central task metadata
1260
+ - close a worktree-backed task and verify it stops running role sessions, removes the worktree, deletes the task branch, and removes central task metadata
1221
1261
 
1222
1262
  ## 17. V1 Boundaries To Preserve
1223
1263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [
@@ -44,6 +44,8 @@
44
44
  "node-pty": "^1.0.0",
45
45
  "react": "^19.1.0",
46
46
  "react-dom": "^19.1.0",
47
+ "react-markdown": "^10.1.0",
48
+ "remark-gfm": "^4.0.1",
47
49
  "ws": "^8.18.2",
48
50
  "zod": "^3.25.28"
49
51
  },
@@ -1,35 +0,0 @@
1
- import { WebSocketServer } from "ws";
2
- import { toVcmError } from "../errors.js";
3
- export function registerTranslationWs(app, deps) {
4
- const wss = new WebSocketServer({ noServer: true });
5
- app.server.on("upgrade", (request, socket, head) => {
6
- const url = new URL(request.url ?? "/", "http://localhost");
7
- const match = /^\/ws\/translation\/([^/]+)$/.exec(url.pathname);
8
- if (!match) {
9
- return;
10
- }
11
- wss.handleUpgrade(request, socket, head, (ws) => {
12
- bindTranslationSocket(ws, decodeURIComponent(match[1] ?? ""), deps.translationService);
13
- });
14
- });
15
- }
16
- function bindTranslationSocket(ws, sessionId, translationService) {
17
- let unsubscribe = () => { };
18
- try {
19
- unsubscribe = translationService.subscribeToSession(sessionId, (message) => send(ws, message));
20
- }
21
- catch (error) {
22
- const vcmError = toVcmError(error);
23
- send(ws, { type: "translation-error", message: vcmError.message });
24
- ws.close();
25
- return;
26
- }
27
- ws.on("close", () => {
28
- unsubscribe();
29
- });
30
- }
31
- function send(ws, message) {
32
- if (ws.readyState === ws.OPEN) {
33
- ws.send(JSON.stringify(message));
34
- }
35
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
- * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
- * https://github.com/chjj/term.js
5
- * @license MIT
6
- *
7
- * Permission is hereby granted, free of charge, to any person obtaining a copy
8
- * of this software and associated documentation files (the "Software"), to deal
9
- * in the Software without restriction, including without limitation the rights
10
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- * copies of the Software, and to permit persons to whom the Software is
12
- * furnished to do so, subject to the following conditions:
13
- *
14
- * The above copyright notice and this permission notice shall be included in
15
- * all copies or substantial portions of the Software.
16
- *
17
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- * THE SOFTWARE.
24
- *
25
- * Originally forked from (with the author's permission):
26
- * Fabrice Bellard's javascript vt100 for jslinux:
27
- * http://bellard.org/jslinux/
28
- * Copyright (c) 2011 Fabrice Bellard
29
- * The original design remains. The terminal itself
30
- * has been extended to include xterm CSI codes, among
31
- * other features.
32
- */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}:root{color-scheme:light;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;background:#f5f2ea;color:#1c2024;line-height:1.5}html,body,#root{height:100%}*{box-sizing:border-box}body{margin:0;min-width:320px;min-height:100vh;background:#f5f2ea}button,input,select,textarea{font:inherit}button{border:1px solid #9ba6ad;background:#f8f7f2;color:#1d252b;border-radius:6px;min-height:34px;padding:6px 10px;cursor:pointer}button:hover:not(:disabled){background:#eef4f2;border-color:#607d74}button:disabled{cursor:not-allowed;opacity:.55}input,select,textarea:not(.xterm-helper-textarea){width:100%;border:1px solid #b9b0a1;border-radius:6px;background:#fffdf8;color:#202326;padding:8px 10px}textarea:not(.xterm-helper-textarea){min-height:240px;resize:vertical;font-family:Menlo,Monaco,Consolas,monospace;font-size:12px}h1,h2,p{margin-top:0}h1{margin-bottom:4px;font-size:26px;line-height:1.15}h2{margin-bottom:10px;font-size:14px;letter-spacing:0}.app-shell{display:grid;grid-template-columns:minmax(280px,320px) minmax(0,1fr);height:100vh;min-height:0;overflow:hidden}.app-shell.is-sidebar-collapsed{grid-template-columns:46px minmax(0,1fr)}.app-sidebar{position:relative;min-width:0;border-right:1px solid #d3c9b8;background:#fbfaf6;padding:14px;overflow:auto}.app-shell.is-sidebar-collapsed .app-sidebar{overflow:hidden;padding:8px}.sidebar-toggle{position:absolute;top:10px;right:10px;z-index:2;display:grid;place-items:center;width:28px;min-height:28px;padding:0;background:#fffdf8}.sidebar-toggle:before{width:8px;height:8px;border-color:currentColor;border-style:solid;border-width:0 2px 2px 0;content:"";transform:translate(2px) rotate(135deg)}.app-shell.is-sidebar-collapsed .sidebar-toggle{left:9px;right:auto}.app-shell.is-sidebar-collapsed .sidebar-toggle:before{transform:translate(-2px) rotate(-45deg)}.sidebar-content{min-width:0}.app-shell.is-sidebar-collapsed .sidebar-content{width:0;opacity:0;pointer-events:none}.app-main{min-width:0;height:100%;padding:14px 16px;overflow:auto}.brand-header{display:flex;gap:12px;align-items:baseline;margin-bottom:10px;padding-right:34px}.brand-header strong{font-size:18px}.brand-header span,.muted,.workspace-branch,.workspace-worktree{color:#667071;font-size:13px}.sidebar-section{margin-bottom:8px;border:1px solid #e0d6c7;border-radius:8px;background:#fffdfa;overflow:hidden}.sidebar-section-toggle{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;width:100%;min-height:34px;border:0;border-radius:0;background:transparent;color:#1c2024;font-size:13px;font-weight:750;text-align:left}.sidebar-section-toggle:hover{background:#f5f1e8}.sidebar-section-toggle[aria-expanded=true]{border-bottom:1px solid #ece5d9}.sidebar-section-chevron{width:8px;height:8px;border-color:currentColor;border-style:solid;border-width:0 2px 2px 0;transform:rotate(45deg)}.sidebar-section-toggle[aria-expanded=true] .sidebar-section-chevron{transform:rotate(-135deg)}.sidebar-section-content{padding:8px}.repo-connect,.project-summary,.harness-panel,.task-create{margin:0}.inline-form{display:grid;grid-template-columns:minmax(0,1fr);gap:8px}.inline-form.has-recent-paths{grid-template-columns:minmax(0,1fr) auto}.inline-form>input{grid-column:1 / -1}.inline-form>button{justify-self:end}.inline-form.has-recent-paths>button{justify-self:auto}.repo-recent-select{min-width:0;max-width:none}.project-summary dl{display:grid;gap:8px;margin:0}.project-summary div{min-width:0}.project-summary dt{color:#6c6255;font-size:12px}.project-summary dd{margin:0;overflow-wrap:anywhere;font-size:13px}.warnings,.error-banner{border:1px solid #c87b54;background:#fff4ed;color:#6f3218;border-radius:6px;padding:10px 12px}.warnings{margin:12px 0 0;padding-left:26px;font-size:13px}.harness-panel{display:grid;gap:8px}.harness-panel-header{display:flex;justify-content:space-between;gap:8px;align-items:center}.harness-panel-header h2,.harness-panel-header p,.harness-result p{margin-bottom:0}.harness-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.harness-file-list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.harness-file-list li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;border:1px solid #ece5d9;border-radius:6px;padding:6px 8px;background:#fffdfa}.harness-file-list span{overflow:hidden;font-size:12px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.harness-changes,.harness-result{border:1px solid #e0d6c7;border-radius:6px;padding:8px;background:#f8f7f2;font-size:12px}.harness-changes h3{margin:0 0 4px;font-size:12px}.harness-changes ul,.harness-result ul{margin:0;padding-left:18px}.task-create form{display:grid;gap:8px}.task-create-preview{display:grid;gap:2px;border:1px solid #e0d6c7;border-radius:6px;background:#f8f7f2;padding:6px 8px}.task-create-preview span{color:#255f3d;font-size:12px;font-weight:700}.task-create-preview small{overflow:hidden;color:#687273;font-size:11px;text-overflow:ellipsis;white-space:nowrap}.task-nav{display:grid;gap:8px}.task-nav-item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;text-align:left}.task-nav-item.is-active,.role-tab.is-active{border-color:#2f6f73;background:#e8f1ef}.sidebar-settings{display:grid;gap:8px}.sidebar-settings button{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;width:100%;text-align:left}.sidebar-settings .settings-toggle.is-active{border-color:#2f6f73;background:#e8f1ef}.workspace-header{display:grid;grid-template-columns:minmax(180px,auto) minmax(420px,1fr) auto auto;gap:16px;align-items:center;margin-bottom:6px}.workspace-title-line{display:flex;flex-wrap:wrap;gap:10px;align-items:baseline;min-width:0}.workspace-title-line h1{margin-bottom:0;font-size:18px;line-height:1.15}.workspace-branch{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.workspace-worktree{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.eyebrow{color:#7a5c2f;font-size:12px;font-weight:700;text-transform:uppercase}.role-tabs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin-bottom:8px;min-width:0}.workspace-header .role-tabs{margin-bottom:0}.role-tab{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px;align-items:center;min-height:30px;padding:4px 8px;text-align:left}.workflow-panel{display:grid;gap:8px;margin:0}.workflow-summary p{margin-bottom:0}.workflow-summary p{color:#4f5558;font-size:13px}.workflow-steps{display:grid;grid-template-columns:minmax(0,1fr);gap:6px;margin:0;padding:0;list-style:none}.workflow-steps li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px;align-items:center;min-height:28px;border:1px solid #ece5d9;border-radius:6px;padding:4px 6px;background:#fffdfa}.workflow-steps li.is-current{border-color:#2f6f73;background:#e8f1ef}.workflow-steps span{overflow:hidden;color:#394246;font-size:12px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.workspace-grid{display:grid;grid-template-columns:minmax(0,1fr);flex:1;gap:10px;align-items:stretch;min-height:0}.workspace-main{min-width:0;min-height:0;display:flex;flex-direction:column;gap:8px}.role-console-stack{min-width:0;min-height:0;flex:1;display:flex;flex-direction:column}.role-console-panel{min-width:0;min-height:0;flex:1;display:none}.role-console-panel.is-active{display:flex;flex-direction:column}.session-console,.message-panel,.event-log,.empty-workspace{border:1px solid #d6d0c6;border-radius:8px;background:#fffdf8;padding:10px}.task-workspace{display:flex;flex-direction:column;gap:8px;height:100%;min-height:0}.session-console{display:grid;grid-template-rows:auto minmax(0,1fr);gap:8px;flex:1;min-height:0}.session-console-top{display:flex;gap:10px;align-items:center;justify-content:space-between}.session-controls{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:space-between;margin:0 0 8px}.permission-mode-field{display:grid;grid-template-columns:auto minmax(180px,260px);gap:8px;align-items:center;width:fit-content;max-width:100%}.permission-mode-field span{color:#5f6a6c;font-size:13px;font-weight:650}.permission-mode-field small{display:block;color:#7b8587;font-size:11px;font-weight:500;line-height:1.2}.permission-mode-field select{width:100%;min-height:30px;border:1px solid #b9b0a1;border-radius:6px;background:#fffdf8;color:#202326;padding:4px 8px}.session-toolbar{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.session-toolbar button{min-height:30px;padding:4px 9px}.translation-toggle{display:inline-flex;align-items:center;justify-content:center;min-height:28px;border:1px solid #b5bec4;border-radius:6px;background:#fffefa;color:#4f5558;font-size:12px;font-weight:650;padding:3px 10px;white-space:nowrap}.translation-toggle.is-active{border-color:#2f7e84;background:#e8f4f2;color:#145e64}.translation-settings-grid input[type=checkbox]{width:auto}.session-console-body{min-width:0;min-height:0;height:100%}.session-console-body.has-translation{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px;align-items:stretch}.terminal-pane,.translation-pane{min-width:0;min-height:0}.terminal-frame,.terminal-empty{height:100%;min-height:0;border-radius:6px;overflow:hidden;background:#111316}.terminal-empty{display:grid;place-items:center;align-content:center;gap:8px;color:#d6d0c6;border:1px solid #292d31}.translation-panel{display:grid;grid-template-rows:auto minmax(0,1fr) auto;gap:8px;height:100%;min-height:0;border:1px solid #292d31;border-radius:6px;background:#0d1117;color:#d6deeb;padding:8px;min-width:0;width:100%;overflow:hidden;font-family:Menlo,Monaco,Consolas,monospace}.translation-panel-header{display:grid;gap:3px}.translation-panel-titlebar,.translation-panel-actions{display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:space-between}.translation-panel-header h2,.translation-panel-header p{margin-bottom:0}.translation-panel-titlebar h2{font-size:16px}.translation-panel-header p,.translation-composer span{color:#8b949e;font-size:12px}.translation-panel-actions button{border-color:#3a4149;background:#161b22;color:#d6deeb;min-height:26px;padding:2px 8px;font-size:12px}.translation-panel-actions button:hover:not(:disabled),.translation-composer-actions button:hover:not(:disabled){border-color:#58a6ff;background:#1f2937}.translation-panel-actions .auto-send-toggle.is-active{border-color:#56d364;background:#12261a;color:#d6deeb}.translation-panel-actions{justify-content:flex-end}.translation-entry-list{display:grid;align-content:start;gap:8px;min-height:0;min-width:0;overflow-x:hidden;overflow-y:auto;scrollbar-color:#4b5563 #0d1117}.translation-entry{border:0;border-radius:0;background:transparent;min-width:0;max-width:100%;padding:0}.translation-entry pre{box-sizing:border-box;margin:0;max-height:none;max-width:100%;min-width:0;overflow:visible;white-space:pre-wrap;overflow-wrap:anywhere;font-family:Menlo,Monaco,Consolas,monospace;font-size:12px;line-height:1.45;color:#d6deeb}.translation-entry.is-tool-output pre{display:block;overflow:hidden;color:#7d8590;text-overflow:ellipsis;white-space:nowrap;width:100%}.translation-warning{color:#ffab70;margin-bottom:6px}.translation-composer{display:grid;gap:6px;border-top:1px solid #292d31;padding-top:8px}.translation-composer-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:stretch}.translation-composer textarea{width:100%;min-height:38px;max-height:88px;border-color:#3a4149;background:#0d1117;color:#d6deeb;font-family:inherit;font-size:12px;line-height:1.35;resize:vertical}.translation-composer textarea::placeholder{color:#7d8590}.translation-composer textarea::selection{background:#8b949e59;color:#fff}.translation-composer textarea:focus,.translation-composer textarea:focus-visible{border-color:#4b5563;outline:none;box-shadow:none}.translation-composer-actions{display:grid;align-content:start;gap:6px;min-width:104px}.translation-composer-actions button{border-color:#3a4149;background:#161b22;color:#d6deeb;width:100%;min-height:38px;padding:4px 9px;font-size:12px}.translation-panel .muted{color:#8b949e}.translation-panel .error-banner{border-color:#da7b72;background:#2d1518;color:#ffdcd7}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:20;display:grid;place-items:center;background:#181c1f61;padding:18px}.translation-settings-modal{display:grid;gap:12px;width:min(760px,100%);max-height:min(760px,92vh);overflow:auto;border:1px solid #d6d0c6;border-radius:8px;background:#fffdf8;padding:14px}.message-modal,.event-modal{display:grid;grid-template-rows:auto minmax(0,1fr);gap:12px;width:min(980px,100%);max-height:min(760px,92vh);overflow:hidden;border:1px solid #d6d0c6;border-radius:8px;background:#fffdf8;padding:14px}.translation-settings-modal header,.message-modal header,.event-modal header,.translation-settings-modal footer{display:flex;gap:8px;align-items:center;justify-content:space-between}.translation-prompt-settings{display:grid;gap:10px;border-top:1px solid #ece5d9;padding-top:12px}.translation-prompt-settings header{display:flex;gap:10px;align-items:end;justify-content:space-between}.translation-prompt-settings h3,.translation-prompt-settings p{margin-bottom:0}.translation-prompt-settings h3{font-size:13px}.translation-prompt-settings textarea{min-height:120px;max-height:260px;font-family:Menlo,Monaco,Consolas,monospace;font-size:12px}.translation-prompt-editor-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.translation-settings-modal h2,.message-modal h2,.message-modal p,.event-modal h2,.event-modal p,.translation-settings-modal p{margin-bottom:0}.translation-settings-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.translation-settings-grid label{display:grid;gap:4px}.translation-settings-grid span{color:#4f5558;font-size:12px;font-weight:650}.translation-settings-grid select,.translation-prompt-settings select{min-height:34px;border:1px solid #b9b0a1;border-radius:6px;background:#fffdf8;color:#202326;padding:6px 10px}.translation-test-result{border-radius:6px;padding:8px;font-size:13px}.translation-test-result.is-ok{border:1px solid #6ea77e;background:#e6f3e9;color:#245334}.translation-test-result.is-error{border:1px solid #c46e5f;background:#fae9e6;color:#6f2b21}.message-panel{display:grid;gap:8px}.message-modal .message-panel,.event-modal .event-log{min-height:0;overflow:auto;border:0;background:transparent;padding:0}.message-panel-header{display:flex;justify-content:space-between;gap:12px;align-items:center}.message-panel-header h2,.message-panel-header p{margin-bottom:0}.message-controls,.message-mode-toggle,.message-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.message-mode-toggle{color:#4f5558;font-size:13px;font-weight:650}.message-mode-toggle input{width:auto}.message-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}.message-item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:start;border:1px solid #ece5d9;border-radius:6px;padding:8px;background:#fffdfa}.message-meta{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:4px}.message-meta span:not(.status-badge),.message-path{color:#667071;font-size:12px}.message-item p{display:-webkit-box;margin-bottom:4px;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}.message-path{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.event-log ol{margin:0;padding-left:22px;font-size:13px}.event-log{max-height:92px;overflow:auto}.event-log h2{margin-bottom:4px;font-size:13px}.event-log p{margin-bottom:0}.event-log li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-badge{display:inline-flex;align-items:center;justify-content:center;min-width:66px;min-height:20px;border-radius:999px;padding:2px 8px;border:1px solid #c7c1b8;background:#f2eee7;color:#4f5558;font-size:11px;white-space:nowrap}.status-running,.status-ok{border-color:#6ea77e;background:#e6f3e9;color:#245334}.status-blocked,.status-crashed,.status-missing,.status-empty{border-color:#c46e5f;background:#fae9e6;color:#6f2b21}.status-waiting,.status-starting,.status-incomplete{border-color:#c4a34e;background:#f7efcf;color:#604e16}.status-exited,.status-done,.status-resumable,.status-staged,.status-delivered,.status-acknowledged{border-color:#7b98b8;background:#e9f0f8;color:#2e4e70}.status-pending_approval,.status-queued,.status-translating,.status-ready,.status-create,.status-insert,.status-update{border-color:#c4a34e;background:#f7efcf;color:#604e16}.status-rejected,.status-failed,.status-cancelled,.status-blocked{border-color:#c46e5f;background:#fae9e6;color:#6f2b21}.status-pending{border-color:#c7c1b8;background:#f2eee7;color:#4f5558}.status-translated,.status-preserved{border-color:#7b98b8;background:#e9f0f8;color:#2e4e70}.empty-workspace{max-width:680px}@media(max-width:980px){.app-shell,.workspace-grid{grid-template-columns:1fr}.app-sidebar{border-right:0;border-bottom:1px solid #d3c9b8}.role-tabs{grid-template-columns:repeat(2,minmax(0,1fr))}.workflow-panel,.workflow-steps,.session-console-body.has-translation,.translation-settings-grid,.translation-prompt-editor-grid{grid-template-columns:1fr}}@media(max-width:560px){.app-main,.app-sidebar{padding:12px}.workspace-header,.inline-form,.inline-form.has-recent-paths{grid-template-columns:1fr;display:grid}.role-tabs{grid-template-columns:1fr}.terminal-frame,.terminal-empty{min-height:300px;height:54vh}.permission-mode-field{grid-template-columns:1fr;width:100%}}