@triflux/remote 10.0.1 → 10.25.1

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 (155) hide show
  1. package/hub/pipe.mjs +239 -121
  2. package/hub/public/dashboard.html +68 -7
  3. package/hub/server.mjs +2181 -701
  4. package/hub/store-adapter.mjs +101 -37
  5. package/hub/store.mjs +356 -153
  6. package/hub/team/agent-map.json +26 -7
  7. package/hub/team/ansi.mjs +245 -146
  8. package/hub/team/backend.mjs +84 -17
  9. package/hub/team/build-worker-prompt.mjs +42 -0
  10. package/hub/team/check-mcp-hub.mjs +49 -0
  11. package/hub/team/claude-daemon-control.mjs +219 -0
  12. package/hub/team/claude-native-bridge.mjs +784 -0
  13. package/hub/team/claude-session-projection.mjs +65 -0
  14. package/hub/team/cli/commands/attach.mjs +57 -37
  15. package/hub/team/cli/commands/control.mjs +29 -8
  16. package/hub/team/cli/commands/debug.mjs +95 -74
  17. package/hub/team/cli/commands/focus.mjs +80 -53
  18. package/hub/team/cli/commands/interrupt.mjs +18 -6
  19. package/hub/team/cli/commands/kill.mjs +16 -5
  20. package/hub/team/cli/commands/list.mjs +31 -24
  21. package/hub/team/cli/commands/send.mjs +19 -6
  22. package/hub/team/cli/commands/start/index.mjs +154 -33
  23. package/hub/team/cli/commands/start/parse-args.mjs +89 -12
  24. package/hub/team/cli/commands/start/start-headless.mjs +127 -37
  25. package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
  26. package/hub/team/cli/commands/start/start-mux.mjs +122 -73
  27. package/hub/team/cli/commands/start/start-wt.mjs +86 -69
  28. package/hub/team/cli/commands/status.mjs +43 -14
  29. package/hub/team/cli/commands/stop.mjs +11 -4
  30. package/hub/team/cli/commands/task.mjs +8 -3
  31. package/hub/team/cli/commands/tasks.mjs +13 -13
  32. package/hub/team/cli/index.mjs +2 -2
  33. package/hub/team/cli/manifest.mjs +38 -8
  34. package/hub/team/cli/render.mjs +52 -30
  35. package/hub/team/cli/services/attach-fallback.mjs +74 -54
  36. package/hub/team/cli/services/hub-client.mjs +45 -17
  37. package/hub/team/cli/services/member-selector.mjs +37 -30
  38. package/hub/team/cli/services/native-control.mjs +144 -116
  39. package/hub/team/cli/services/runtime-mode.mjs +55 -1
  40. package/hub/team/cli/services/state-store.mjs +66 -9
  41. package/hub/team/cli/services/task-model.mjs +40 -30
  42. package/hub/team/codex-review.mjs +521 -0
  43. package/hub/team/conductor-mesh-bridge.mjs +25 -24
  44. package/hub/team/conductor-registry.mjs +121 -0
  45. package/hub/team/conductor.mjs +1379 -671
  46. package/hub/team/consensus-meta.mjs +176 -0
  47. package/hub/team/dashboard-anchor.mjs +4 -5
  48. package/hub/team/dashboard-layout.mjs +3 -1
  49. package/hub/team/dashboard-open.mjs +21 -119
  50. package/hub/team/dashboard.mjs +76 -28
  51. package/hub/team/event-log.mjs +18 -10
  52. package/hub/team/execution-mode.mjs +241 -0
  53. package/hub/team/extract-completion-payload.mjs +110 -0
  54. package/hub/team/git-preflight.mjs +286 -0
  55. package/hub/team/handoff.mjs +49 -18
  56. package/hub/team/headless-bridge-session.mjs +17 -0
  57. package/hub/team/headless.mjs +2065 -1156
  58. package/hub/team/health-probe.mjs +232 -69
  59. package/hub/team/launcher-template.mjs +23 -13
  60. package/hub/team/lead-control.mjs +91 -4
  61. package/hub/team/mcp-selector.mjs +145 -0
  62. package/hub/team/native-supervisor.mjs +90 -26
  63. package/hub/team/native.mjs +74 -35
  64. package/hub/team/nativeProxy.mjs +184 -113
  65. package/hub/team/notify.mjs +147 -78
  66. package/hub/team/orchestrator.mjs +173 -161
  67. package/hub/team/pane.mjs +43 -15
  68. package/hub/team/process-cleanup.mjs +69 -25
  69. package/hub/team/psmux.mjs +1067 -264
  70. package/hub/team/recovery-store.mjs +149 -0
  71. package/hub/team/remote-probe.mjs +69 -53
  72. package/hub/team/remote-session.mjs +131 -59
  73. package/hub/team/remote-watcher.mjs +62 -34
  74. package/hub/team/retry-state-machine.mjs +267 -0
  75. package/hub/team/routing.mjs +51 -25
  76. package/hub/team/runtime-strategy.mjs +200 -0
  77. package/hub/team/schemas/worker-completion.json +39 -0
  78. package/hub/team/sentinel-capture.mjs +127 -0
  79. package/hub/team/session-sync.mjs +153 -6
  80. package/hub/team/session.mjs +605 -611
  81. package/hub/team/shared.mjs +13 -13
  82. package/hub/team/staleState.mjs +72 -30
  83. package/hub/team/swarm-cli.mjs +390 -0
  84. package/hub/team/swarm-hypervisor.mjs +1526 -198
  85. package/hub/team/swarm-intent.mjs +216 -0
  86. package/hub/team/swarm-locks.mjs +140 -25
  87. package/hub/team/swarm-planner.mjs +121 -21
  88. package/hub/team/swarm-preflight.mjs +317 -0
  89. package/hub/team/swarm-reconciler.mjs +128 -23
  90. package/hub/team/synapse-cli.mjs +458 -0
  91. package/hub/team/synapse-http.mjs +60 -0
  92. package/hub/team/synapse-registry.mjs +329 -0
  93. package/hub/team/terminal-opener.mjs +216 -0
  94. package/hub/team/tui-core.mjs +288 -0
  95. package/hub/team/tui-lite.mjs +234 -146
  96. package/hub/team/tui-remote-adapter.mjs +21 -37
  97. package/hub/team/tui-synapse.mjs +223 -0
  98. package/hub/team/tui-viewer.mjs +104 -45
  99. package/hub/team/tui-widgets.mjs +262 -0
  100. package/hub/team/tui.mjs +936 -449
  101. package/hub/team/worker-completion-validator.mjs +84 -0
  102. package/hub/team/worker-sandbox.mjs +93 -0
  103. package/hub/team/worker-signal.mjs +263 -0
  104. package/hub/team/worker-signal.types.d.ts +52 -0
  105. package/hub/team/worktree-lifecycle.mjs +518 -64
  106. package/hub/team/wt-manager.mjs +297 -26
  107. package/hub/tools.mjs +474 -252
  108. package/hub/tray.mjs +411 -368
  109. package/hub/workers/claude-worker.mjs +162 -118
  110. package/hub/workers/codex-app-server-worker.mjs +1015 -0
  111. package/hub/workers/codex-mcp.mjs +724 -507
  112. package/hub/workers/delegator-mcp.mjs +663 -337
  113. package/hub/workers/factory.mjs +100 -21
  114. package/hub/workers/gemini-worker.mjs +179 -86
  115. package/hub/workers/interface.mjs +23 -2
  116. package/hub/workers/lib/jsonrpc-stdio.mjs +463 -0
  117. package/hub/workers/worker-utils.mjs +22 -16
  118. package/package.json +4 -3
  119. package/scripts/lib/agent-json.mjs +27 -0
  120. package/scripts/lib/async.mjs +174 -0
  121. package/scripts/lib/claudemd-scanner.mjs +223 -0
  122. package/scripts/lib/cli-agy.mjs +62 -0
  123. package/scripts/lib/cli-claude.mjs +78 -0
  124. package/scripts/lib/cli-codex.mjs +199 -0
  125. package/scripts/lib/cli-gemini.mjs +67 -0
  126. package/scripts/lib/codex-recovery.sh +50 -0
  127. package/scripts/lib/context.mjs +67 -0
  128. package/scripts/lib/cross-review-utils.mjs +59 -0
  129. package/scripts/lib/doctor-env-checks.mjs +121 -0
  130. package/scripts/lib/dynamic-route-cli.mjs +107 -0
  131. package/scripts/lib/env-probe.mjs +269 -0
  132. package/scripts/lib/env.mjs +14 -0
  133. package/scripts/lib/gemini-profiles.mjs +119 -0
  134. package/scripts/lib/handoff.mjs +187 -0
  135. package/scripts/lib/hook-utils.mjs +16 -0
  136. package/scripts/lib/hub.mjs +48 -0
  137. package/scripts/lib/keyword-rules.mjs +195 -0
  138. package/scripts/lib/logger.mjs +105 -0
  139. package/scripts/lib/mcp-filter.mjs +883 -0
  140. package/scripts/lib/mcp-gateway-servers.mjs +70 -0
  141. package/scripts/lib/mcp-guard-engine.mjs +2120 -0
  142. package/scripts/lib/mcp-health.mjs +773 -0
  143. package/scripts/lib/mcp-manifest.mjs +89 -0
  144. package/scripts/lib/mcp-server-catalog.mjs +355 -0
  145. package/scripts/lib/pid.mjs +63 -0
  146. package/scripts/lib/process-utils.mjs +26 -0
  147. package/scripts/lib/psmux-info.mjs +179 -0
  148. package/scripts/lib/quota.mjs +47 -0
  149. package/scripts/lib/remote-spawn-transfer.mjs +226 -0
  150. package/scripts/lib/skill-state.mjs +220 -0
  151. package/scripts/lib/skill-template.mjs +375 -0
  152. package/scripts/lib/team.mjs +75 -0
  153. package/scripts/lib/timeout.mjs +66 -0
  154. package/scripts/lib/tmp.mjs +26 -0
  155. package/scripts/lib/toml.mjs +113 -0
@@ -1,24 +1,31 @@
1
- import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
2
- import { listSessions } from "../../session.mjs";
3
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
4
- import { loadTeamState } from "../services/state-store.mjs";
5
-
6
- export function teamList() {
7
- const state = loadTeamState();
8
- if (state && isTeamAlive(state) && (isNativeMode(state) || isWtMode(state))) {
9
- console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
10
- console.log(` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`);
11
- console.log("");
12
- return;
13
- }
14
-
15
- const sessions = listSessions();
16
- if (!sessions.length) {
17
- console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
18
- return;
19
- }
20
-
21
- console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
22
- for (const session of sessions) console.log(` ${GREEN}●${RESET} ${session}`);
23
- console.log("");
24
- }
1
+ import { listSessions } from "../../session.mjs";
2
+ import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
3
+ import {
4
+ isNativeMode,
5
+ isTeamAlive,
6
+ isWtMode,
7
+ } from "../services/runtime-mode.mjs";
8
+ import { loadTeamState } from "../services/state-store.mjs";
9
+
10
+ export function teamList() {
11
+ const state = loadTeamState();
12
+ if (state && isTeamAlive(state) && (isNativeMode(state) || isWtMode(state))) {
13
+ console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
14
+ console.log(
15
+ ` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`,
16
+ );
17
+ console.log("");
18
+ return;
19
+ }
20
+
21
+ const sessions = listSessions();
22
+ if (!sessions.length) {
23
+ console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
24
+ return;
25
+ }
26
+
27
+ console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
28
+ for (const session of sessions)
29
+ console.log(` ${GREEN}●${RESET} ${session}`);
30
+ console.log("");
31
+ }
@@ -1,10 +1,14 @@
1
1
  import { injectPrompt } from "../../pane.mjs";
2
2
  import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
+ import { ok, warn } from "../render.mjs";
3
4
  import { resolveMember } from "../services/member-selector.mjs";
4
5
  import { nativeRequest } from "../services/native-control.mjs";
5
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
6
+ import {
7
+ isNativeMode,
8
+ isTeamAlive,
9
+ isWtMode,
10
+ } from "../services/runtime-mode.mjs";
6
11
  import { loadTeamState } from "../services/state-store.mjs";
7
- import { ok, warn } from "../render.mjs";
8
12
 
9
13
  export async function teamSend(args = []) {
10
14
  const state = loadTeamState();
@@ -16,17 +20,26 @@ export async function teamSend(args = []) {
16
20
  const member = resolveMember(state, args[0]);
17
21
  const message = args.slice(1).join(" ");
18
22
  if (!member || !message) {
19
- console.log(`\n 사용법: ${WHITE}tfx multi send <lead|이름|번호> "메시지"${RESET}\n`);
23
+ console.log(
24
+ `\n 사용법: ${WHITE}tfx multi send <lead|이름|번호> "메시지"${RESET}\n`,
25
+ );
20
26
  return;
21
27
  }
22
28
  if (isWtMode(state)) {
23
- console.log(`\n ${YELLOW}⚠${RESET} wt 모드는 pane 프롬프트 자동 주입(send)이 지원되지 않습니다.\n ${DIM}수동 전달: 선택한 pane에 직접 붙여넣으세요.${RESET}\n`);
29
+ console.log(
30
+ `\n ${YELLOW}⚠${RESET} wt 모드는 pane 프롬프트 자동 주입(send)이 지원되지 않습니다.\n ${DIM}수동 전달: 선택한 pane에 직접 붙여넣으세요.${RESET}\n`,
31
+ );
24
32
  return;
25
33
  }
26
34
 
27
35
  if (isNativeMode(state)) {
28
- const result = await nativeRequest(state, "/send", { member: member.name, text: message });
29
- (result?.ok ? ok : warn)(`${member.name}${result?.ok ? "에 메시지 주입 완료" : " 메시지 주입 실패"}`);
36
+ const result = await nativeRequest(state, "/send", {
37
+ member: member.name,
38
+ text: message,
39
+ });
40
+ (result?.ok ? ok : warn)(
41
+ `${member.name}${result?.ok ? "에 메시지 주입 완료" : " 메시지 주입 실패"}`,
42
+ );
30
43
  console.log("");
31
44
  return;
32
45
  }
@@ -1,31 +1,62 @@
1
1
  import { decomposeTask } from "../../../orchestrator.mjs";
2
- import { hasWindowsTerminal, hasWindowsTerminalSession } from "../../../session.mjs";
3
- import { AMBER, BOLD, DIM, GREEN, RED, RESET, WHITE } from "../../../shared.mjs";
4
- import { getDefaultHubUrl, getHubInfo, startHubDaemon } from "../../services/hub-client.mjs";
5
- import { ensureTmuxOrExit } from "../../services/runtime-mode.mjs";
6
- import { saveTeamState } from "../../services/state-store.mjs";
2
+ import {
3
+ AMBER,
4
+ BOLD,
5
+ DIM,
6
+ GREEN,
7
+ RED,
8
+ RESET,
9
+ WHITE,
10
+ } from "../../../shared.mjs";
7
11
  import { fail, ok, warn } from "../../render.mjs";
12
+ import {
13
+ getDefaultHubUrl,
14
+ getHubInfo,
15
+ startHubDaemon,
16
+ } from "../../services/hub-client.mjs";
17
+ import {
18
+ ensureTmuxOrExit,
19
+ resolveEffectiveMode,
20
+ } from "../../services/runtime-mode.mjs";
21
+ import { saveTeamState } from "../../services/state-store.mjs";
8
22
  import { parseTeamArgs } from "./parse-args.mjs";
23
+ import { startHeadlessTeam } from "./start-headless.mjs";
9
24
  import { startInProcessTeam } from "./start-in-process.mjs";
10
25
  import { startMuxTeam } from "./start-mux.mjs";
11
- import { startHeadlessTeam } from "./start-headless.mjs";
12
26
  import { startWtTeam } from "./start-wt.mjs";
13
27
 
14
28
  function printStartUsage() {
15
29
  console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
16
30
  console.log(` 사용법: ${WHITE}tfx multi "작업 설명"${RESET}`);
17
- console.log(` ${WHITE}tfx multi --agents codex,gemini --lead claude "작업"${RESET}`);
18
- console.log(` ${WHITE}tfx multi --teammate-mode headless "작업"${RESET} ${DIM}(psmux 헤드리스, 기본)${RESET}`);
19
- console.log(` ${WHITE}tfx multi --dashboard-layout lite "작업"${RESET} ${DIM}(dashboard-lite 기본 뷰)${RESET}`);
20
- console.log(` ${WHITE}tfx multi --dashboard-layout auto "작업"${RESET} ${DIM}(dashboard viewer 레이아웃 자동)${RESET}`);
21
- console.log(` ${WHITE}tfx multi --dashboard-anchor window "작업"${RESET} ${DIM}(dashboard anchor: window|tab, 기본 window)${RESET}`);
22
- console.log(` ${WHITE}tfx multi --teammate-mode wt "작업"${RESET} ${DIM}(Windows Terminal split-pane)${RESET}`);
23
- console.log(` ${WHITE}tfx multi --teammate-mode in-process "작업"${RESET} ${DIM}(mux 불필요)${RESET}\n`);
31
+ console.log(
32
+ ` ${WHITE}tfx multi --agents codex,gemini --lead claude "작업"${RESET}`,
33
+ );
34
+ console.log(
35
+ ` ${WHITE}tfx multi --teammate-mode headless "작업"${RESET} ${DIM}(psmux 헤드리스, 기본)${RESET}`,
36
+ );
37
+ console.log(
38
+ ` ${WHITE}tfx multi --dashboard-layout lite "작업"${RESET} ${DIM}(dashboard-lite 기본 뷰)${RESET}`,
39
+ );
40
+ console.log(
41
+ ` ${WHITE}tfx multi --dashboard-layout auto "작업"${RESET} ${DIM}(dashboard viewer 레이아웃 자동)${RESET}`,
42
+ );
43
+ console.log(
44
+ ` ${WHITE}tfx multi --dashboard-anchor window "작업"${RESET} ${DIM}(dashboard anchor: window|tab, 기본 window)${RESET}`,
45
+ );
46
+ console.log(
47
+ ` ${WHITE}tfx multi --teammate-mode wt "작업"${RESET} ${DIM}(Windows Terminal split-pane)${RESET}`,
48
+ );
49
+ console.log(
50
+ ` ${WHITE}tfx multi --teammate-mode in-process "작업"${RESET} ${DIM}(mux 불필요)${RESET}\n`,
51
+ );
24
52
  }
25
53
 
26
54
  function printWorkerPreview(agents, subtasks) {
27
55
  for (let index = 0; index < subtasks.length; index += 1) {
28
- const preview = subtasks[index].length > 44 ? `${subtasks[index].slice(0, 44)}…` : subtasks[index];
56
+ const preview =
57
+ subtasks[index].length > 44
58
+ ? `${subtasks[index].slice(0, 44)}…`
59
+ : subtasks[index];
29
60
  console.log(` ${DIM}[${agents[index]}-${index + 1}] ${preview}${RESET}`);
30
61
  }
31
62
  console.log("");
@@ -34,16 +65,43 @@ function printWorkerPreview(agents, subtasks) {
34
65
  function renderTmuxInstallHelp() {
35
66
  console.log(`\n ${RED}${BOLD}tmux 미발견${RESET}\n`);
36
67
  console.log(" 현재 선택한 모드는 tmux 기반 팀세션이 필요합니다.\n");
37
- console.log(` 설치:\n WSL2: ${WHITE}wsl sudo apt install tmux${RESET}\n macOS: ${WHITE}brew install tmux${RESET}\n Linux: ${WHITE}apt install tmux${RESET}\n`);
38
- console.log(` Windows에서는 WSL2를 권장합니다:\n 1. ${WHITE}wsl --install${RESET}\n 2. ${WHITE}wsl sudo apt install tmux${RESET}\n 3. ${WHITE}tfx multi "작업"${RESET}\n`);
68
+ console.log(
69
+ ` 설치:\n WSL2: ${WHITE}wsl sudo apt install tmux${RESET}\n macOS: ${WHITE}brew install tmux${RESET}\n Linux: ${WHITE}apt install tmux${RESET}\n`,
70
+ );
71
+ console.log(
72
+ ` Windows에서는 WSL2를 권장합니다:\n 1. ${WHITE}wsl --install${RESET}\n 2. ${WHITE}wsl sudo apt install tmux${RESET}\n 3. ${WHITE}tfx multi "작업"${RESET}\n`,
73
+ );
39
74
  }
40
75
 
41
76
  export { parseTeamArgs };
42
77
 
43
78
  export async function teamStart(args = []) {
44
- const { agents, lead, layout, teammateMode, task: rawTask, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, dashboardLayout, dashboardSize, dashboardAnchor, mcpProfile, model, cwd } = parseTeamArgs(args);
79
+ const {
80
+ agents,
81
+ lead,
82
+ layout,
83
+ teammateMode,
84
+ task: rawTask,
85
+ assigns,
86
+ autoAttach,
87
+ progressive,
88
+ timeoutSec,
89
+ verbose,
90
+ dashboard,
91
+ dashboardLayout,
92
+ dashboardSize,
93
+ dashboardAnchor,
94
+ mcpProfile,
95
+ model,
96
+ cwd,
97
+ nativeBridge,
98
+ nativeBridgeMode,
99
+ nativeBridgeUiOptOut,
100
+ } = parseTeamArgs(args);
45
101
  // --assign 사용 시 task를 자동 생성
46
- const task = rawTask || (assigns.length > 0 ? assigns.map(a => a.prompt).join(" + ") : "");
102
+ const task =
103
+ rawTask ||
104
+ (assigns.length > 0 ? assigns.map((a) => a.prompt).join(" + ") : "");
47
105
  if (!task) return printStartUsage();
48
106
 
49
107
  console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
@@ -57,7 +115,12 @@ export async function teamStart(args = []) {
57
115
  hub = await getHubInfo();
58
116
  if (!hub) {
59
117
  process.stdout.write(" Hub 시작 중...");
60
- try { hub = await startHubDaemon(); } catch (error) { if (error?.code === "HUB_SERVER_MISSING") fail("hub/server.mjs 없음 — hub 모듈이 설치되지 않음"); }
118
+ try {
119
+ hub = await startHubDaemon();
120
+ } catch (error) {
121
+ if (error?.code === "HUB_SERVER_MISSING")
122
+ fail("hub/server.mjs 없음 — hub 모듈이 설치되지 않음");
123
+ }
61
124
  console.log(` ${hub ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`}`);
62
125
  if (!hub) warn("Hub 시작 실패 — 수동으로 실행: tfx hub start");
63
126
  } else ok(`Hub: ${DIM}${hub.url}${RESET}`);
@@ -68,27 +131,81 @@ export async function teamStart(args = []) {
68
131
  const sessionId = `tfx-multi-${Date.now().toString(36).slice(-4)}${Math.random().toString(36).slice(2, 6)}`;
69
132
  const subtasks = decomposeTask(task, agents.length);
70
133
  const hubUrl = hub?.url || getDefaultHubUrl();
71
- let effectiveMode = teammateMode;
72
- if (effectiveMode === "wt" && !hasWindowsTerminal()) { warn("wt.exe 미발견 — in-process 모드로 자동 fallback"); effectiveMode = "in-process"; }
73
- if (effectiveMode === "wt" && !hasWindowsTerminalSession()) { warn("WT_SESSION 미감지(Windows Terminal 외부) — in-process 모드로 자동 fallback"); effectiveMode = "in-process"; }
134
+ const { mode: effectiveMode, warnings: modeWarnings } =
135
+ resolveEffectiveMode(teammateMode);
136
+ for (const message of modeWarnings) warn(message);
137
+ const effectiveNativeBridge =
138
+ effectiveMode === "headless" && !nativeBridgeUiOptOut ? true : nativeBridge;
74
139
 
75
140
  console.log(` 세션: ${WHITE}${sessionId}${RESET}`);
76
141
  console.log(` 모드: ${effectiveMode}`);
77
142
  console.log(` 리드: ${AMBER}${lead}${RESET}`);
78
- console.log(` 워커: ${agents.map((agent) => `${AMBER}${agent}${RESET}`).join(", ")}`);
143
+ console.log(
144
+ ` 워커: ${agents.map((agent) => `${AMBER}${agent}${RESET}`).join(", ")}`,
145
+ );
79
146
  printWorkerPreview(agents, subtasks);
80
147
 
81
148
  if (effectiveMode === "tmux") {
82
- try { ensureTmuxOrExit(); } catch { return renderTmuxInstallHelp(); }
149
+ try {
150
+ ensureTmuxOrExit();
151
+ } catch {
152
+ return renderTmuxInstallHelp();
153
+ }
83
154
  }
84
155
 
85
- const state = effectiveMode === "in-process"
86
- ? await startInProcessTeam({ sessionId, task, lead, agents, subtasks, hubUrl })
87
- : effectiveMode === "headless"
88
- ? await startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, dashboardLayout, dashboardSize, dashboardAnchor, mcpProfile, model, cwd })
89
- : effectiveMode === "wt"
90
- ? await startWtTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl })
91
- : await startMuxTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl, teammateMode: effectiveMode });
156
+ const state =
157
+ effectiveMode === "in-process"
158
+ ? await startInProcessTeam({
159
+ sessionId,
160
+ task,
161
+ lead,
162
+ agents,
163
+ subtasks,
164
+ hubUrl,
165
+ })
166
+ : effectiveMode === "headless"
167
+ ? await startHeadlessTeam({
168
+ sessionId,
169
+ task,
170
+ lead,
171
+ agents,
172
+ subtasks,
173
+ layout,
174
+ assigns,
175
+ autoAttach,
176
+ progressive,
177
+ timeoutSec,
178
+ verbose,
179
+ dashboard,
180
+ dashboardLayout,
181
+ dashboardSize,
182
+ dashboardAnchor,
183
+ mcpProfile,
184
+ model,
185
+ cwd,
186
+ nativeBridge: effectiveNativeBridge,
187
+ nativeBridgeMode,
188
+ })
189
+ : effectiveMode === "wt"
190
+ ? await startWtTeam({
191
+ sessionId,
192
+ task,
193
+ lead,
194
+ agents,
195
+ subtasks,
196
+ layout,
197
+ hubUrl,
198
+ })
199
+ : await startMuxTeam({
200
+ sessionId,
201
+ task,
202
+ lead,
203
+ agents,
204
+ subtasks,
205
+ layout,
206
+ hubUrl,
207
+ teammateMode: effectiveMode,
208
+ });
92
209
 
93
210
  if (!state) return fail("in-process supervisor 시작 실패");
94
211
  state.sessionId = sessionId;
@@ -100,7 +217,11 @@ export async function teamStart(args = []) {
100
217
  console.log(` ${DIM}제어: tfx multi send/control/tasks/status${RESET}\n`);
101
218
  } else if (effectiveMode === "wt") {
102
219
  ok("Windows Terminal wt 팀 시작 완료");
103
- console.log(` ${DIM}현재 pane 기준으로 ${state.layout} 분할 생성됨${RESET}`);
104
- console.log(` ${DIM}wt 모드는 자동 프롬프트 주입/Hub direct 제어(send/control)가 제한됩니다.${RESET}\n`);
220
+ console.log(
221
+ ` ${DIM}현재 pane 기준으로 ${state.layout} 분할 생성됨${RESET}`,
222
+ );
223
+ console.log(
224
+ ` ${DIM}wt 모드는 자동 프롬프트 주입/Hub direct 제어(send/control)가 제한됩니다.${RESET}\n`,
225
+ );
105
226
  }
106
227
  }
@@ -1,16 +1,33 @@
1
1
  import { resolve } from "node:path";
2
- import { normalizeLayout, normalizeTeammateMode } from "../../services/runtime-mode.mjs";
3
- import { parseDashboardLayout } from "../../../dashboard-layout.mjs";
4
2
  import { parseDashboardAnchor } from "../../../dashboard-anchor.mjs";
3
+ import { parseDashboardLayout } from "../../../dashboard-layout.mjs";
4
+ import {
5
+ normalizeLayout,
6
+ normalizeTeammateMode,
7
+ } from "../../services/runtime-mode.mjs";
5
8
 
6
9
  // --assign 파싱 시 마지막 콜론 뒤를 role로 인식할 알려진 역할/CLI 이름
7
10
  const KNOWN_ROLES = new Set([
8
- "codex", "gemini", "claude",
9
- "executor", "architect", "planner", "analyst", "critic",
10
- "debugger", "verifier", "code-reviewer", "security-reviewer",
11
- "test-engineer", "designer", "writer", "scientist",
11
+ "codex",
12
+ "gemini",
13
+ "claude",
14
+ "executor",
15
+ "architect",
16
+ "planner",
17
+ "analyst",
18
+ "critic",
19
+ "debugger",
20
+ "verifier",
21
+ "code-reviewer",
22
+ "security-reviewer",
23
+ "test-engineer",
24
+ "designer",
25
+ "writer",
26
+ "scientist",
12
27
  ]);
13
28
 
29
+ const NATIVE_BRIDGE_MODES = new Set(["roster", "agents", "claude-wrapper"]);
30
+
14
31
  /**
15
32
  * --assign "cli:prompt:role" 형식을 콜론-안전하게 파싱한다.
16
33
  * 프롬프트 내부의 콜론(:)은 구분자로 취급하지 않는다.
@@ -29,7 +46,10 @@ function parseAssignValue(raw) {
29
46
 
30
47
  const lastColon = rest.lastIndexOf(":");
31
48
  if (lastColon > 0) {
32
- const candidate = rest.slice(lastColon + 1).trim().toLowerCase();
49
+ const candidate = rest
50
+ .slice(lastColon + 1)
51
+ .trim()
52
+ .toLowerCase();
33
53
  if (KNOWN_ROLES.has(candidate)) {
34
54
  return { cli, prompt: rest.slice(0, lastColon).trim(), role: candidate };
35
55
  }
@@ -51,21 +71,32 @@ export function parseTeamArgs(args = []) {
51
71
  let verbose = false;
52
72
  let dashboard = true;
53
73
  let dashboardLayout = "lite";
54
- let dashboardSize = 0.40;
74
+ let dashboardSize = 0.4;
55
75
  let dashboardAnchor = "window";
56
76
  let mcpProfile = "";
57
77
  let model = "";
58
78
  let cwd = "";
79
+ let nativeBridge = false;
80
+ let nativeBridgeMode = "agents";
81
+ let nativeBridgeExplicit = false;
82
+ let nativeBridgeUiRequested = false;
83
+ let nativeBridgeUiOptOut = false;
59
84
 
60
85
  for (let index = 0; index < args.length; index += 1) {
61
86
  const current = args[index];
62
87
  if (current === "--agents" && args[index + 1]) {
63
- agents = args[++index].split(",").map((value) => value.trim().toLowerCase()).filter(Boolean);
88
+ agents = args[++index]
89
+ .split(",")
90
+ .map((value) => value.trim().toLowerCase())
91
+ .filter(Boolean);
64
92
  } else if (current === "--lead" && args[index + 1]) {
65
93
  lead = args[++index].trim().toLowerCase();
66
94
  } else if (current === "--layout" && args[index + 1]) {
67
95
  layout = args[++index];
68
- } else if ((current === "--teammate-mode" || current === "--mode") && args[index + 1]) {
96
+ } else if (
97
+ (current === "--teammate-mode" || current === "--mode") &&
98
+ args[index + 1]
99
+ ) {
69
100
  teammateMode = args[++index];
70
101
  } else if (current === "--assign" && args[index + 1]) {
71
102
  const parsed = parseAssignValue(args[++index]);
@@ -83,7 +114,10 @@ export function parseTeamArgs(args = []) {
83
114
  } else if (current === "--dashboard-layout" && args[index + 1]) {
84
115
  dashboardLayout = parseDashboardLayout(args[++index]);
85
116
  } else if (current === "--dashboard-size" && args[index + 1]) {
86
- dashboardSize = Math.min(0.8, Math.max(0.2, parseFloat(args[++index]) || 0.50));
117
+ dashboardSize = Math.min(
118
+ 0.8,
119
+ Math.max(0.2, parseFloat(args[++index]) || 0.5),
120
+ );
87
121
  } else if (current === "--dashboard-anchor" && args[index + 1]) {
88
122
  dashboardAnchor = parseDashboardAnchor(args[++index]);
89
123
  } else if (current === "--no-progressive") {
@@ -94,6 +128,37 @@ export function parseTeamArgs(args = []) {
94
128
  mcpProfile = args[++index].trim();
95
129
  } else if ((current === "--model" || current === "-m") && args[index + 1]) {
96
130
  model = args[++index].trim();
131
+ } else if (current === "--native-bridge" || current === "-nb") {
132
+ nativeBridge = true;
133
+ nativeBridgeExplicit = true;
134
+ } else if (current === "--native-bridge-ui") {
135
+ if (nativeBridgeUiOptOut) {
136
+ throw new Error(
137
+ "cannot combine --native-bridge-ui and --no-native-bridge-ui",
138
+ );
139
+ }
140
+ nativeBridge = true;
141
+ nativeBridgeMode = "agents";
142
+ nativeBridgeExplicit = true;
143
+ nativeBridgeUiRequested = true;
144
+ } else if (current === "--no-native-bridge-ui") {
145
+ if (nativeBridgeUiRequested) {
146
+ throw new Error(
147
+ "cannot combine --native-bridge-ui and --no-native-bridge-ui",
148
+ );
149
+ }
150
+ nativeBridge = false;
151
+ nativeBridgeUiOptOut = true;
152
+ } else if (current === "--native-bridge-mode") {
153
+ const mode = args[++index];
154
+ if (!NATIVE_BRIDGE_MODES.has(mode)) {
155
+ throw new Error(
156
+ `unknown native bridge mode: ${mode || ""}; expected roster, agents, or claude-wrapper`,
157
+ );
158
+ }
159
+ nativeBridge = true;
160
+ nativeBridgeMode = mode;
161
+ nativeBridgeExplicit = true;
97
162
  } else if (current === "--cwd" && args[index + 1]) {
98
163
  let p = args[++index].trim();
99
164
  // MSYS/Git Bash 드라이브 문자 변환: /c/... → C:/...
@@ -108,11 +173,20 @@ export function parseTeamArgs(args = []) {
108
173
  }
109
174
  }
110
175
 
176
+ const normalizedTeammateMode = normalizeTeammateMode(teammateMode);
177
+ if (
178
+ !nativeBridgeExplicit &&
179
+ !nativeBridgeUiOptOut &&
180
+ normalizedTeammateMode === "headless"
181
+ ) {
182
+ nativeBridge = true;
183
+ }
184
+
111
185
  return {
112
186
  agents,
113
187
  lead,
114
188
  layout: normalizeLayout(layout),
115
- teammateMode: normalizeTeammateMode(teammateMode),
189
+ teammateMode: normalizedTeammateMode,
116
190
  task: taskParts.join(" ").trim(),
117
191
  assigns,
118
192
  autoAttach,
@@ -126,5 +200,8 @@ export function parseTeamArgs(args = []) {
126
200
  mcpProfile,
127
201
  model,
128
202
  cwd,
203
+ nativeBridge,
204
+ nativeBridgeMode,
205
+ nativeBridgeUiOptOut,
129
206
  };
130
207
  }