vibe-coding-master 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -23
- package/dist/backend/api/app-settings-routes.js +8 -0
- package/dist/backend/api/message-routes.js +3 -1
- package/dist/backend/api/session-routes.js +7 -1
- package/dist/backend/api/task-routes.js +3 -10
- package/dist/backend/api/translation-routes.js +21 -3
- package/dist/backend/runtime/terminal-submit.js +20 -0
- package/dist/backend/server.js +8 -4
- package/dist/backend/services/app-settings-service.js +28 -0
- package/dist/backend/services/claude-transcript-service.js +12 -8
- package/dist/backend/services/command-dispatcher.js +2 -1
- package/dist/backend/services/message-service.js +10 -6
- package/dist/backend/services/project-service.js +0 -3
- package/dist/backend/services/session-service.js +7 -4
- package/dist/backend/services/task-service.js +65 -56
- package/dist/backend/services/translation-service.js +264 -77
- package/dist/shared/types/app-settings.js +1 -0
- package/dist-frontend/assets/index-B1vIIwLq.js +88 -0
- package/dist-frontend/assets/index-DPyKuEOz.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/cc-best-practices.md +4 -4
- package/docs/product-design.md +64 -29
- package/docs/v1-architecture-design.md +82 -48
- package/docs/v1-implementation-plan.md +73 -53
- package/package.json +3 -1
- package/dist/backend/ws/translation-ws.js +0 -35
- package/dist-frontend/assets/index-CuiNNOzj.css +0 -32
- package/dist-frontend/assets/index-D59GuHCR.js +0 -58
|
@@ -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
|
|
|
@@ -187,16 +187,16 @@ Defines:
|
|
|
187
187
|
- `TaskRecord`
|
|
188
188
|
- `CreateTaskRequest`
|
|
189
189
|
|
|
190
|
-
Current UI sends
|
|
190
|
+
Current UI sends `taskSlug` and `createWorktree`; the API type still permits optional `title` and `specPath`.
|
|
191
191
|
|
|
192
192
|
Worktree fields:
|
|
193
193
|
|
|
194
|
-
- `worktreePath
|
|
195
|
-
- `branch: feature/<taskSlug>`
|
|
194
|
+
- `worktreePath?: string`
|
|
195
|
+
- `branch: feature/<taskSlug>` when worktree creation is selected, otherwise the connected repo's current branch
|
|
196
196
|
- `cleanupStatus?: "active" | "cleaned"`
|
|
197
197
|
- `cleanedAt?: string`
|
|
198
198
|
|
|
199
|
-
`CreateTaskRequest` creates a worktree and branch by default
|
|
199
|
+
`CreateTaskRequest` supports `createWorktree?: boolean`. It creates a worktree and branch by default, and skips both when `createWorktree === false`.
|
|
200
200
|
|
|
201
201
|
### `src/shared/types/session.ts`
|
|
202
202
|
|
|
@@ -244,7 +244,10 @@ Defines:
|
|
|
244
244
|
- `SendTranslatedInputRequest`
|
|
245
245
|
- `TranslationProviderTestResult`
|
|
246
246
|
- `TranslationPromptPreview`
|
|
247
|
-
- `
|
|
247
|
+
- `TranslationSessionStatus`
|
|
248
|
+
- `TranslationSessionEvent`
|
|
249
|
+
- `StartTranslationSessionResult`
|
|
250
|
+
- `PollTranslationSessionResult`
|
|
248
251
|
|
|
249
252
|
Prompt keys:
|
|
250
253
|
|
|
@@ -520,39 +523,41 @@ Create flow:
|
|
|
520
523
|
```text
|
|
521
524
|
createTask(baseRepoRoot, { taskSlug })
|
|
522
525
|
-> assertValidTaskSlug(taskSlug)
|
|
523
|
-
-> branch = feature/<taskSlug>
|
|
524
|
-
-> worktreePath = <baseRepoRoot>/.ai/vcm/worktrees/<taskSlug>
|
|
525
526
|
-> assert .ai/vcm/ is ignored
|
|
526
|
-
->
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
527
|
+
-> if createWorktree is not false:
|
|
528
|
+
-> branch = feature/<taskSlug>
|
|
529
|
+
-> worktreePath = <baseRepoRoot>/.ai/vcm/worktrees/<taskSlug>
|
|
530
|
+
-> assert base repo has no uncommitted changes
|
|
531
|
+
-> assert branch does not exist
|
|
532
|
+
-> assert worktree path does not exist
|
|
533
|
+
-> git.createWorktree({ baseRepoRoot, branch, worktreePath, baseRef: HEAD })
|
|
534
|
+
-> taskRepoRoot = worktreePath
|
|
535
|
+
-> otherwise:
|
|
536
|
+
-> branch = current base repo branch
|
|
537
|
+
-> worktreePath = undefined
|
|
538
|
+
-> taskRepoRoot = baseRepoRoot
|
|
539
|
+
-> artifactService.ensureHandoffStructure({ repoRoot: taskRepoRoot, handoffDir })
|
|
540
|
+
-> artifactService.createArtifactTemplates({ repoRoot: taskRepoRoot, handoffDir })
|
|
541
|
+
-> ensure task runtime state dirs under <taskRepoRoot>/.ai/vcm/
|
|
532
542
|
-> write central task record under <baseRepoRoot>/.ai/vcm/tasks/<task>.json
|
|
533
543
|
```
|
|
534
544
|
|
|
535
|
-
|
|
545
|
+
Close Task flow:
|
|
536
546
|
|
|
537
547
|
```text
|
|
538
548
|
cleanupTask(baseRepoRoot, taskSlug, options)
|
|
539
|
-
-> require all role sessions stopped
|
|
540
549
|
-> load central task record
|
|
541
|
-
->
|
|
542
|
-
->
|
|
543
|
-
->
|
|
544
|
-
->
|
|
545
|
-
-> delete
|
|
546
|
-
-> delete
|
|
547
|
-
-> delete
|
|
548
|
-
-> delete
|
|
550
|
+
-> if worktreePath exists, verify it is under <baseRepoRoot>/.ai/vcm/worktrees/
|
|
551
|
+
-> if worktreePath exists, git.removeWorktree(baseRepoRoot, worktreePath, force=true)
|
|
552
|
+
-> if worktreePath exists, git.deleteBranch(baseRepoRoot, task.branch, force=true) by default
|
|
553
|
+
-> delete <baseRepoRoot>/.ai/vcm/tasks/<task>.json
|
|
554
|
+
-> delete <taskRepoRoot>/.ai/vcm/sessions/<task>.json
|
|
555
|
+
-> delete <taskRepoRoot>/.ai/vcm/messages/<task>.jsonl
|
|
556
|
+
-> delete <taskRepoRoot>/.ai/vcm/orchestration/<task>.json
|
|
557
|
+
-> delete <taskRepoRoot>/.ai/vcm/translation/<task>/
|
|
549
558
|
```
|
|
550
559
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
- keep `feature/<taskSlug>` by default
|
|
554
|
-
- optional `deleteBranch` only with explicit confirmation
|
|
555
|
-
- prefer requiring merged branch unless force-confirmed
|
|
560
|
+
The UI labels this operation `Close Task`, styles it as a red destructive action, and shows a browser confirmation that names the worktree, branch, and metadata that will be deleted. VCM does not check running sessions or uncommitted changes before closing. Tasks created without a worktree remove VCM metadata only.
|
|
556
561
|
|
|
557
562
|
### `src/backend/services/artifact-service.ts`
|
|
558
563
|
|
|
@@ -621,7 +626,7 @@ Responsibilities:
|
|
|
621
626
|
Persistence:
|
|
622
627
|
|
|
623
628
|
```text
|
|
624
|
-
<
|
|
629
|
+
<taskRepoRoot>/.ai/vcm/sessions/<task>.json
|
|
625
630
|
```
|
|
626
631
|
|
|
627
632
|
Environment passed to Claude Code:
|
|
@@ -634,7 +639,7 @@ Environment passed to Claude Code:
|
|
|
634
639
|
In task-worktree mode:
|
|
635
640
|
|
|
636
641
|
- session cwd is `task.worktreePath`
|
|
637
|
-
- session persistence
|
|
642
|
+
- session persistence is written under `task.worktreePath/.ai/vcm/sessions`
|
|
638
643
|
- raw logs and handoff artifacts are written under the task worktree
|
|
639
644
|
|
|
640
645
|
### `src/backend/services/message-service.ts`
|
|
@@ -658,7 +663,8 @@ Responsibilities:
|
|
|
658
663
|
|
|
659
664
|
In task-worktree mode:
|
|
660
665
|
|
|
661
|
-
- message snapshots
|
|
666
|
+
- message snapshots live under `task.worktreePath/.ai/vcm/messages`
|
|
667
|
+
- orchestration state lives under `task.worktreePath/.ai/vcm/orchestration`
|
|
662
668
|
- message body files live under `task.worktreePath/.ai/handoffs/<task>/messages`
|
|
663
669
|
- terminal delivery uses the runtime session for the role, whose cwd is the task worktree
|
|
664
670
|
|
|
@@ -700,6 +706,12 @@ Exports:
|
|
|
700
706
|
- `AppSettingsServiceDeps`
|
|
701
707
|
- `createAppSettingsService(deps)`
|
|
702
708
|
|
|
709
|
+
Settings responsibilities:
|
|
710
|
+
|
|
711
|
+
- persist UI theme mode: `system`, `light`, or `dark`
|
|
712
|
+
- persist translation settings and translation secrets
|
|
713
|
+
- persist up to five recent repository paths
|
|
714
|
+
|
|
703
715
|
Storage:
|
|
704
716
|
|
|
705
717
|
```text
|
|
@@ -770,10 +782,12 @@ Responsibilities:
|
|
|
770
782
|
- load/update translation settings
|
|
771
783
|
- expose prompt previews
|
|
772
784
|
- test provider
|
|
785
|
+
- start backend transcript listening for a role session
|
|
786
|
+
- poll cached translation events by cursor
|
|
773
787
|
- translate user input
|
|
774
788
|
- send English text to active terminal
|
|
775
|
-
-
|
|
776
|
-
-
|
|
789
|
+
- clear session entries and cached events
|
|
790
|
+
- stop session/task translation listeners
|
|
777
791
|
- retry failed output translation
|
|
778
792
|
- subscribe to Claude transcript service
|
|
779
793
|
- translate prose output and preserve tool output
|
|
@@ -792,17 +806,17 @@ Exports:
|
|
|
792
806
|
- `createDefaultServerDeps(options)`
|
|
793
807
|
- `getDefaultStaticDir()`
|
|
794
808
|
|
|
795
|
-
Registers
|
|
809
|
+
Registers HTTP routes and the terminal WebSocket.
|
|
796
810
|
|
|
797
811
|
### Route files
|
|
798
812
|
|
|
799
813
|
- `src/backend/api/project-routes.ts`: health, recent paths, connect/current project
|
|
800
814
|
- `src/backend/api/harness-routes.ts`: harness status/apply
|
|
801
|
-
- `src/backend/api/task-routes.ts`: tasks, task status,
|
|
815
|
+
- `src/backend/api/task-routes.ts`: tasks, task status, and Close Task cleanup endpoint
|
|
802
816
|
- `src/backend/api/session-routes.ts`: session lifecycle and dispatch compatibility endpoint
|
|
803
817
|
- `src/backend/api/artifact-routes.ts`: artifact, role command, and log reads/writes
|
|
804
818
|
- `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
|
|
819
|
+
- `src/backend/api/translation-routes.ts`: settings, prompt previews, provider test, start/poll, input/send, clear/retry
|
|
806
820
|
|
|
807
821
|
Worktree task API:
|
|
808
822
|
|
|
@@ -815,11 +829,10 @@ Do not add a "switch task worktree" endpoint. Worktree assignment happens only d
|
|
|
815
829
|
### WebSocket files
|
|
816
830
|
|
|
817
831
|
- `src/backend/ws/terminal-ws.ts`
|
|
818
|
-
- `src/backend/ws/translation-ws.ts`
|
|
819
832
|
|
|
820
833
|
Terminal WebSocket forwards PTY output/input/resize.
|
|
821
834
|
|
|
822
|
-
Translation WebSocket
|
|
835
|
+
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
836
|
|
|
824
837
|
## 10. Backend Templates
|
|
825
838
|
|
|
@@ -931,9 +944,9 @@ Responsibilities:
|
|
|
931
944
|
- messages modal
|
|
932
945
|
- events modal
|
|
933
946
|
- harness panel
|
|
934
|
-
- task creation with one task-name field,
|
|
947
|
+
- task creation with one task-name field, `Create worktree and branch` checkbox selected by default, branch preview, and worktree path preview
|
|
935
948
|
- task navigation
|
|
936
|
-
-
|
|
949
|
+
- red `Close Task` action with destructive confirmation
|
|
937
950
|
|
|
938
951
|
### `src/frontend/routes/task-workspace.tsx`
|
|
939
952
|
|
|
@@ -1068,6 +1081,7 @@ Important current behavior:
|
|
|
1068
1081
|
- no `Original` buttons
|
|
1069
1082
|
- tool output is preserved, dim, one-line
|
|
1070
1083
|
- prose source is replaced by translated text after completion
|
|
1084
|
+
- prose renders Markdown with GFM support
|
|
1071
1085
|
- no separate translated-English textarea
|
|
1072
1086
|
|
|
1073
1087
|
### `src/frontend/components/translation-settings-modal.tsx`
|
|
@@ -1079,18 +1093,15 @@ Exports:
|
|
|
1079
1093
|
|
|
1080
1094
|
Settings:
|
|
1081
1095
|
|
|
1082
|
-
- enable translation
|
|
1083
1096
|
- base URL
|
|
1084
1097
|
- API key as text input
|
|
1085
1098
|
- model
|
|
1086
1099
|
- target language
|
|
1087
|
-
- input mode
|
|
1088
1100
|
- context
|
|
1089
|
-
- translate output
|
|
1090
|
-
- translate user input
|
|
1091
1101
|
- timeout
|
|
1092
1102
|
- temperature
|
|
1093
|
-
-
|
|
1103
|
+
- direct editors for `zh-to-en`, `zh-to-en-with-context`, and `en-to-zh`
|
|
1104
|
+
- reset prompts to built-in defaults
|
|
1094
1105
|
- provider test
|
|
1095
1106
|
|
|
1096
1107
|
### `src/frontend/components/status-badge.tsx`
|
|
@@ -1114,7 +1125,8 @@ Sidebar:
|
|
|
1114
1125
|
|
|
1115
1126
|
- all groups default collapsed
|
|
1116
1127
|
- `Repository Path` default open only when no task is selected
|
|
1117
|
-
- `Settings` includes `Messages`, `Events`, and `Auto orchestration`
|
|
1128
|
+
- `Settings` includes `Theme`, `Messages`, `Events`, and `Auto orchestration`
|
|
1129
|
+
- `Theme` cycles through `System`, `Light`, and `Dark`; `System` follows the browser/OS color-scheme preference
|
|
1118
1130
|
|
|
1119
1131
|
Task workspace:
|
|
1120
1132
|
|
|
@@ -1139,6 +1151,8 @@ App settings:
|
|
|
1139
1151
|
~/.vcm/settings.json
|
|
1140
1152
|
```
|
|
1141
1153
|
|
|
1154
|
+
Contains UI theme preference, translation settings/secrets, and recent repository paths.
|
|
1155
|
+
|
|
1142
1156
|
Project config:
|
|
1143
1157
|
|
|
1144
1158
|
```text
|
|
@@ -1149,26 +1163,32 @@ Project config:
|
|
|
1149
1163
|
Task state:
|
|
1150
1164
|
|
|
1151
1165
|
```text
|
|
1152
|
-
|
|
1166
|
+
<baseRepoRoot>/.ai/vcm/tasks/<task>.json
|
|
1153
1167
|
```
|
|
1154
1168
|
|
|
1155
1169
|
Session state:
|
|
1156
1170
|
|
|
1157
1171
|
```text
|
|
1158
|
-
|
|
1172
|
+
<taskRepoRoot>/.ai/vcm/sessions/<task>.json
|
|
1159
1173
|
```
|
|
1160
1174
|
|
|
1161
1175
|
Messages:
|
|
1162
1176
|
|
|
1163
1177
|
```text
|
|
1164
|
-
|
|
1165
|
-
|
|
1178
|
+
<taskRepoRoot>/.ai/vcm/messages/<task>.jsonl
|
|
1179
|
+
<taskRepoRoot>/.ai/handoffs/<task>/messages/<message-id>.md
|
|
1166
1180
|
```
|
|
1167
1181
|
|
|
1168
1182
|
Orchestration:
|
|
1169
1183
|
|
|
1170
1184
|
```text
|
|
1171
|
-
|
|
1185
|
+
<taskRepoRoot>/.ai/vcm/orchestration/<task>.json
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
Translation cache:
|
|
1189
|
+
|
|
1190
|
+
```text
|
|
1191
|
+
<taskRepoRoot>/.ai/vcm/translation/<task>/
|
|
1172
1192
|
```
|
|
1173
1193
|
|
|
1174
1194
|
Task worktrees:
|
|
@@ -1217,7 +1237,7 @@ For frontend layout changes, also verify manually:
|
|
|
1217
1237
|
- Auto orchestration toggles on/off
|
|
1218
1238
|
- `Enter` in translation composer translates/sends
|
|
1219
1239
|
- `Shift+Enter` inserts newline
|
|
1220
|
-
-
|
|
1240
|
+
- close a worktree-backed task and verify it removes the worktree, deletes the task branch, and removes central task metadata
|
|
1221
1241
|
|
|
1222
1242
|
## 17. V1 Boundaries To Preserve
|
|
1223
1243
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe-coding-master",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
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%}}
|