vibe-coding-master 0.3.28 → 0.3.30

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.
Files changed (33) hide show
  1. package/README.md +14 -18
  2. package/dist/backend/api/project-routes.js +1 -1
  3. package/dist/backend/api/task-routes.js +2 -2
  4. package/dist/backend/api/{codex-translation-routes.js → translation-worker-routes.js} +21 -21
  5. package/dist/backend/cli/install-vcm-harness.js +30 -25
  6. package/dist/backend/gateway/gateway-service.js +24 -5
  7. package/dist/backend/server.js +12 -22
  8. package/dist/backend/services/claude-hook-service.js +93 -26
  9. package/dist/backend/services/harness-service.js +49 -29
  10. package/dist/backend/services/message-service.js +9 -6
  11. package/dist/backend/services/project-service.js +2 -2
  12. package/dist/backend/services/session-service.js +99 -172
  13. package/dist/backend/services/translation-service.js +17 -17
  14. package/dist/backend/services/{codex-translation-service.js → translation-worker-service.js} +11 -11
  15. package/dist/backend/templates/harness/gate-review.js +2 -58
  16. package/dist/shared/constants.js +19 -12
  17. package/dist/shared/types/app-settings.js +1 -1
  18. package/dist/shared/types/session.js +2 -14
  19. package/dist-frontend/assets/{index-Cfum1Prr.css → index-BvUHLq4X.css} +1 -1
  20. package/dist-frontend/assets/{index-x9I-bGUt.js → index-D0ezB4na.js} +36 -36
  21. package/dist-frontend/index.html +2 -2
  22. package/docs/{codex-translation-plan.md → claude-code-translation-plan.md} +151 -153
  23. package/docs/full-harness-baseline.md +3 -7
  24. package/docs/gate-review-gates.md +13 -5
  25. package/docs/gateway-design.md +1 -1
  26. package/docs/product-design.md +12 -11
  27. package/docs/v0.4-custom-workflow-plan.md +0 -1
  28. package/docs/vcm-cc-best-practices.md +2 -5
  29. package/package.json +1 -1
  30. package/dist/backend/api/codex-hook-routes.js +0 -9
  31. package/dist/backend/codex-sandbox-mode.js +0 -28
  32. package/dist/backend/services/codex-hook-service.js +0 -71
  33. package/dist/shared/types/codex-hook.js +0 -1
package/README.md CHANGED
@@ -11,7 +11,7 @@ VCM is designed for long-running coding work where one Claude Code conversation
11
11
 
12
12
  Each role runs as a real Claude Code process inside an embedded terminal. The GUI lets the user start, stop, resume, restart, switch, observe, and manually intervene in those sessions without juggling separate terminal windows.
13
13
 
14
- When Gate Review Gates are enabled for a task, or when a Gate Reviewer session already exists, the workspace can also show a fifth `Gate Reviewer` terminal role. It runs Claude Code as a project-scoped long-lived review session, receives short gate prompts that include the current task and worktree path, writes reports under the task worktree, and stays outside normal PM role routing.
14
+ When Gate Review Gates are enabled for a task, or when a Gate Reviewer session already exists, the workspace can also show a fifth `Gate Reviewer` terminal role. It is an optional VCM flow role powered by Claude Code. Its session is project-scoped and reusable across tasks, but each gate turn is bound to the current task/worktree, writes reports under the task worktree, and stays outside normal PM route-file dispatch.
15
15
 
16
16
  ## Current Capabilities
17
17
 
@@ -21,7 +21,7 @@ When Gate Review Gates are enabled for a task, or when a Gate Reviewer session a
21
21
  - Connected repository status for the base repo, including branch, upstream status, commit hash, dirty state, and fast-forward-only pull.
22
22
  - Embedded Claude Code terminals powered by `node-pty` and `xterm.js`.
23
23
  - One Claude Code session per role, with role tabs in the task header.
24
- - Optional Gate Reviewer terminal role when any Gate Review Gate is enabled.
24
+ - Optional Gate Reviewer VCM flow role when any Gate Review Gate is enabled.
25
25
  - Role session recovery through persisted Claude session ids and `claude --resume`.
26
26
  - Permission mode selection before start, resume, or restart:
27
27
  - `default`
@@ -31,7 +31,7 @@ When Gate Review Gates are enabled for a task, or when a Gate Reviewer session a
31
31
  - Two-stage VCM harness setup: deterministic fixed install plus AI-assisted bootstrap.
32
32
  - VCM-managed root rules, role agents, repo-local VCM skills, Claude Code hooks, generated-context tools, and PR template.
33
33
  - Rust generated context for module indexing and crate-external public surface indexing.
34
- - Translation panel powered by the long-lived Codex Translator session.
34
+ - Translation panel powered by the long-lived Translator session.
35
35
  - Mobile Gateway through Tencent iLink Bot API / Weixin DM, for talking to PM and managing tasks from Weixin.
36
36
  - Durable task state, role session state, handoff artifacts, and message history.
37
37
 
@@ -139,10 +139,9 @@ Important container notes:
139
139
 
140
140
  - Install Claude Code inside the container, or make `claude` available in the container `PATH`.
141
141
  - Make sure Claude Code authentication works inside the container.
142
- - Make sure the container has network access to Claude services and any configured Codex model endpoints if translation is enabled.
142
+ - Make sure the container has network access to Claude services if translation is enabled.
143
143
  - VCM accepts normal Git repositories by checking `.git` directly. It also supports `.git` files that point to worktree gitdirs.
144
144
  - VCM uses per-command `git -c safe.directory=...` for Git metadata reads and does not require global `git config --global --add safe.directory`.
145
- - Set `VCM_SANDBOX=devcontainer` so VCM-managed Codex Translator sessions rely on the container boundary and do not start Codex's nested Linux sandbox.
146
145
  - Treat the container as the sandbox boundary, especially when using relaxed Claude Code permission modes.
147
146
 
148
147
  ## Basic Usage
@@ -210,7 +209,7 @@ VCM will not offer a separate `Create task worktree` button after a task exists,
210
209
 
211
210
  Because worktrees live under `.claude/worktrees/`, the connected repository must ignore both `.ai/vcm/` and `.claude/worktrees/`. Apply the VCM Harness before creating tasks so `.gitignore` contains the managed ignore block. The base repository must also be clean because the task branch/worktree is created from the connected repo's current `HEAD`.
212
211
 
213
- When a task is complete, VCM provides a red `Close Task` action. Closing a task shows a destructive confirmation, stops VCM-managed running role sessions for that task, then deletes the task worktree, deletes the task branch by default, removes the app-local task record, and removes task runtime metadata. VCM does not preflight running sessions or uncommitted changes before closing. Project-scoped Gate Reviewer and Codex Translator sessions are not task-owned and are not stopped by Close Task.
212
+ When a task is complete, VCM provides a red `Close Task` action. Closing a task shows a destructive confirmation, stops VCM-managed running role sessions for that task, then deletes the task worktree, deletes the task branch by default, removes the app-local task record, and removes task runtime metadata. VCM does not preflight running sessions or uncommitted changes before closing. Project-scoped Gate Reviewer and Translator sessions are not task-owned and are not stopped by Close Task.
214
213
 
215
214
  ## Sidebar UI
216
215
 
@@ -219,7 +218,7 @@ The left sidebar is intentionally compact and collapsible:
219
218
  - `Repository Path`: path input on one row; `Recent` and `Connect` on the next row.
220
219
  - `Connected Repository`: connected base repo path, branch, upstream/ahead-behind status, commit hash, working tree state, and a `Pull` button.
221
220
  - `Settings`: `Theme`, `Flow pause alert`, `Try alert`, `Messages`, and `Events`.
222
- - `Translation`: global conversation translation, auto-send, target language, output scope, file translation, bootstrap, memory update, session status, and Codex Translator session access.
221
+ - `Translation`: global conversation translation, auto-send, target language, output scope, file translation, bootstrap, memory update, session status, and Translator session access.
223
222
  - `Gate Review Gates`: global gate switches for architecture plan, validation adequacy, and final diff.
224
223
  - `Gateway`: Weixin iLink binding, Gateway on/off, Gateway translation, and QR login.
225
224
  - `VCM Harness`: fixed-install status, bootstrap completion checks, and the bootstrap terminal when one is running.
@@ -350,14 +349,14 @@ Typical mobile flow:
350
349
  - `/pull-current`: runs the same fast-forward-only connected repository pull as the desktop `Pull` button.
351
350
  - `/tasks`: lists tasks for the selected project.
352
351
  - `/use-task <index-or-task-slug>`: selects the Gateway's current task context.
353
- - `/create-task <task-slug> [title]`: creates a worktree-backed task and starts the four role sessions using the saved launch template.
352
+ - `/create-task <task-slug> [title]`: creates a worktree-backed task and starts the four core role sessions using the saved launch template. If any Gate Review Gate is enabled, Gateway also resumes or starts Gate Reviewer from the saved template.
354
353
  - `/close-task`: starts a destructive close confirmation for the current task.
355
354
  - `/close-task confirm <task-slug>`: closes the task through VCM cleanup after exact slug confirmation.
356
355
  - `/translate on` and `/translate off`: changes Gateway translation for mobile messages.
357
356
 
358
357
  `/pull-current` only pulls the connected base repository. It does not pull task worktrees, stash local changes, merge divergent branches, or run arbitrary shell commands.
359
358
 
360
- `/create-task` uses the saved launch template from the desktop settings. The template controls permission mode, model, effort, and auto orchestration for the four core role sessions. Gateway translation uses the global Gateway translation setting, not the launch template.
359
+ `/create-task` uses the saved launch template from the desktop settings. The template controls permission mode, model, effort, and auto orchestration for the four core role sessions plus the optional Gate Reviewer. Gateway translation uses the global Gateway translation setting, not the launch template.
361
360
 
362
361
  `/close-task` is destructive. It stops VCM-managed role sessions and removes task-owned worktree/branch state according to the same cleanup behavior as the desktop `Close Task` action.
363
362
 
@@ -387,7 +386,7 @@ Translation settings are local and stored in:
387
386
 
388
387
  The same file stores recent repository paths and global translation preferences such as enablement, auto-send, and target language.
389
388
 
390
- VCM resolves `vcmDataDir` from `VCM_DATA_DIR`. If `VCM_DATA_DIR` is unset or empty, VCM uses `~/.vcm`. In Dev Containers, set `VCM_DATA_DIR=/workspace/.ai/vcm` and `VCM_SANDBOX=devcontainer` through `containerEnv` so VCM app state survives container rebuilds and VCM-managed Codex Translator sessions do not run a nested Codex sandbox.
389
+ VCM resolves `vcmDataDir` from `VCM_DATA_DIR`. If `VCM_DATA_DIR` is unset or empty, VCM uses `~/.vcm`. In Dev Containers, set `VCM_DATA_DIR=/workspace/.ai/vcm` through `containerEnv` so VCM app state survives container rebuilds.
391
390
 
392
391
  The sidebar `Settings` section also stores the UI theme preference in this file. The default is `system`, which follows the OS/browser color-scheme preference; users can cycle between `System`, `Light`, and `Dark`.
393
392
 
@@ -397,11 +396,11 @@ When Gateway is on, `Flow pause alert` is forced off because mobile notification
397
396
 
398
397
  Translation behavior:
399
398
 
400
- - Conversation translation is routed through the Codex Translator session and result files.
399
+ - Conversation translation is routed through the Translator session and result files.
401
400
  - Global translation controls live in the sidebar Translation section: enablement, auto-send, target language, output scope, bootstrap, memory update, file translation, session status, and `Open Session`.
402
401
  - File and conversation translation share `<baseRepoRoot>/.ai/vcm/translations/`; conversation result files are temporary runtime artifacts.
403
402
  - Claude Code output translation reads semantic Claude transcript JSONL files under `~/.claude/projects`, not raw PTY output.
404
- - Claude Code prose output waits 10 seconds before dispatch so adjacent output can be translated in one Codex batch.
403
+ - Claude Code prose output waits 10 seconds before dispatch so adjacent output can be translated in one Claude Code Translator batch.
405
404
  - VCM tails those transcript files in the backend. Closing the translation panel does not stop capture; the tailer stops only when the role session is stopped/restarted or the task is closed.
406
405
  - Translation events are cached under the task runtime repo at `.ai/vcm/translation/<task>/<role>/<session-id>.jsonl` and delivered to the frontend through HTTP polling.
407
406
  - The polling cursor is the next expected seq: `after=18` acknowledges seq `1..17` and returns seq `18+`; there is no snapshot mismatch error.
@@ -437,10 +436,7 @@ CLAUDE.md
437
436
  .claude/skills/vcm-harness-bootstrap/SKILL.md
438
437
  .claude/skills/vcm-gate-review/SKILL.md
439
438
  .claude/agents/gate-reviewer.md
440
- .ai/codex-translator/AGENTS.md
441
- .ai/codex-translator/config.toml
442
- .ai/codex-translator/.codex/config.toml
443
- .ai/codex-translator/.codex/hooks.json
439
+ .claude/agents/translator.md
444
440
  .ai/tools/request-gate-review
445
441
  .ai/tools/generate-module-index
446
442
  .ai/tools/generate-public-surface
@@ -574,7 +570,7 @@ When it is on, VCM is in auto mode:
574
570
  - When the target role later reaches `Stop`, VCM scans again and may deliver the next pending route file.
575
571
  - If auto orchestration gets stuck after a manual copy/paste recovery, `Mark All Done` clears pending route files. It does not mutate message history.
576
572
 
577
- VCM Harness injects Claude Code `UserPromptSubmit` and `Stop` hooks into `.claude/settings.json`. Role tabs become `running` when Claude Code accepts a prompt and `idle` after `Stop`; VCM also marks a role `running` immediately after it writes a message to that embedded terminal. Gate Reviewer is a Claude Code role, but VCM triggers and settles gate turns directly around the gate prompt and report callback. The terminal process status is still tracked separately. Claude role rules require a role to end its turn after writing or updating a message route file, rather than polling, looping, or waiting for another role inside the same Claude Code turn.
573
+ VCM Harness injects Claude Code `UserPromptSubmit` and `Stop` hooks into `.claude/settings.json`. Role tabs become `running` when Claude Code accepts a prompt and `idle` after `Stop`; VCM also marks a role `running` immediately after it writes a message to that embedded terminal. Gate Reviewer uses the same Claude hook/Round/translation path as other VCM flow roles, while VCM still owns gate report polling and the PM callback. The terminal process status is tracked separately. Claude role rules require a role to end its turn after writing or updating a message route file, rather than polling, looping, or waiting for another role inside the same Claude Code turn.
578
574
 
579
575
  The implementation keeps only the active manual/auto orchestration mode. It does not expose pause/resume, stage/approve/reject, or a separate agent-facing message CLI.
580
576
 
@@ -683,5 +679,5 @@ See also:
683
679
  - `docs/vcm-cc-best-practices.md`
684
680
  - `docs/full-harness-baseline.md`
685
681
  - `docs/gate-review-gates.md`
686
- - `docs/codex-translation-plan.md`
682
+ - `docs/claude-code-translation-plan.md`
687
683
  - `docs/cc-best-practices.md` is archived and no longer maintained.
@@ -5,7 +5,7 @@ export function registerProjectRoutes(app, deps) {
5
5
  });
6
6
  app.post("/api/projects/connect", async (request) => {
7
7
  const project = await deps.projectService.connectProject(request.body);
8
- await deps.codexTranslationService?.cleanupStartupRuntime(project.repoRoot);
8
+ await deps.translationWorkerService?.cleanupStartupRuntime(project.repoRoot);
9
9
  return project;
10
10
  });
11
11
  app.get("/api/projects/current", async () => {
@@ -1,4 +1,4 @@
1
- import { DISPATCHABLE_ROLES, isVcmRoleName } from "../../shared/constants.js";
1
+ import { CORE_VCM_ROLE_NAMES, DISPATCHABLE_ROLES } from "../../shared/constants.js";
2
2
  import { isOpenFileLimitError, VcmError } from "../errors.js";
3
3
  import { getTaskRuntimeRepoRoot } from "../services/task-service.js";
4
4
  export function registerTaskRoutes(app, deps) {
@@ -40,7 +40,7 @@ export function registerTaskRoutes(app, deps) {
40
40
  async function stopRunningRoleSessions(deps, repoRoot, taskSlug) {
41
41
  const sessions = await deps.sessionService.listRoleSessions(repoRoot, taskSlug);
42
42
  for (const session of sessions) {
43
- if (session.status === "running" && isVcmRoleName(session.role)) {
43
+ if (session.status === "running" && CORE_VCM_ROLE_NAMES.some((role) => role === session.role)) {
44
44
  await deps.sessionService.stopRoleSession(repoRoot, taskSlug, session.role);
45
45
  }
46
46
  }
@@ -1,60 +1,60 @@
1
1
  import { VcmError } from "../errors.js";
2
- export function registerCodexTranslationRoutes(app, deps) {
3
- app.get("/api/translation/codex/state", async () => {
2
+ export function registerTranslationWorkerRoutes(app, deps) {
3
+ app.get("/api/translation/state", async () => {
4
4
  const project = await requireCurrentProject(deps.projectService);
5
- return deps.codexTranslationService.getState(project.repoRoot, {
5
+ return deps.translationWorkerService.getState(project.repoRoot, {
6
6
  visibility: "public"
7
7
  });
8
8
  });
9
- app.get("/api/translation/codex/session", async () => {
9
+ app.get("/api/translation/session", async () => {
10
10
  const project = await requireCurrentProject(deps.projectService);
11
11
  return (await deps.sessionService.getProjectTranslatorSession(project.repoRoot)) ?? null;
12
12
  });
13
- app.post("/api/translation/codex/session/ensure", async (request) => {
13
+ app.post("/api/translation/session/ensure", async (request) => {
14
14
  const project = await requireCurrentProject(deps.projectService);
15
15
  return deps.sessionService.ensureProjectTranslatorSession(project.repoRoot, request.body);
16
16
  });
17
- app.post("/api/translation/codex/session/start", async (request) => {
17
+ app.post("/api/translation/session/start", async (request) => {
18
18
  const project = await requireCurrentProject(deps.projectService);
19
19
  return deps.sessionService.startProjectTranslatorSession(project.repoRoot, request.body);
20
20
  });
21
- app.post("/api/translation/codex/session/resume", async (request) => {
21
+ app.post("/api/translation/session/resume", async (request) => {
22
22
  const project = await requireCurrentProject(deps.projectService);
23
23
  return deps.sessionService.resumeProjectTranslatorSession(project.repoRoot, request.body);
24
24
  });
25
- app.post("/api/translation/codex/session/restart", async (request) => {
25
+ app.post("/api/translation/session/restart", async (request) => {
26
26
  const project = await requireCurrentProject(deps.projectService);
27
27
  return deps.sessionService.restartProjectTranslatorSession(project.repoRoot, request.body);
28
28
  });
29
- app.post("/api/translation/codex/session/stop", async () => {
29
+ app.post("/api/translation/session/stop", async () => {
30
30
  const project = await requireCurrentProject(deps.projectService);
31
31
  return deps.sessionService.stopProjectTranslatorSession(project.repoRoot);
32
32
  });
33
- app.get("/api/translation/codex/source-files", async (request) => {
33
+ app.get("/api/translation/source-files", async (request) => {
34
34
  const project = await requireCurrentProject(deps.projectService);
35
- return deps.codexTranslationService.browseSourceFiles(project.repoRoot, {
35
+ return deps.translationWorkerService.browseSourceFiles(project.repoRoot, {
36
36
  path: request.query.path,
37
37
  query: request.query.query,
38
38
  limit: request.query.limit === undefined ? undefined : Number(request.query.limit)
39
39
  });
40
40
  });
41
- app.post("/api/translation/codex/files", async (request) => {
41
+ app.post("/api/translation/files", async (request) => {
42
42
  const project = await requireCurrentProject(deps.projectService);
43
- return deps.codexTranslationService.createFileJob(project.repoRoot, request.body);
43
+ return deps.translationWorkerService.createFileJob(project.repoRoot, request.body);
44
44
  });
45
- app.post("/api/translation/codex/bootstrap", async (request) => {
45
+ app.post("/api/translation/bootstrap", async (request) => {
46
46
  const project = await requireCurrentProject(deps.projectService);
47
- return deps.codexTranslationService.createBootstrapRun(project.repoRoot, request.body);
47
+ return deps.translationWorkerService.createBootstrapRun(project.repoRoot, request.body);
48
48
  });
49
- app.post("/api/translation/codex/memory-update", async (request) => {
49
+ app.post("/api/translation/memory-update", async (request) => {
50
50
  const project = await requireCurrentProject(deps.projectService);
51
- return deps.codexTranslationService.createMemoryUpdate(project.repoRoot, request.body);
51
+ return deps.translationWorkerService.createMemoryUpdate(project.repoRoot, request.body);
52
52
  });
53
- app.get("/api/translation/codex/files/:jobId", async (request) => {
53
+ app.get("/api/translation/files/:jobId", async (request) => {
54
54
  const project = await requireCurrentProject(deps.projectService);
55
- return deps.codexTranslationService.readFileJobOutput(project.repoRoot, request.params.jobId);
55
+ return deps.translationWorkerService.readFileJobOutput(project.repoRoot, request.params.jobId);
56
56
  });
57
- app.post("/api/translation/codex/files/:jobId/promote", async (request) => {
57
+ app.post("/api/translation/files/:jobId/promote", async (request) => {
58
58
  const project = await requireCurrentProject(deps.projectService);
59
59
  const targetPath = request.body?.targetPath?.trim();
60
60
  if (!targetPath) {
@@ -64,7 +64,7 @@ export function registerCodexTranslationRoutes(app, deps) {
64
64
  statusCode: 400
65
65
  });
66
66
  }
67
- return deps.codexTranslationService.promoteFileJob(project.repoRoot, request.params.jobId, targetPath);
67
+ return deps.translationWorkerService.promoteFileJob(project.repoRoot, request.params.jobId, targetPath);
68
68
  });
69
69
  }
70
70
  async function requireCurrentProject(projectService) {
@@ -6,7 +6,7 @@ import process from "node:process";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { renderArchitectHarnessRules } from "../templates/harness/architect-agent.js";
8
8
  import { renderCoderHarnessRules } from "../templates/harness/coder-agent.js";
9
- import { renderCodexCliConfigHarnessRules, renderCodexHooksHarnessRules, renderCodexTranslatorAgentsHarnessRules, renderCodexTranslatorConfigHarnessRules, renderGateReviewerAgentRules, renderRequestGateReviewTool, renderVcmGateReviewSkillRules } from "../templates/harness/gate-review.js";
9
+ import { renderGateReviewerAgentRules, renderRequestGateReviewTool, renderTranslatorAgentRules, renderVcmGateReviewSkillRules } from "../templates/harness/gate-review.js";
10
10
  import { renderRootClaudeHarnessRules } from "../templates/harness/claude-root.js";
11
11
  import { renderGitignoreHarnessRules } from "../templates/harness/gitignore.js";
12
12
  import { renderProjectManagerHarnessRules } from "../templates/harness/project-manager-agent.js";
@@ -22,6 +22,12 @@ const APP_ROOT = path.resolve(CLI_DIR, "../../..");
22
22
  const MANIFEST_PATH = ".ai/vcm-harness-manifest.json";
23
23
  const HTML_BLOCK_PATTERN = /<!-- VCM:BEGIN(?:\s+version=\d+)? -->[\s\S]*?<!-- VCM:END -->/m;
24
24
  const HASH_BLOCK_PATTERN = /# VCM:BEGIN(?:\s+version=\d+)?\n[\s\S]*?# VCM:END/m;
25
+ const LEGACY_CODEX_HARNESS_PATHS = [
26
+ ".ai/codex",
27
+ ".ai/codex-translator",
28
+ ".claude/skills/vcm-codex-review-gate",
29
+ ".ai/tools/request-codex-review"
30
+ ];
25
31
  const VCM_HOOK_COMMAND = `sh -c 'if [ -z "\${VCM_TASK_SLUG:-}" ] || [ -z "\${VCM_ROLE:-}" ] || [ -z "\${VCM_API_URL:-}" ]; then exit 0; fi; node -e '"'"'let s="";process.stdin.setEncoding("utf8");process.stdin.on("data",d=>s+=d);process.stdin.on("end",()=>{let event={};try{event=s.trim()?JSON.parse(s):{};}catch{event={raw:s};}process.stdout.write(JSON.stringify({taskSlug:process.env.VCM_TASK_SLUG,role:process.env.VCM_ROLE,event}));});'"'"' | curl -fsS --max-time 2 -X POST "\${VCM_API_URL}/api/hooks/claude-code" -H "content-type: application/json" --data-binary @- >/dev/null || true'`;
26
32
  const VCM_STOP_HOOK_COMMAND = `sh -c 'if [ -z "\${VCM_TASK_SLUG:-}" ] || [ -z "\${VCM_ROLE:-}" ] || [ -z "\${VCM_API_URL:-}" ]; then exit 0; fi; node -e '"'"'let s="";process.stdin.setEncoding("utf8");process.stdin.on("data",d=>s+=d);process.stdin.on("end",()=>{let event={};try{event=s.trim()?JSON.parse(s):{};}catch{event={raw:s};}process.stdout.write(JSON.stringify({taskSlug:process.env.VCM_TASK_SLUG,role:process.env.VCM_ROLE,event}));});'"'"' | curl -fsS --max-time 5 -X POST "\${VCM_API_URL}/api/hooks/claude-code/stop" -H "content-type: application/json" --data-binary @- || true'`;
27
33
  const VCM_PERMISSION_REQUEST_HOOK_COMMAND = `sh -c 'if [ -z "\${VCM_TASK_SLUG:-}" ] || [ -z "\${VCM_ROLE:-}" ] || [ -z "\${VCM_API_URL:-}" ]; then exit 0; fi; node -e '"'"'let s="";process.stdin.setEncoding("utf8");process.stdin.on("data",d=>s+=d);process.stdin.on("end",()=>{let event={};try{event=s.trim()?JSON.parse(s):{};}catch{event={raw:s};}process.stdout.write(JSON.stringify({taskSlug:process.env.VCM_TASK_SLUG,role:process.env.VCM_ROLE,event}));});'"'"' | curl -fsS --max-time 5 -X POST "\${VCM_API_URL}/api/hooks/claude-code/permission-request" -H "content-type: application/json" --data-binary @- || true'`;
@@ -50,6 +56,9 @@ const AGENT_FRONTMATTER = {
50
56
  },
51
57
  "gate-reviewer": {
52
58
  description: "VCM independent gate review role for architecture plans, validation adequacy, and final diffs."
59
+ },
60
+ translator: {
61
+ description: "VCM project translation tool role for conversation translation, file translation, bootstrap, and memory updates."
53
62
  }
54
63
  };
55
64
  const MANAGED_FILES = [
@@ -117,11 +126,12 @@ const MANAGED_FILES = [
117
126
  content: renderGateReviewerAgentRules()
118
127
  },
119
128
  {
120
- path: ".ai/codex-translator/AGENTS.md",
121
- title: "VCM Codex Translator",
129
+ path: ".claude/agents/translator.md",
130
+ title: "Translator Agent",
131
+ agentName: "translator",
122
132
  commentStyle: "html",
123
- category: "codex-translator-agent",
124
- content: renderCodexTranslatorAgentsHarnessRules()
133
+ category: "agent-translator",
134
+ content: renderTranslatorAgentRules()
125
135
  }
126
136
  ];
127
137
  const DURABLE_DOC_TEMPLATES = [
@@ -181,24 +191,6 @@ const WHOLE_FILES = [
181
191
  mode: 0o644,
182
192
  content: renderSkillFile("VCM Gate Review Skill", "vcm-gate-review", "Use when project-manager reaches a Gate Review trigger or receives a VCM Gate Review callback.", renderVcmGateReviewSkillRules())
183
193
  },
184
- {
185
- path: ".ai/codex-translator/config.toml",
186
- category: "codex-translator-config",
187
- mode: 0o644,
188
- content: renderCodexTranslatorConfigHarnessRules()
189
- },
190
- {
191
- path: ".ai/codex-translator/.codex/config.toml",
192
- category: "codex-translator-hooks",
193
- mode: 0o644,
194
- content: renderCodexCliConfigHarnessRules()
195
- },
196
- {
197
- path: ".ai/codex-translator/.codex/hooks.json",
198
- category: "codex-translator-hooks",
199
- mode: 0o644,
200
- content: renderCodexHooksHarnessRules("codex-translator")
201
- },
202
194
  {
203
195
  path: ".ai/tools/request-gate-review",
204
196
  category: "runtime-tool",
@@ -271,6 +263,7 @@ async function main() {
271
263
  await installWholeFile({ projectRoot, file, dryRun, operations });
272
264
  }
273
265
  await removeLegacyFlatSkillFiles({ projectRoot, dryRun, operations });
266
+ await removeLegacyCodexHarnessPaths({ projectRoot, dryRun, operations });
274
267
  printReport({ projectRoot, dryRun, operations });
275
268
  }
276
269
  function parseArgs(argv) {
@@ -417,8 +410,6 @@ function fixedDirectories() {
417
410
  ".claude/skills/vcm-long-running-validation/",
418
411
  ".claude/skills/vcm-route-message/",
419
412
  ".claude/skills/vcm-gate-review/",
420
- ".ai/codex-translator/",
421
- ".ai/codex-translator/.codex/",
422
413
  ".ai/vcm/translations/",
423
414
  ".ai/vcm/gate-reviews/",
424
415
  ".ai/tools/",
@@ -632,6 +623,20 @@ async function removeLegacyFlatSkillFiles({ projectRoot, dryRun, operations }) {
632
623
  operations.push(done(legacy.path, "deleted legacy flat skill file"));
633
624
  }
634
625
  }
626
+ async function removeLegacyCodexHarnessPaths({ projectRoot, dryRun, operations }) {
627
+ for (const relativePath of LEGACY_CODEX_HARNESS_PATHS) {
628
+ const targetPath = resolveInside(projectRoot, relativePath);
629
+ if (!await pathExists(targetPath)) {
630
+ continue;
631
+ }
632
+ if (dryRun) {
633
+ operations.push(plan(relativePath, "delete legacy Codex harness path"));
634
+ continue;
635
+ }
636
+ await fs.rm(targetPath, { recursive: true, force: true });
637
+ operations.push(done(relativePath, "deleted legacy Codex harness path"));
638
+ }
639
+ }
635
640
  function stripSkillFrontmatter(content) {
636
641
  return content.replace(/^---\n[\s\S]*?\n---\n\n/, "");
637
642
  }
@@ -1,5 +1,5 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { VCM_ROLE_DEFINITIONS } from "../../shared/constants.js";
2
+ import { CORE_VCM_ROLE_DEFINITIONS, CORE_VCM_ROLE_NAMES, GATE_REVIEWER_ROLE_DEFINITION } from "../../shared/constants.js";
3
3
  import { VcmError } from "../errors.js";
4
4
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
5
5
  import { getTaskRuntimeRepoRoot } from "../services/task-service.js";
@@ -407,17 +407,36 @@ export function createGatewayService(deps) {
407
407
  taskSlug: task.taskSlug,
408
408
  mode: template.autoOrchestration ? "auto" : "manual"
409
409
  });
410
+ const gateReviewSettings = await deps.appSettings.getGateReviewSettings(project.repoRoot, task.taskSlug);
411
+ const roleDefinitions = [
412
+ ...CORE_VCM_ROLE_DEFINITIONS,
413
+ ...(gateReviewSettings.enabled ? [GATE_REVIEWER_ROLE_DEFINITION] : [])
414
+ ];
410
415
  const startedRoles = [];
411
- for (const definition of VCM_ROLE_DEFINITIONS) {
416
+ for (const definition of roleDefinitions) {
412
417
  const roleTemplate = template.roles[definition.name];
413
418
  try {
414
- await deps.sessionService.startRoleSession(project.repoRoot, task.taskSlug, definition.name, {
419
+ const sessionInput = {
415
420
  cols: 100,
416
421
  rows: 28,
417
422
  permissionMode: roleTemplate.permissionMode,
418
423
  model: roleTemplate.model,
419
424
  effort: roleTemplate.effort
420
- });
425
+ };
426
+ const existing = await deps.sessionService.getRoleSession(project.repoRoot, task.taskSlug, definition.name);
427
+ if (existing?.status === "running") {
428
+ if (definition.name === "gate-reviewer") {
429
+ await deps.sessionService.resumeRoleSession(project.repoRoot, task.taskSlug, definition.name, sessionInput);
430
+ }
431
+ startedRoles.push(definition.name);
432
+ continue;
433
+ }
434
+ if (existing?.claudeSessionId) {
435
+ await deps.sessionService.resumeRoleSession(project.repoRoot, task.taskSlug, definition.name, sessionInput);
436
+ }
437
+ else {
438
+ await deps.sessionService.startRoleSession(project.repoRoot, task.taskSlug, definition.name, sessionInput);
439
+ }
421
440
  startedRoles.push(definition.name);
422
441
  }
423
442
  catch (error) {
@@ -536,7 +555,7 @@ export function createGatewayService(deps) {
536
555
  async function stopRunningRoleSessions(repoRoot, taskSlug) {
537
556
  const sessions = await deps.sessionService.listRoleSessions(repoRoot, taskSlug);
538
557
  for (const session of sessions) {
539
- if (session.status === "running") {
558
+ if (session.status === "running" && CORE_VCM_ROLE_NAMES.some((role) => role === session.role)) {
540
559
  await deps.sessionService.stopRoleSession(repoRoot, taskSlug, session.role);
541
560
  }
542
561
  }
@@ -7,12 +7,11 @@ import { createClaudeAdapter } from "./adapters/claude-adapter.js";
7
7
  import { createCommandRunner } from "./adapters/command-runner.js";
8
8
  import { createCommandDispatcher } from "./services/command-dispatcher.js";
9
9
  import { createClaudeHookService } from "./services/claude-hook-service.js";
10
- import { createCodexHookService } from "./services/codex-hook-service.js";
11
10
  import { createGitAdapter } from "./adapters/git-adapter.js";
12
11
  import { createAppSettingsService } from "./services/app-settings-service.js";
13
12
  import { createClaudeTranscriptService } from "./services/claude-transcript-service.js";
14
13
  import { createGateReviewService } from "./services/gate-review-service.js";
15
- import { createCodexTranslationService } from "./services/codex-translation-service.js";
14
+ import { createTranslationWorkerService } from "./services/translation-worker-service.js";
16
15
  import { createHarnessService, createScriptFixedHarnessInstaller } from "./services/harness-service.js";
17
16
  import { createNodeFileSystemAdapter } from "./adapters/filesystem.js";
18
17
  import { createNodePtyTerminalRuntime } from "./runtime/node-pty-runtime.js";
@@ -22,7 +21,6 @@ import { createWeixinIlinkChannel } from "./gateway/channels/weixin-ilink-channe
22
21
  import { createGatewayAuditLog } from "./gateway/gateway-audit-log.js";
23
22
  import { createGatewayService } from "./gateway/gateway-service.js";
24
23
  import { createGatewaySettingsService } from "./gateway/gateway-settings-service.js";
25
- import { resolveCodexSandboxMode } from "./codex-sandbox-mode.js";
26
24
  import { createJobGuardService } from "./services/job-guard-service.js";
27
25
  import { createProjectService } from "./services/project-service.js";
28
26
  import { createSessionRegistry } from "./runtime/session-registry.js";
@@ -36,9 +34,8 @@ import { createDiagnosticsService } from "./services/diagnostics-service.js";
36
34
  import { registerAppSettingsRoutes } from "./api/app-settings-routes.js";
37
35
  import { registerArtifactRoutes } from "./api/artifact-routes.js";
38
36
  import { registerClaudeHookRoutes } from "./api/claude-hook-routes.js";
39
- import { registerCodexHookRoutes } from "./api/codex-hook-routes.js";
40
37
  import { registerGateReviewRoutes } from "./api/gate-review-routes.js";
41
- import { registerCodexTranslationRoutes } from "./api/codex-translation-routes.js";
38
+ import { registerTranslationWorkerRoutes } from "./api/translation-worker-routes.js";
42
39
  import { registerHarnessRoutes } from "./api/harness-routes.js";
43
40
  import { registerMessageRoutes } from "./api/message-routes.js";
44
41
  import { registerProjectRoutes } from "./api/project-routes.js";
@@ -66,19 +63,18 @@ export async function createServer(deps, options = {}) {
66
63
  registerDiagnosticsRoutes(app, { diagnosticsService: deps.diagnosticsService });
67
64
  registerAppSettingsRoutes(app, { appSettings: deps.appSettings });
68
65
  registerClaudeHookRoutes(app, { claudeHookService: deps.claudeHookService });
69
- registerCodexHookRoutes(app, { codexHookService: deps.codexHookService });
70
66
  registerGateReviewRoutes(app, {
71
67
  projectService: deps.projectService,
72
68
  gateReviewService: deps.gateReviewService
73
69
  });
74
- registerCodexTranslationRoutes(app, {
70
+ registerTranslationWorkerRoutes(app, {
75
71
  projectService: deps.projectService,
76
- codexTranslationService: deps.codexTranslationService,
72
+ translationWorkerService: deps.translationWorkerService,
77
73
  sessionService: deps.sessionService
78
74
  });
79
75
  registerProjectRoutes(app, {
80
76
  projectService: deps.projectService,
81
- codexTranslationService: deps.codexTranslationService
77
+ translationWorkerService: deps.translationWorkerService
82
78
  });
83
79
  registerHarnessRoutes(app, {
84
80
  projectService: deps.projectService,
@@ -142,7 +138,7 @@ export async function createServer(deps, options = {}) {
142
138
  }
143
139
  async function cleanupRecentTranslationRuntime(deps) {
144
140
  const repoRoots = await deps.projectService.getRecentRepositoryPaths();
145
- await Promise.all(repoRoots.map((repoRoot) => deps.codexTranslationService.cleanupStartupRuntime(repoRoot)));
141
+ await Promise.all(repoRoots.map((repoRoot) => deps.translationWorkerService.cleanupStartupRuntime(repoRoot)));
146
142
  }
147
143
  export async function startServer(options = {}) {
148
144
  const host = options.host ?? "127.0.0.1";
@@ -186,8 +182,7 @@ export function createDefaultServerDeps(options = {}) {
186
182
  artifactService,
187
183
  projectService,
188
184
  taskService,
189
- apiUrl: options.apiUrl,
190
- sandboxMode: resolveCodexSandboxMode(process.env)
185
+ apiUrl: options.apiUrl
191
186
  });
192
187
  const commandDispatcher = createCommandDispatcher({
193
188
  runtime,
@@ -223,7 +218,7 @@ export function createDefaultServerDeps(options = {}) {
223
218
  sessionService,
224
219
  roundService
225
220
  });
226
- const codexTranslationService = createCodexTranslationService({
221
+ const translationWorkerService = createTranslationWorkerService({
227
222
  fs,
228
223
  runtime,
229
224
  sessionService
@@ -234,7 +229,7 @@ export function createDefaultServerDeps(options = {}) {
234
229
  sessionRegistry: registry,
235
230
  transcripts,
236
231
  sessionService,
237
- codexTranslationService,
232
+ translationWorkerService,
238
233
  fs,
239
234
  projectService,
240
235
  appSettings
@@ -268,12 +263,8 @@ export function createDefaultServerDeps(options = {}) {
268
263
  appSettings,
269
264
  runtime,
270
265
  gatewayService,
271
- jobGuard: createJobGuardService()
272
- });
273
- const codexHookService = createCodexHookService({
274
- projectService,
275
- sessionService,
276
- codexTranslationService
266
+ jobGuard: createJobGuardService(),
267
+ translationWorkerService
277
268
  });
278
269
  const diagnosticsService = createDiagnosticsService({
279
270
  appRoot: getAppRoot(),
@@ -290,10 +281,9 @@ export function createDefaultServerDeps(options = {}) {
290
281
  harnessService,
291
282
  commandDispatcher,
292
283
  claudeHookService,
293
- codexHookService,
294
284
  messageService,
295
285
  gateReviewService,
296
- codexTranslationService,
286
+ translationWorkerService,
297
287
  roundService,
298
288
  statusService,
299
289
  translationService,