vibe-coding-master 0.0.13 → 0.0.15
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 +36 -36
- package/dist/backend/api/claude-hook-routes.js +5 -0
- package/dist/backend/api/message-routes.js +8 -36
- package/dist/backend/server.js +12 -26
- package/dist/backend/services/artifact-service.js +27 -2
- package/dist/backend/services/claude-hook-service.js +67 -0
- package/dist/backend/services/harness-service.js +112 -0
- package/dist/backend/services/message-service.js +283 -175
- package/dist/backend/services/round-service.js +17 -127
- package/dist/backend/services/session-service.js +55 -2
- package/dist/backend/services/status-service.js +0 -76
- package/dist/backend/templates/handoff.js +3 -0
- package/dist/backend/templates/harness/architect-agent.js +5 -0
- package/dist/backend/templates/harness/claude-root.js +6 -2
- package/dist/backend/templates/harness/coder-agent.js +5 -0
- package/dist/backend/templates/harness/project-manager-agent.js +7 -3
- package/dist/backend/templates/harness/reviewer-agent.js +5 -0
- package/dist/backend/templates/message-envelope.js +7 -1
- package/dist/shared/types/claude-hook.js +1 -0
- package/dist-frontend/assets/{index-CyJrJge9.js → index-QhzvzTMZ.js} +43 -42
- package/dist-frontend/assets/index-jEkUTnIY.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/cc-best-practices.md +13 -4
- package/docs/product-design.md +74 -47
- package/docs/v1-architecture-design.md +53 -53
- package/docs/v1-implementation-plan.md +94 -61
- package/package.json +2 -3
- package/scripts/verify-package.mjs +0 -3
- package/dist/cli/vcmctl.js +0 -141
- package/dist-frontend/assets/index-N5DA0uE9.css +0 -32
|
@@ -28,10 +28,9 @@ File:
|
|
|
28
28
|
Current package facts:
|
|
29
29
|
|
|
30
30
|
- package name: `vibe-coding-master`
|
|
31
|
-
- current version: `0.0.
|
|
31
|
+
- current version: `0.0.14`
|
|
32
32
|
- type: ESM
|
|
33
33
|
- `bin.vcm`: `dist/main.js`
|
|
34
|
-
- `bin.vcmctl`: `dist/cli/vcmctl.js`
|
|
35
34
|
- published files: `dist`, `dist-frontend`, `docs`, `scripts`, `README.md`
|
|
36
35
|
|
|
37
36
|
Scripts:
|
|
@@ -59,8 +58,6 @@ It verifies required files, shebangs, packaged static path behavior, and fronten
|
|
|
59
58
|
```text
|
|
60
59
|
src/
|
|
61
60
|
main.ts
|
|
62
|
-
cli/
|
|
63
|
-
vcmctl.ts
|
|
64
61
|
shared/
|
|
65
62
|
constants.ts
|
|
66
63
|
types/
|
|
@@ -108,23 +105,24 @@ Responsibilities:
|
|
|
108
105
|
- serve `dist-frontend` through backend in production mode
|
|
109
106
|
- close backend/Vite on `SIGINT`
|
|
110
107
|
|
|
111
|
-
###
|
|
108
|
+
### Removed CLI Surface
|
|
112
109
|
|
|
113
|
-
CLI
|
|
110
|
+
The target design removes `vcmctl` completely. Role sessions must not call a VCM CLI to send messages or hook events.
|
|
114
111
|
|
|
115
|
-
|
|
116
|
-
- `vcmctl reply`
|
|
117
|
-
- `vcmctl result`
|
|
118
|
-
- `vcmctl inbox`
|
|
119
|
-
- `vcmctl ready`
|
|
112
|
+
Remove:
|
|
120
113
|
|
|
121
|
-
|
|
114
|
+
- `src/cli/vcmctl.ts`
|
|
115
|
+
- `bin.vcmctl` from `package.json`
|
|
116
|
+
- packaged `dist/cli/vcmctl.js`
|
|
117
|
+
- `VCM_CTL_COMMAND` session environment
|
|
118
|
+
- harness rules that mention `vcmctl`
|
|
119
|
+
|
|
120
|
+
Role sessions still receive enough environment for direct hook POSTs and backend-aware terminal delivery:
|
|
122
121
|
|
|
123
122
|
- `VCM_API_URL`
|
|
124
123
|
- `VCM_TASK_SLUG`
|
|
125
124
|
- `VCM_ROLE`
|
|
126
|
-
|
|
127
|
-
Role sessions receive these env vars from `SessionService`.
|
|
125
|
+
- `VCM_SESSION_ID`
|
|
128
126
|
|
|
129
127
|
## 5. Shared Layer
|
|
130
128
|
|
|
@@ -239,10 +237,10 @@ Defines:
|
|
|
239
237
|
- `VcmOrchestrationMode`
|
|
240
238
|
- `VcmRoleMessage`
|
|
241
239
|
- `VcmOrchestrationState`
|
|
242
|
-
- `
|
|
243
|
-
- `
|
|
240
|
+
- `VcmRouteFile`
|
|
241
|
+
- `VcmRouteFileDispatchResult`
|
|
244
242
|
|
|
245
|
-
The state type
|
|
243
|
+
The state type should only expose the current manual/auto mode. Remove compatibility-only paused state when deleting `vcmctl`.
|
|
246
244
|
|
|
247
245
|
### `src/shared/types/round.ts`
|
|
248
246
|
|
|
@@ -304,10 +302,6 @@ Defines:
|
|
|
304
302
|
|
|
305
303
|
- `ApiErrorResponse`
|
|
306
304
|
- `TaskStatusReport`
|
|
307
|
-
- `TaskWorkflowStepId`
|
|
308
|
-
- `TaskWorkflowStepStatus`
|
|
309
|
-
- `TaskWorkflowStep`
|
|
310
|
-
- `TaskWorkflowReport`
|
|
311
305
|
- `DispatchRoleCommandResult`
|
|
312
306
|
- `BootstrapState`
|
|
313
307
|
|
|
@@ -640,7 +634,7 @@ Responsibilities:
|
|
|
640
634
|
- assemble `TaskStatusReport`
|
|
641
635
|
- list sessions
|
|
642
636
|
- list artifact checks
|
|
643
|
-
-
|
|
637
|
+
- emit missing/incomplete artifact warnings
|
|
644
638
|
|
|
645
639
|
### `src/backend/services/session-service.ts`
|
|
646
640
|
|
|
@@ -683,32 +677,77 @@ In task-worktree mode:
|
|
|
683
677
|
Exports:
|
|
684
678
|
|
|
685
679
|
- `MessageService`
|
|
686
|
-
-
|
|
680
|
+
- `ScanPendingRouteFilesInput`
|
|
681
|
+
- `DispatchPendingRouteFileInput`
|
|
682
|
+
- `RouteFileRecord`
|
|
687
683
|
- `createMessageService(deps)`
|
|
688
684
|
|
|
689
685
|
Responsibilities:
|
|
690
686
|
|
|
691
687
|
- list messages
|
|
692
|
-
-
|
|
693
|
-
-
|
|
688
|
+
- scan route-file outboxes after Claude Code `Stop`
|
|
689
|
+
- snapshot pending route-file messages
|
|
690
|
+
- mark all open messages done for manual recovery
|
|
694
691
|
- get/update orchestration state
|
|
695
692
|
- enforce message policy
|
|
696
693
|
- enforce per-target-role in-flight delivery
|
|
697
|
-
-
|
|
698
|
-
-
|
|
699
|
-
- deliver the next queued message for that role after acknowledgement
|
|
694
|
+
- leave non-empty route files pending when a target role is busy, unavailable, or terminal submission fails
|
|
695
|
+
- deliver the next pending route file when a target role becomes idle
|
|
700
696
|
- persist message snapshots
|
|
701
|
-
-
|
|
702
|
-
-
|
|
697
|
+
- archive dispatched route-file bodies before clearing source files
|
|
698
|
+
- submit route-file messages to the target terminal
|
|
703
699
|
|
|
704
700
|
In task-worktree mode:
|
|
705
701
|
|
|
706
702
|
- message snapshots live under `task.worktreePath/.ai/vcm/messages`
|
|
707
703
|
- orchestration state lives under `task.worktreePath/.ai/vcm/orchestration`
|
|
708
|
-
-
|
|
704
|
+
- pending route files live under `task.worktreePath/.ai/vcm/handoffs/messages`
|
|
709
705
|
- terminal delivery uses the runtime session for the role, whose cwd is the task worktree
|
|
710
706
|
- message mutations are serialized per task inside the VCM process to avoid concurrent API calls bypassing the in-flight check
|
|
711
707
|
|
|
708
|
+
Route-file protocol:
|
|
709
|
+
|
|
710
|
+
- route file name is `<from-role>-<to-role>.md`
|
|
711
|
+
- the filename is the authoritative route; frontmatter cannot override `from` or `to`
|
|
712
|
+
- blank or whitespace-only files are ignored
|
|
713
|
+
- non-empty files are pending
|
|
714
|
+
- each directed route has exactly one pending file, so repeated sends by the same role to the same target become edits to the same file
|
|
715
|
+
- VCM scans after every `Stop` hook
|
|
716
|
+
- VCM scans the stopped role's outgoing files and pending files targeting newly idle roles
|
|
717
|
+
- VCM delivers at most one route file per target role per scan
|
|
718
|
+
- if several files target the same idle role, choose oldest modified time, then route name
|
|
719
|
+
- successful terminal submission snapshots the body, stores `submitted`, then clears the source route file
|
|
720
|
+
- failed, blocked, manual, or unavailable delivery leaves the source route file unchanged
|
|
721
|
+
- `markAllDone` appends `acknowledged` snapshots and may clear pending route files only after user confirmation
|
|
722
|
+
|
|
723
|
+
Required service functions:
|
|
724
|
+
|
|
725
|
+
- `listMessages(taskSlug): Promise<VcmRoleMessage[]>`
|
|
726
|
+
- `listPendingRouteFiles(repoRoot, taskSlug): Promise<RouteFileRecord[]>`
|
|
727
|
+
- `scanAndDispatchPendingRouteFiles(input: { repoRoot: string; taskSlug: string; stoppedRole: RoleName }): Promise<void>`
|
|
728
|
+
- `readRouteFile(path): Promise<RouteFileRecord | null>`
|
|
729
|
+
- `dispatchPendingRouteFile(record): Promise<VcmRouteFileDispatchResult>`
|
|
730
|
+
- `appendMessageSnapshot(message): Promise<void>`
|
|
731
|
+
- `clearRouteFile(path): Promise<void>`
|
|
732
|
+
- `markAllDone(taskSlug, options): Promise<MarkAllMessagesDoneResult>`
|
|
733
|
+
|
|
734
|
+
`dispatchPendingRouteFile` must snapshot before clearing. Clearing means truncating the route file to an empty string, not deleting it.
|
|
735
|
+
|
|
736
|
+
### `src/backend/services/claude-hook-service.ts`
|
|
737
|
+
|
|
738
|
+
Exports:
|
|
739
|
+
|
|
740
|
+
- `ClaudeHookService`
|
|
741
|
+
- `createClaudeHookService(deps)`
|
|
742
|
+
|
|
743
|
+
Responsibilities:
|
|
744
|
+
|
|
745
|
+
- accept Claude Code `Stop` hook events directly over HTTP
|
|
746
|
+
- map hook events to the current VCM project, task, role, and persisted Claude session id
|
|
747
|
+
- update `RoleSessionRecord.activityStatus`: `Stop -> idle`
|
|
748
|
+
- call `MessageService.scanAndDispatchPendingRouteFiles` after recording the stop event
|
|
749
|
+
- never require `vcmctl hook-event`
|
|
750
|
+
|
|
712
751
|
### `src/backend/services/command-dispatcher.ts`
|
|
713
752
|
|
|
714
753
|
Exports:
|
|
@@ -718,7 +757,7 @@ Exports:
|
|
|
718
757
|
- `CommandDispatcherDeps`
|
|
719
758
|
- `createCommandDispatcher(deps)`
|
|
720
759
|
|
|
721
|
-
|
|
760
|
+
Obsolete role-command dispatch path. Remove this as part of the route-file messaging migration; preferred orchestration is `MessageService` scanning `.ai/vcm/handoffs/messages/<from-role>-<to-role>.md` after `Stop`.
|
|
722
761
|
|
|
723
762
|
### `src/backend/services/harness-service.ts`
|
|
724
763
|
|
|
@@ -846,13 +885,12 @@ Exports:
|
|
|
846
885
|
|
|
847
886
|
Responsibilities:
|
|
848
887
|
|
|
849
|
-
-
|
|
850
|
-
-
|
|
851
|
-
-
|
|
888
|
+
- read hook-driven role activity from `RoleSessionRecord`
|
|
889
|
+
- map `activityStatus: "running"` to role state `answering`
|
|
890
|
+
- map `activityStatus: "idle"` plus `lastStopAt` to role state `idle`
|
|
852
891
|
- evaluate task-level round completion from messages plus role states
|
|
853
|
-
- wait for the latest
|
|
892
|
+
- wait for the latest active submitted target role in PM -> role -> PM chains
|
|
854
893
|
- ignore completion while queued or pending messages exist
|
|
855
|
-
- stop trackers on session stop/restart and task close
|
|
856
894
|
|
|
857
895
|
## 9. Backend API
|
|
858
896
|
|
|
@@ -876,9 +914,10 @@ Registers HTTP routes and the terminal WebSocket.
|
|
|
876
914
|
- `src/backend/api/project-routes.ts`: health, recent paths, connect/current project
|
|
877
915
|
- `src/backend/api/harness-routes.ts`: harness status/apply
|
|
878
916
|
- `src/backend/api/task-routes.ts`: tasks, task status, and Close Task cleanup endpoint; Close Task stops running role sessions before translation/task cleanup
|
|
879
|
-
- `src/backend/api/session-routes.ts`: session lifecycle
|
|
880
|
-
- `src/backend/api/artifact-routes.ts`: artifact
|
|
881
|
-
- `src/backend/api/message-routes.ts`:
|
|
917
|
+
- `src/backend/api/session-routes.ts`: session lifecycle
|
|
918
|
+
- `src/backend/api/artifact-routes.ts`: artifact and log reads/writes
|
|
919
|
+
- `src/backend/api/message-routes.ts`: message history, pending route files, Mark All Done recovery, and orchestration
|
|
920
|
+
- `src/backend/api/claude-hook-routes.ts`: Claude Code `Stop` hook receiver
|
|
882
921
|
- `src/backend/api/round-routes.ts`: task round completion state
|
|
883
922
|
- `src/backend/api/translation-routes.ts`: settings, prompt previews, provider test, start/poll, input/send, clear/retry
|
|
884
923
|
|
|
@@ -898,7 +937,7 @@ Terminal WebSocket forwards PTY output/input/resize.
|
|
|
898
937
|
|
|
899
938
|
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.
|
|
900
939
|
|
|
901
|
-
Round completion is HTTP-polled too. `GET /api/tasks/:taskSlug/round` returns `VcmTaskRoundState` from
|
|
940
|
+
Round completion is HTTP-polled too. `GET /api/tasks/:taskSlug/round` returns `VcmTaskRoundState` from hook-driven role activity and message state; it is not a WebSocket stream.
|
|
902
941
|
|
|
903
942
|
## 10. Backend Templates
|
|
904
943
|
|
|
@@ -955,6 +994,7 @@ It calls:
|
|
|
955
994
|
- artifact endpoints
|
|
956
995
|
- message endpoints
|
|
957
996
|
- orchestration endpoints
|
|
997
|
+
- route-file inspection endpoints for the Messages modal
|
|
958
998
|
- round completion endpoint
|
|
959
999
|
- translation endpoints
|
|
960
1000
|
|
|
@@ -993,7 +1033,7 @@ Responsibilities:
|
|
|
993
1033
|
- load tasks and harness status after connect
|
|
994
1034
|
- pass sidebar props to `ProjectDashboard`
|
|
995
1035
|
- pass task props to `TaskWorkspace`
|
|
996
|
-
- keep active
|
|
1036
|
+
- keep active messages/orchestration/events synchronized by task
|
|
997
1037
|
- dedupe task round `completionId` values and show/play completion alerts when enabled
|
|
998
1038
|
|
|
999
1039
|
### `src/frontend/routes/project-dashboard.tsx`
|
|
@@ -1008,9 +1048,9 @@ Responsibilities:
|
|
|
1008
1048
|
- collapsible sidebar
|
|
1009
1049
|
- repository connect form
|
|
1010
1050
|
- repository summary
|
|
1011
|
-
- workflow panel
|
|
1012
1051
|
- settings section
|
|
1013
1052
|
- round completion alert toggle
|
|
1053
|
+
- try alert test button
|
|
1014
1054
|
- messages modal
|
|
1015
1055
|
- events modal
|
|
1016
1056
|
- harness panel
|
|
@@ -1070,15 +1110,6 @@ Exports:
|
|
|
1070
1110
|
|
|
1071
1111
|
Shows harness status and install/update action.
|
|
1072
1112
|
|
|
1073
|
-
### `src/frontend/components/workflow-panel.tsx`
|
|
1074
|
-
|
|
1075
|
-
Exports:
|
|
1076
|
-
|
|
1077
|
-
- `WorkflowPanelProps`
|
|
1078
|
-
- `WorkflowPanel({ workflow })`
|
|
1079
|
-
|
|
1080
|
-
Renders the sidebar workflow steps.
|
|
1081
|
-
|
|
1082
1113
|
### `src/frontend/components/message-timeline.tsx`
|
|
1083
1114
|
|
|
1084
1115
|
Exports:
|
|
@@ -1087,7 +1118,7 @@ Exports:
|
|
|
1087
1118
|
- `getMessageCounts(messages)`
|
|
1088
1119
|
- `MessageTimeline(props)`
|
|
1089
1120
|
|
|
1090
|
-
Used inside the Messages modal. Current UI rows show sequence, timestamp, route, type, status, body preview,
|
|
1121
|
+
Used inside the Messages modal. Current UI rows show sequence, timestamp, route, type, status, body preview, source route file path, and a `Copy` button. The modal header includes `Mark All Done` for manual recovery from stuck queued/in-flight messages. Stage/approve/reject backend APIs should be removed with the route-file migration.
|
|
1091
1122
|
|
|
1092
1123
|
### `src/frontend/components/event-log.tsx`
|
|
1093
1124
|
|
|
@@ -1134,7 +1165,7 @@ Exports:
|
|
|
1134
1165
|
|
|
1135
1166
|
Renders permission select and session lifecycle buttons.
|
|
1136
1167
|
|
|
1137
|
-
There is no visible primary `Send Command` button in the current toolbar. Role-command dispatch
|
|
1168
|
+
There is no visible primary `Send Command` button in the current toolbar. Role-command dispatch should be removed with the route-file migration.
|
|
1138
1169
|
|
|
1139
1170
|
### `src/frontend/components/translation-panel.tsx`
|
|
1140
1171
|
|
|
@@ -1197,23 +1228,23 @@ Sidebar:
|
|
|
1197
1228
|
|
|
1198
1229
|
- all groups default collapsed
|
|
1199
1230
|
- `Repository Path` default open only when no task is selected
|
|
1200
|
-
- `Settings` includes `Theme`, `Round alert`, `Messages`, and `Events`
|
|
1231
|
+
- `Settings` includes `Theme`, `Round alert`, `Try alert`, `Messages`, and `Events`
|
|
1201
1232
|
- `Theme` cycles through `System`, `Light`, and `Dark`; `System` follows the browser/OS color-scheme preference
|
|
1202
1233
|
- `Round alert` is on by default and controls the in-app completion prompt plus short sound
|
|
1234
|
+
- `Try alert` calls the same prompt/sound path without waiting for a real completed round
|
|
1203
1235
|
|
|
1204
1236
|
Task workspace:
|
|
1205
1237
|
|
|
1206
1238
|
- role tabs in the first header row
|
|
1207
|
-
- workflow is not in main workspace
|
|
1208
1239
|
- messages/events are not in main workspace
|
|
1209
1240
|
- active role console fills available space
|
|
1210
1241
|
- `Auto orchestration` appears immediately to the left of `Translate` and uses the same compact toggle style
|
|
1211
1242
|
|
|
1212
1243
|
Round completion:
|
|
1213
1244
|
|
|
1214
|
-
-
|
|
1215
|
-
-
|
|
1216
|
-
- PM -> role -> PM chains complete after PM's final `
|
|
1245
|
+
- task state follows the latest active submitted message target role
|
|
1246
|
+
- role activity uses VCM terminal submit -> `running` and Claude Code `Stop` -> `idle`
|
|
1247
|
+
- PM -> role -> PM chains complete after PM's final hook `Stop`, not after the intermediate role's `Stop`
|
|
1217
1248
|
- queued or pending messages keep the round active
|
|
1218
1249
|
- frontend dedupes `completionId` before showing a prompt or playing sound
|
|
1219
1250
|
|
|
@@ -1258,7 +1289,7 @@ Messages:
|
|
|
1258
1289
|
|
|
1259
1290
|
```text
|
|
1260
1291
|
<taskRepoRoot>/.ai/vcm/messages/<task>.jsonl
|
|
1261
|
-
<taskRepoRoot>/.ai/vcm/handoffs/messages/<
|
|
1292
|
+
<taskRepoRoot>/.ai/vcm/handoffs/messages/<from-role>-<to-role>.md
|
|
1262
1293
|
```
|
|
1263
1294
|
|
|
1264
1295
|
Orchestration:
|
|
@@ -1317,7 +1348,7 @@ For frontend layout changes, also verify manually:
|
|
|
1317
1348
|
- Messages modal opens from sidebar Settings
|
|
1318
1349
|
- Events modal opens from sidebar Settings
|
|
1319
1350
|
- Auto orchestration toggles on/off from the role toolbar
|
|
1320
|
-
- Auto orchestration switches to the target role tab when a
|
|
1351
|
+
- Auto orchestration switches to the target role tab when VCM submits a route-file message
|
|
1321
1352
|
- Round alert can be toggled from sidebar Settings and fires once after a chained round truly completes
|
|
1322
1353
|
- `Enter` in translation composer translates/sends
|
|
1323
1354
|
- `Shift+Enter` inserts newline
|
|
@@ -1337,6 +1368,8 @@ Do not reintroduce these into V1 docs or UI unless the product direction changes
|
|
|
1337
1368
|
- optional title input in New Task
|
|
1338
1369
|
- `Dirty: yes/no` sidebar label
|
|
1339
1370
|
- role command dispatch as the primary orchestration path
|
|
1371
|
+
- `vcmctl` as an active CLI or agent-facing message path
|
|
1372
|
+
- `UserPromptSubmit` as the delivery confirmation mechanism
|
|
1340
1373
|
- per-role worktrees
|
|
1341
1374
|
- switching a task to another branch/worktree after creation
|
|
1342
1375
|
- a separate `Create task worktree` button outside task creation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe-coding-master",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Local GUI session cockpit for Claude Code role sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
"README.md"
|
|
12
12
|
],
|
|
13
13
|
"bin": {
|
|
14
|
-
"vcm": "dist/main.js"
|
|
15
|
-
"vcmctl": "dist/cli/vcmctl.js"
|
|
14
|
+
"vcm": "dist/main.js"
|
|
16
15
|
},
|
|
17
16
|
"engines": {
|
|
18
17
|
"node": "^20.0.0 || >=22.0.0"
|
|
@@ -7,7 +7,6 @@ const requiredFiles = [
|
|
|
7
7
|
"package.json",
|
|
8
8
|
"scripts/fix-node-pty-spawn-helper.mjs",
|
|
9
9
|
"dist/main.js",
|
|
10
|
-
"dist/cli/vcmctl.js",
|
|
11
10
|
"dist/backend/server.js",
|
|
12
11
|
"dist/backend/api/harness-routes.js",
|
|
13
12
|
"dist/backend/runtime/node-pty-runtime.js",
|
|
@@ -40,14 +39,12 @@ async function main() {
|
|
|
40
39
|
const pkg = JSON.parse(await readText("package.json"));
|
|
41
40
|
assertArrayIncludes(pkg.files, requiredFileEntries, "package.json files");
|
|
42
41
|
assertEqual(pkg.bin?.vcm, "dist/main.js", "package.json bin.vcm");
|
|
43
|
-
assertEqual(pkg.bin?.vcmctl, "dist/cli/vcmctl.js", "package.json bin.vcmctl");
|
|
44
42
|
|
|
45
43
|
for (const file of requiredFiles) {
|
|
46
44
|
await assertFile(file);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
await assertStartsWith("dist/main.js", "#!/usr/bin/env node", "vcm bin shebang");
|
|
50
|
-
await assertStartsWith("dist/cli/vcmctl.js", "#!/usr/bin/env node", "vcmctl bin shebang");
|
|
51
48
|
|
|
52
49
|
const server = await readText("dist/backend/server.js");
|
|
53
50
|
assertIncludes(server, 'path.join(getAppRoot(), "dist-frontend")', "packaged static dir must resolve from app root");
|
package/dist/cli/vcmctl.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
async function main(argv = process.argv.slice(2)) {
|
|
4
|
-
const [command, ...rest] = argv;
|
|
5
|
-
if (!command || command === "--help" || command === "-h") {
|
|
6
|
-
printHelp();
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
if (command === "send") {
|
|
10
|
-
const options = parseOptions(rest);
|
|
11
|
-
await sendMessage({
|
|
12
|
-
fromRole: getEnvRole(),
|
|
13
|
-
toRole: requireOption(options.to, "--to"),
|
|
14
|
-
type: options.type ?? "task",
|
|
15
|
-
body: await resolveBody(options),
|
|
16
|
-
artifactRefs: options.artifactRefs
|
|
17
|
-
});
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (command === "reply" || command === "result") {
|
|
21
|
-
const options = parseOptions(rest);
|
|
22
|
-
await sendMessage({
|
|
23
|
-
fromRole: getEnvRole(),
|
|
24
|
-
toRole: "project-manager",
|
|
25
|
-
type: command === "result" ? "result" : options.type ?? "question",
|
|
26
|
-
body: await resolveBody(options),
|
|
27
|
-
artifactRefs: options.artifactRefs
|
|
28
|
-
});
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (command === "inbox") {
|
|
32
|
-
const response = await fetchJson(`${getApiUrl()}/api/tasks/${encodeURIComponent(getTaskSlug())}/messages`);
|
|
33
|
-
console.log(JSON.stringify(response, null, 2));
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (command === "ready") {
|
|
37
|
-
console.log("ready: VCM readiness signaling is planned for a later phase.");
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
throw new Error(`Unknown vcmctl command: ${command}`);
|
|
41
|
-
}
|
|
42
|
-
async function sendMessage(input) {
|
|
43
|
-
const result = await fetchJson(`${getApiUrl()}/api/tasks/${encodeURIComponent(getTaskSlug())}/messages`, {
|
|
44
|
-
method: "POST",
|
|
45
|
-
body: JSON.stringify(input),
|
|
46
|
-
headers: {
|
|
47
|
-
"content-type": "application/json"
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
console.log(JSON.stringify(result, null, 2));
|
|
51
|
-
}
|
|
52
|
-
function parseOptions(args) {
|
|
53
|
-
const options = {
|
|
54
|
-
artifactRefs: []
|
|
55
|
-
};
|
|
56
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
57
|
-
const arg = args[index];
|
|
58
|
-
if (arg === "--to") {
|
|
59
|
-
options.to = requireValue(args, index += 1);
|
|
60
|
-
}
|
|
61
|
-
else if (arg === "--type") {
|
|
62
|
-
options.type = requireValue(args, index += 1);
|
|
63
|
-
}
|
|
64
|
-
else if (arg === "--body") {
|
|
65
|
-
options.body = requireValue(args, index += 1);
|
|
66
|
-
}
|
|
67
|
-
else if (arg === "--body-file") {
|
|
68
|
-
options.bodyFile = requireValue(args, index += 1);
|
|
69
|
-
}
|
|
70
|
-
else if (arg === "--artifact") {
|
|
71
|
-
options.artifactRefs.push(requireValue(args, index += 1));
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
throw new Error(`Unknown option: ${arg}`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return options;
|
|
78
|
-
}
|
|
79
|
-
async function resolveBody(options) {
|
|
80
|
-
if (options.bodyFile) {
|
|
81
|
-
return fs.readFile(options.bodyFile, "utf8");
|
|
82
|
-
}
|
|
83
|
-
if (options.body) {
|
|
84
|
-
return options.body;
|
|
85
|
-
}
|
|
86
|
-
throw new Error("Message body is required. Use --body or --body-file.");
|
|
87
|
-
}
|
|
88
|
-
function requireValue(args, index) {
|
|
89
|
-
const value = args[index];
|
|
90
|
-
if (!value) {
|
|
91
|
-
throw new Error("Missing option value.");
|
|
92
|
-
}
|
|
93
|
-
return value;
|
|
94
|
-
}
|
|
95
|
-
function requireOption(value, name) {
|
|
96
|
-
if (!value) {
|
|
97
|
-
throw new Error(`Missing required option: ${name}`);
|
|
98
|
-
}
|
|
99
|
-
return value;
|
|
100
|
-
}
|
|
101
|
-
async function fetchJson(url, init = {}) {
|
|
102
|
-
const response = await fetch(url, init);
|
|
103
|
-
if (!response.ok) {
|
|
104
|
-
const payload = await response.json().catch(() => null);
|
|
105
|
-
const message = payload?.error?.hint
|
|
106
|
-
? `${payload.error.message} ${payload.error.hint}`
|
|
107
|
-
: payload?.error?.message ?? `Request failed: ${response.status}`;
|
|
108
|
-
throw new Error(message);
|
|
109
|
-
}
|
|
110
|
-
return response.json();
|
|
111
|
-
}
|
|
112
|
-
function getApiUrl() {
|
|
113
|
-
return process.env.VCM_API_URL ?? "http://127.0.0.1:4173";
|
|
114
|
-
}
|
|
115
|
-
function getTaskSlug() {
|
|
116
|
-
return requireEnv("VCM_TASK_SLUG");
|
|
117
|
-
}
|
|
118
|
-
function getEnvRole() {
|
|
119
|
-
return requireEnv("VCM_ROLE");
|
|
120
|
-
}
|
|
121
|
-
function requireEnv(name) {
|
|
122
|
-
const value = process.env[name];
|
|
123
|
-
if (!value) {
|
|
124
|
-
throw new Error(`${name} is not set.`);
|
|
125
|
-
}
|
|
126
|
-
return value;
|
|
127
|
-
}
|
|
128
|
-
function printHelp() {
|
|
129
|
-
console.log(`vcmctl
|
|
130
|
-
|
|
131
|
-
Usage:
|
|
132
|
-
vcmctl send --to coder --type task --body-file /tmp/message.md
|
|
133
|
-
vcmctl reply --type blocked --body "Need clarification."
|
|
134
|
-
vcmctl result --body-file /tmp/result.md --artifact .ai/vcm/handoffs/implementation-log.md
|
|
135
|
-
vcmctl inbox
|
|
136
|
-
`);
|
|
137
|
-
}
|
|
138
|
-
main().catch((error) => {
|
|
139
|
-
console.error(error.message);
|
|
140
|
-
process.exit(1);
|
|
141
|
-
});
|
|
@@ -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}.danger-button{border-color:#b84a45;background:#fff1ee;color:#8d211d;font-weight:750}.danger-button:hover:not(:disabled){border-color:#8d211d;background:#ffe2dc}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}.round-notice{position:fixed;right:20px;bottom:20px;z-index:30;display:grid;gap:2px;min-width:220px;max-width:min(360px,calc(100vw - 40px));border:1px solid #2f6f73;border-radius:8px;background:#e8f4f2;color:#123f43;box-shadow:0 14px 32px #1f242b29;padding:10px 12px}.round-notice strong{font-size:13px}.round-notice span{font-size: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-option{display:flex;align-items:center;gap:8px;color:#1f242b;font-size:13px;font-weight:700}.task-create-option input{width:16px;height:16px;margin:0}.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}.sidebar-settings .theme-mode-toggle span:last-child{font-weight:750}.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;height:100%;min-height:0}.session-console-top{display:flex;gap:10px;align-items:center;justify-content:space-between}.session-console-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}.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{display:grid;grid-template-columns:minmax(0,1fr);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{display:grid;min-width:0;min-height:0;height:100%}.terminal-frame,.terminal-empty{width:100%;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,.translation-status-row{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-status-row{flex-wrap:nowrap}.translation-status-row p:last-child{flex:0 0 auto;text-align:right}.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.is-user-input{border-top:4px solid #3a4149;margin-top:14px;padding-top:14px}.translation-entry.is-user-input:first-child{margin-top: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-markdown{max-width:100%;min-width:0;color:#d6deeb;font-size:12px;line-height:1.55;overflow-wrap:anywhere}.translation-markdown>:first-child{margin-top:0}.translation-markdown>:last-child{margin-bottom:0}.translation-markdown p,.translation-markdown ul,.translation-markdown ol,.translation-markdown blockquote,.translation-markdown pre,.translation-markdown table{margin:0 0 8px}.translation-markdown h1,.translation-markdown h2,.translation-markdown h3,.translation-markdown h4,.translation-markdown h5,.translation-markdown h6{margin:10px 0 6px;color:#f0f6fc;font-weight:700;line-height:1.25}.translation-markdown h1{font-size:17px}.translation-markdown h2{font-size:15px}.translation-markdown h3,.translation-markdown h4,.translation-markdown h5,.translation-markdown h6{font-size:13px}.translation-markdown ul,.translation-markdown ol{padding-left:22px}.translation-markdown li{margin:2px 0}.translation-markdown .task-list-item{list-style:none}.translation-markdown input[type=checkbox]{width:13px;height:13px;margin:0 6px 0 0;accent-color:#56d364}.translation-markdown blockquote{border-left:3px solid #3a4149;color:#b7c0ca;padding-left:10px}.translation-markdown a{color:#79c0ff}.translation-markdown code{border-radius:4px;background:#161b22;color:#f0f6fc;padding:1px 4px;font-family:Menlo,Monaco,Consolas,monospace;font-size:.95em}.translation-markdown pre{overflow-x:auto;border:1px solid #292d31;border-radius:6px;background:#111316;padding:8px;white-space:pre}.translation-markdown pre code{background:transparent;padding:0}.translation-markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse}.translation-markdown img{max-width:100%;border-radius:4px}.translation-markdown th,.translation-markdown td{border:1px solid #30363d;padding:4px 6px;text-align:left;vertical-align:top}.translation-markdown hr{border:0;border-top:1px solid #292d31;margin:10px 0}.translation-entry.is-tool-output pre{display:block;overflow:hidden;color:#7d8590;text-overflow:ellipsis;white-space:nowrap;width:100%}.translation-entry-note{margin:2px 0 0;color:#6e7681;font-size:10px;line-height:1.35}.translation-entry-note.is-error{color:#a3715f}.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(980px,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-stack{display:grid;gap:10px}.translation-prompt-stack label{display:grid;gap:4px}.translation-prompt-stack span{color:#4f5558;font-size:12px;font-weight:650}.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 time,.message-meta span:not(.status-badge),.message-reason,.message-path{color:#667071;font-size:12px}.message-sequence{min-width:32px;color:#1f242b;font-weight:800}.message-actions button{min-height:30px;padding:4px 10px}.message-item p{display:-webkit-box;margin-bottom:4px;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}.message-reason,.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{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%}}:root[data-theme=dark]{color-scheme:dark;background:#0d1117;color:#e6edf3}:root[data-theme=dark] body,:root[data-theme=dark] .app-main{background:#0d1117;color:#e6edf3}:root[data-theme=dark] button{border-color:#3d4652;background:#161b22;color:#e6edf3}:root[data-theme=dark] button:hover:not(:disabled){border-color:#58a6ff;background:#1f2937}:root[data-theme=dark] input,:root[data-theme=dark] select,:root[data-theme=dark] textarea:not(.xterm-helper-textarea){border-color:#3d4652;background:#0d1117;color:#e6edf3}:root[data-theme=dark] input::placeholder,:root[data-theme=dark] textarea::placeholder{color:#7d8590}:root[data-theme=dark] .danger-button{border-color:#da7b72;background:#2d1518;color:#ffdcd7}:root[data-theme=dark] .danger-button:hover:not(:disabled){border-color:#ffa198;background:#3d1f23}:root[data-theme=dark] .app-sidebar{border-color:#30363d;background:#0f141b}:root[data-theme=dark] .sidebar-toggle{background:#161b22}:root[data-theme=dark] .brand-header span,:root[data-theme=dark] .muted,:root[data-theme=dark] .workspace-branch,:root[data-theme=dark] .workspace-worktree,:root[data-theme=dark] .message-meta time,:root[data-theme=dark] .message-meta span:not(.status-badge),:root[data-theme=dark] .message-reason,:root[data-theme=dark] .message-path{color:#8b949e}:root[data-theme=dark] .sidebar-section,:root[data-theme=dark] .session-console,:root[data-theme=dark] .message-panel,:root[data-theme=dark] .event-log,:root[data-theme=dark] .empty-workspace,:root[data-theme=dark] .translation-settings-modal,:root[data-theme=dark] .message-modal,:root[data-theme=dark] .event-modal{border-color:#30363d;background:#11161d}:root[data-theme=dark] .sidebar-section-toggle{color:#e6edf3}:root[data-theme=dark] .sidebar-section-toggle:hover{background:#161b22}:root[data-theme=dark] .sidebar-section-toggle[aria-expanded=true],:root[data-theme=dark] .translation-prompt-settings{border-color:#30363d}:root[data-theme=dark] .project-summary dt,:root[data-theme=dark] .permission-mode-field span,:root[data-theme=dark] .translation-settings-grid span,:root[data-theme=dark] .translation-prompt-stack span,:root[data-theme=dark] .message-mode-toggle,:root[data-theme=dark] .workflow-summary p{color:#b7c0ca}:root[data-theme=dark] .permission-mode-field small,:root[data-theme=dark] .task-create-preview small{color:#8b949e}:root[data-theme=dark] .warnings,:root[data-theme=dark] .error-banner{border-color:#da7b72;background:#2d1518;color:#ffdcd7}:root[data-theme=dark] .round-notice{border-color:#56d4dd;background:#10262b;color:#d6fbff;box-shadow:0 14px 32px #0104097a}:root[data-theme=dark] .harness-file-list li,:root[data-theme=dark] .harness-changes,:root[data-theme=dark] .harness-result,:root[data-theme=dark] .task-create-preview,:root[data-theme=dark] .workflow-steps li,:root[data-theme=dark] .message-item{border-color:#30363d;background:#0d1117}:root[data-theme=dark] .task-create-option{color:#e6edf3}:root[data-theme=dark] .task-create-preview span{color:#7ee787}:root[data-theme=dark] .workflow-steps span{color:#d6deeb}:root[data-theme=dark] .message-sequence{color:#f0f6fc}:root[data-theme=dark] .eyebrow{color:#d29922}:root[data-theme=dark] .task-nav-item.is-active,:root[data-theme=dark] .role-tab.is-active,:root[data-theme=dark] .workflow-steps li.is-current,:root[data-theme=dark] .sidebar-settings .settings-toggle.is-active,:root[data-theme=dark] .translation-toggle.is-active{border-color:#56d4dd;background:#10262b;color:#d6fbff}:root[data-theme=dark] .translation-toggle{border-color:#3d4652;background:#161b22;color:#b7c0ca}:root[data-theme=dark] .permission-mode-field select,:root[data-theme=dark] .translation-settings-grid select,:root[data-theme=dark] .translation-prompt-settings select{border-color:#3d4652;background:#0d1117;color:#e6edf3}:root[data-theme=dark] .terminal-empty{border-color:#30363d}:root[data-theme=dark] .modal-backdrop{background:#010409b8}:root[data-theme=dark] .message-modal .message-panel,:root[data-theme=dark] .event-modal .event-log{background:transparent}:root[data-theme=dark] .status-badge{border-color:#3d4652;background:#161b22;color:#d6deeb}:root[data-theme=dark] .translation-test-result.is-ok,:root[data-theme=dark] .status-running,:root[data-theme=dark] .status-ok{border-color:#3fb950;background:#12261a;color:#aff5b4}:root[data-theme=dark] .translation-test-result.is-error,:root[data-theme=dark] .status-blocked,:root[data-theme=dark] .status-crashed,:root[data-theme=dark] .status-missing,:root[data-theme=dark] .status-empty,:root[data-theme=dark] .status-rejected,:root[data-theme=dark] .status-failed,:root[data-theme=dark] .status-cancelled{border-color:#f85149;background:#2d1518;color:#ffdcd7}:root[data-theme=dark] .status-waiting,:root[data-theme=dark] .status-starting,:root[data-theme=dark] .status-incomplete,:root[data-theme=dark] .status-pending_approval,:root[data-theme=dark] .status-queued,:root[data-theme=dark] .status-translating,:root[data-theme=dark] .status-ready,:root[data-theme=dark] .status-create,:root[data-theme=dark] .status-insert,:root[data-theme=dark] .status-update{border-color:#d29922;background:#2d2208;color:#f8e3a1}:root[data-theme=dark] .status-exited,:root[data-theme=dark] .status-done,:root[data-theme=dark] .status-resumable,:root[data-theme=dark] .status-staged,:root[data-theme=dark] .status-delivered,:root[data-theme=dark] .status-acknowledged,:root[data-theme=dark] .status-translated,:root[data-theme=dark] .status-preserved{border-color:#388bfd;background:#10223a;color:#c9e2ff}:root[data-theme=dark] .status-pending{border-color:#3d4652;background:#161b22;color:#d6deeb}
|