@triflux/remote 10.50.2 → 10.51.0

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 (97) hide show
  1. package/cto/brief.mjs +0 -12
  2. package/cto/collect.mjs +0 -73
  3. package/cto/current.schema.json +0 -4
  4. package/cto/status.mjs +2 -50
  5. package/hub/index.mjs +0 -8
  6. package/hub/team/agent-map.json +0 -1
  7. package/hub/team/ansi.mjs +1 -35
  8. package/hub/team/backend.mjs +9 -145
  9. package/hub/team/cli/commands/attach.mjs +1 -17
  10. package/hub/team/cli/commands/control.mjs +12 -38
  11. package/hub/team/cli/commands/debug.mjs +1 -37
  12. package/hub/team/cli/commands/focus.mjs +5 -31
  13. package/hub/team/cli/commands/interrupt.mjs +6 -25
  14. package/hub/team/cli/commands/kill.mjs +1 -30
  15. package/hub/team/cli/commands/list.mjs +0 -16
  16. package/hub/team/cli/commands/send.mjs +6 -26
  17. package/hub/team/cli/commands/start/index.mjs +41 -133
  18. package/hub/team/cli/commands/start/parse-args.mjs +15 -40
  19. package/hub/team/cli/commands/start/start-headless.mjs +138 -105
  20. package/hub/team/cli/commands/start/start-mux.mjs +0 -3
  21. package/hub/team/cli/commands/status.mjs +2 -57
  22. package/hub/team/cli/commands/stop.mjs +14 -19
  23. package/hub/team/cli/help.mjs +4 -5
  24. package/hub/team/cli/manifest.mjs +5 -1
  25. package/hub/team/cli/render.mjs +0 -12
  26. package/hub/team/cli/services/hub-client.mjs +2 -88
  27. package/hub/team/cli/services/runtime-mode.mjs +27 -98
  28. package/hub/team/handoff.mjs +3 -34
  29. package/hub/team/headless.mjs +13 -754
  30. package/hub/team/orchestrator.mjs +4 -30
  31. package/hub/team/pane.mjs +6 -28
  32. package/hub/team/psmux.mjs +0 -1
  33. package/hub/team/remote-session.mjs +1 -2
  34. package/hub/team/session.mjs +7 -91
  35. package/hub/team/swarm-intent.mjs +0 -198
  36. package/hub/team/tui-core.mjs +1 -1
  37. package/hub/team/tui-viewer.mjs +2 -2
  38. package/hub/team/tui.mjs +8 -269
  39. package/hub/team/wt-manager.mjs +2 -2
  40. package/hub/workers/factory.mjs +4 -37
  41. package/hub/workers/lib/jsonrpc-ws-uds.mjs +0 -2
  42. package/package.json +1 -1
  43. package/scripts/lib/agent-route-policy.mjs +162 -14
  44. package/scripts/lib/claudemd-scanner.mjs +2 -199
  45. package/scripts/lib/cli-claude.mjs +0 -2
  46. package/scripts/lib/context.mjs +3 -19
  47. package/scripts/lib/env-probe.mjs +5 -64
  48. package/scripts/lib/gemini-profiles.mjs +2 -69
  49. package/scripts/lib/machine-profile.mjs +3 -17
  50. package/scripts/lib/mcp-filter.mjs +109 -53
  51. package/scripts/lib/mcp-health.mjs +2 -2
  52. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  53. package/scripts/lib/psmux-info.mjs +11 -59
  54. package/scripts/lib/skill-template.mjs +0 -245
  55. package/hub/team/build-worker-prompt.mjs +0 -92
  56. package/hub/team/check-mcp-hub.mjs +0 -49
  57. package/hub/team/claude-native-bridge.mjs +0 -987
  58. package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
  59. package/hub/team/cli/commands/start/start-wt.mjs +0 -86
  60. package/hub/team/cli/services/native-control.mjs +0 -145
  61. package/hub/team/codex-review.mjs +0 -520
  62. package/hub/team/conductor-mesh-bridge.mjs +0 -122
  63. package/hub/team/conductor.mjs +0 -1529
  64. package/hub/team/consensus-meta.mjs +0 -176
  65. package/hub/team/cto-pull.mjs +0 -31
  66. package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
  67. package/hub/team/dashboard.mjs +0 -322
  68. package/hub/team/event-log.mjs +0 -84
  69. package/hub/team/execution-mode.mjs +0 -324
  70. package/hub/team/extract-completion-payload.mjs +0 -110
  71. package/hub/team/headless-bridge-session.mjs +0 -17
  72. package/hub/team/health-probe.mjs +0 -435
  73. package/hub/team/interactive-native-launcher.mjs +0 -119
  74. package/hub/team/interactive-native-worker.mjs +0 -80
  75. package/hub/team/interactive-tui-transport.mjs +0 -479
  76. package/hub/team/launcher-template.mjs +0 -144
  77. package/hub/team/mcp-selector.mjs +0 -145
  78. package/hub/team/native-supervisor.mjs +0 -460
  79. package/hub/team/native.mjs +0 -727
  80. package/hub/team/recovery-store.mjs +0 -149
  81. package/hub/team/remote-probe.mjs +0 -292
  82. package/hub/team/remote-watcher.mjs +0 -506
  83. package/hub/team/runtime-strategy.mjs +0 -200
  84. package/hub/team/schemas/worker-completion.json +0 -39
  85. package/hub/team/sentinel-capture.mjs +0 -127
  86. package/hub/team/swarm-cli.mjs +0 -390
  87. package/hub/team/swarm-hypervisor.mjs +0 -2206
  88. package/hub/team/swarm-locks.mjs +0 -335
  89. package/hub/team/swarm-planner.mjs +0 -371
  90. package/hub/team/swarm-preflight.mjs +0 -366
  91. package/hub/team/swarm-reconciler.mjs +0 -242
  92. package/hub/team/tui-remote-adapter.mjs +0 -377
  93. package/hub/team/worker-completion-validator.mjs +0 -79
  94. package/hub/team/worktree-lifecycle.mjs +0 -639
  95. package/hub/team/wt-templates.json +0 -43
  96. package/hub/workers/antigravity-route-worker.mjs +0 -179
  97. package/scripts/lib/dynamic-route-cli.mjs +0 -107
@@ -1,4 +1,4 @@
1
- import { attachSession, focusPane, focusWtPane } from "../../session.mjs";
1
+ import { attachSession, focusPane } from "../../session.mjs";
2
2
  import { DIM, RESET, WHITE } from "../../shared.mjs";
3
3
  import { fail, ok, warn } from "../render.mjs";
4
4
  import {
@@ -7,25 +7,18 @@ import {
7
7
  wantsWtAttachFallback,
8
8
  } from "../services/attach-fallback.mjs";
9
9
  import { resolveMember } from "../services/member-selector.mjs";
10
- import {
11
- isNativeMode,
12
- isTeamAlive,
13
- isWtMode,
14
- } from "../services/runtime-mode.mjs";
10
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
15
11
  import { loadTeamState } from "../services/state-store.mjs";
16
12
 
17
13
  export async function teamFocus(args = []) {
18
14
  const state = loadTeamState();
15
+ if (state?.teammateMode === "headless") {
16
+ throw new Error("headless 실행에는 지원하지 않는다");
17
+ }
19
18
  if (!state || !isTeamAlive(state)) {
20
19
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
21
20
  return;
22
21
  }
23
- if (isNativeMode(state)) {
24
- console.log(
25
- `\n ${DIM}in-process 모드는 focus/attach 개념이 없습니다.${RESET}\n ${DIM}직접 지시: tfx multi send <대상> "메시지"${RESET}\n`,
26
- );
27
- return;
28
- }
29
22
 
30
23
  const member = resolveMember(state, args[0]);
31
24
  if (!member) {
@@ -35,25 +28,6 @@ export async function teamFocus(args = []) {
35
28
  return;
36
29
  }
37
30
 
38
- if (isWtMode(state)) {
39
- const paneIndex = Number(/^wt:(\d+)$/.exec(member.pane || "")?.[1]);
40
- if (!Number.isFinite(paneIndex)) {
41
- console.log(
42
- `\n ${DIM}wt pane 인덱스 파싱 실패: ${member.pane}${RESET}\n`,
43
- );
44
- return;
45
- }
46
- if (
47
- focusWtPane(paneIndex, {
48
- layout: state?.wt?.layout || state?.layout || "1xN",
49
- })
50
- )
51
- ok(`${member.name} pane 포커스 이동 (wt)`);
52
- else warn("wt pane 포커스 이동 실패 (WT_SESSION/wt.exe 상태 확인 필요)");
53
- console.log("");
54
- return;
55
- }
56
-
57
31
  focusPane(member.pane, { zoom: false });
58
32
  try {
59
33
  attachSession(state.sessionName);
@@ -1,17 +1,15 @@
1
1
  import { sendKeys } from "../../pane.mjs";
2
- import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
- import { ok, warn } from "../render.mjs";
2
+ import { DIM, RESET, WHITE } from "../../shared.mjs";
3
+ import { ok } from "../render.mjs";
4
4
  import { resolveMember } from "../services/member-selector.mjs";
5
- import { nativeRequest } from "../services/native-control.mjs";
6
- import {
7
- isNativeMode,
8
- isTeamAlive,
9
- isWtMode,
10
- } from "../services/runtime-mode.mjs";
5
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
11
6
  import { loadTeamState } from "../services/state-store.mjs";
12
7
 
13
8
  export async function teamInterrupt(args = []) {
14
9
  const state = loadTeamState();
10
+ if (state?.teammateMode === "headless") {
11
+ throw new Error("headless 실행에는 지원하지 않는다");
12
+ }
15
13
  if (!state || !isTeamAlive(state)) {
16
14
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
17
15
  return;
@@ -24,23 +22,6 @@ export async function teamInterrupt(args = []) {
24
22
  );
25
23
  return;
26
24
  }
27
- if (isWtMode(state)) {
28
- console.log(
29
- `\n ${YELLOW}⚠${RESET} wt 모드에서는 pane stdin 주입이 지원되지 않아 interrupt를 자동 전송할 수 없습니다.\n ${DIM}수동으로 해당 pane에서 Ctrl+C를 입력하세요.${RESET}\n`,
30
- );
31
- return;
32
- }
33
-
34
- if (isNativeMode(state)) {
35
- const result = await nativeRequest(state, "/interrupt", {
36
- member: member.name,
37
- });
38
- (result?.ok ? ok : warn)(
39
- `${member.name} ${result?.ok ? "인터럽트 전송" : "인터럽트 실패"}`,
40
- );
41
- console.log("");
42
- return;
43
- }
44
25
 
45
26
  sendKeys(member.pane, "C-c");
46
27
  ok(`${member.name} 인터럽트 전송`);
@@ -1,39 +1,10 @@
1
- import { closeWtSession, killSession, listSessions } from "../../session.mjs";
1
+ import { killSession, listSessions } from "../../session.mjs";
2
2
  import { DIM, RESET } from "../../shared.mjs";
3
3
  import { ok } from "../render.mjs";
4
- import { nativeRequest } from "../services/native-control.mjs";
5
- import {
6
- isNativeMode,
7
- isTeamAlive,
8
- isWtMode,
9
- } from "../services/runtime-mode.mjs";
10
4
  import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
11
5
 
12
6
  export async function teamKill() {
13
7
  const state = loadTeamState();
14
- if (state && isNativeMode(state) && isTeamAlive(state)) {
15
- await nativeRequest(state, "/stop", {});
16
- try {
17
- process.kill(state.native.supervisorPid, "SIGTERM");
18
- } catch {}
19
- clearTeamState(state.sessionId);
20
- ok(`종료: ${state.sessionName}`);
21
- console.log("");
22
- return;
23
- }
24
- if (state && isWtMode(state)) {
25
- const closed = closeWtSession({
26
- layout: state?.wt?.layout || state?.layout || "1xN",
27
- paneCount: state?.wt?.paneCount ?? (state.members || []).length,
28
- });
29
- clearTeamState(state.sessionId);
30
- ok(
31
- `종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`,
32
- );
33
- console.log("");
34
- return;
35
- }
36
-
37
8
  const sessions = listSessions();
38
9
  if (!sessions.length) {
39
10
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
@@ -1,23 +1,7 @@
1
1
  import { listSessions } from "../../session.mjs";
2
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
3
 
10
4
  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
5
  const sessions = listSessions();
22
6
  if (!sessions.length) {
23
7
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
@@ -1,17 +1,15 @@
1
1
  import { injectPrompt } from "../../pane.mjs";
2
- import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
- import { ok, warn } from "../render.mjs";
2
+ import { DIM, RESET, WHITE } from "../../shared.mjs";
3
+ import { ok } from "../render.mjs";
4
4
  import { resolveMember } from "../services/member-selector.mjs";
5
- import { nativeRequest } from "../services/native-control.mjs";
6
- import {
7
- isNativeMode,
8
- isTeamAlive,
9
- isWtMode,
10
- } from "../services/runtime-mode.mjs";
5
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
11
6
  import { loadTeamState } from "../services/state-store.mjs";
12
7
 
13
8
  export async function teamSend(args = []) {
14
9
  const state = loadTeamState();
10
+ if (state?.teammateMode === "headless") {
11
+ throw new Error("headless 실행에는 지원하지 않는다");
12
+ }
15
13
  if (!state || !isTeamAlive(state)) {
16
14
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
17
15
  return;
@@ -25,24 +23,6 @@ export async function teamSend(args = []) {
25
23
  );
26
24
  return;
27
25
  }
28
- if (isWtMode(state)) {
29
- console.log(
30
- `\n ${YELLOW}⚠${RESET} wt 모드는 pane 프롬프트 자동 주입(send)이 지원되지 않습니다.\n ${DIM}수동 전달: 선택한 pane에 직접 붙여넣으세요.${RESET}\n`,
31
- );
32
- return;
33
- }
34
-
35
- if (isNativeMode(state)) {
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
- );
43
- console.log("");
44
- return;
45
- }
46
26
 
47
27
  injectPrompt(member.pane, message);
48
28
  ok(`${member.name}에 메시지 주입 완료`);
@@ -1,29 +1,14 @@
1
1
  import { decomposeTask } from "../../../orchestrator.mjs";
2
- import {
3
- AMBER,
4
- BOLD,
5
- DIM,
6
- GREEN,
7
- RED,
8
- RESET,
9
- WHITE,
10
- } from "../../../shared.mjs";
11
- import { fail, ok, warn } from "../../render.mjs";
12
- import {
13
- getDefaultHubUrl,
14
- getHubInfo,
15
- startHubDaemon,
16
- } from "../../services/hub-client.mjs";
2
+ import { AMBER, BOLD, DIM, RESET, WHITE } from "../../../shared.mjs";
3
+ import { fail } from "../../render.mjs";
17
4
  import {
18
5
  ensureTmuxOrExit,
19
- resolveEffectiveMode,
6
+ normalizeTeammateMode,
20
7
  } from "../../services/runtime-mode.mjs";
21
8
  import { saveTeamState } from "../../services/state-store.mjs";
22
9
  import { parseTeamArgs } from "./parse-args.mjs";
23
10
  import { startHeadlessTeam } from "./start-headless.mjs";
24
- import { startInProcessTeam } from "./start-in-process.mjs";
25
11
  import { startMuxTeam } from "./start-mux.mjs";
26
- import { startWtTeam } from "./start-wt.mjs";
27
12
 
28
13
  function printStartUsage() {
29
14
  console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
@@ -43,12 +28,6 @@ function printStartUsage() {
43
28
  console.log(
44
29
  ` ${WHITE}tfx multi --dashboard-anchor window "작업"${RESET} ${DIM}(dashboard anchor: window|tab, 기본 window)${RESET}`,
45
30
  );
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
- );
52
31
  }
53
32
 
54
33
  function printWorkerPreview(agents, subtasks) {
@@ -62,14 +41,6 @@ function printWorkerPreview(agents, subtasks) {
62
41
  console.log("");
63
42
  }
64
43
 
65
- function renderTmuxInstallHelp() {
66
- console.log(`\n ${RED}${BOLD}tmux 미발견${RESET}\n`);
67
- console.log(" 현재 선택한 모드는 tmux 기반 팀세션이 필요합니다.\n");
68
- console.log(
69
- ` 설치:\n Windows: ${WHITE}winget install psmux${RESET}\n macOS: ${WHITE}brew install tmux${RESET}\n Linux: ${WHITE}apt install tmux${RESET}\n`,
70
- );
71
- }
72
-
73
44
  export { parseTeamArgs };
74
45
 
75
46
  export function resolveTeamWorkers({ agents, task, assigns = [] }) {
@@ -116,37 +87,14 @@ export async function teamStart(args = []) {
116
87
  assigns,
117
88
  });
118
89
 
119
- console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
120
-
121
- // P1b: 워커 수 계산 — 단일 워커 headless에는 Hub 불필요
122
- const workerCount = agents.length;
123
- const needsHub = workerCount >= 2 || teammateMode !== "headless";
90
+ const effectiveMode = normalizeTeammateMode(teammateMode);
91
+ if (effectiveMode !== "headless") ensureTmuxOrExit();
92
+ const effectiveNativeBridge =
93
+ effectiveMode === "headless" && !nativeBridgeUiOptOut ? true : nativeBridge;
124
94
 
125
- let hub = null;
126
- if (needsHub) {
127
- hub = await getHubInfo();
128
- if (!hub) {
129
- process.stdout.write(" Hub 시작 중...");
130
- try {
131
- hub = await startHubDaemon();
132
- } catch (error) {
133
- if (error?.code === "HUB_SERVER_MISSING")
134
- fail("hub/server.mjs 없음 — hub 모듈이 설치되지 않음");
135
- }
136
- console.log(` ${hub ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`}`);
137
- if (!hub) warn("Hub 시작 실패 — 수동으로 실행: tfx hub start");
138
- } else ok(`Hub: ${DIM}${hub.url}${RESET}`);
139
- } else {
140
- ok(`Hub: ${DIM}건너뜀 (단일 워커 headless)${RESET}`);
141
- }
95
+ console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
142
96
 
143
97
  const sessionId = `tfx-multi-${Date.now().toString(36).slice(-4)}${Math.random().toString(36).slice(2, 6)}`;
144
- const hubUrl = hub?.url || getDefaultHubUrl();
145
- const { mode: effectiveMode, warnings: modeWarnings } =
146
- resolveEffectiveMode(teammateMode);
147
- for (const message of modeWarnings) warn(message);
148
- const effectiveNativeBridge =
149
- effectiveMode === "headless" && !nativeBridgeUiOptOut ? true : nativeBridge;
150
98
 
151
99
  console.log(` 세션: ${WHITE}${sessionId}${RESET}`);
152
100
  console.log(` 모드: ${effectiveMode}`);
@@ -156,83 +104,43 @@ export async function teamStart(args = []) {
156
104
  );
157
105
  printWorkerPreview(agents, subtasks);
158
106
 
159
- if (effectiveMode === "tmux") {
160
- try {
161
- ensureTmuxOrExit();
162
- } catch {
163
- return renderTmuxInstallHelp();
164
- }
107
+ if (effectiveMode === "headless") {
108
+ return startHeadlessTeam({
109
+ sessionId,
110
+ task,
111
+ lead,
112
+ agents,
113
+ subtasks,
114
+ layout,
115
+ assigns,
116
+ autoAttach,
117
+ progressive,
118
+ timeoutSec,
119
+ verbose,
120
+ dashboard,
121
+ dashboardLayout,
122
+ dashboardSize,
123
+ dashboardAnchor,
124
+ mcpProfile,
125
+ model,
126
+ cwd,
127
+ nativeBridge: effectiveNativeBridge,
128
+ nativeBridgeMode,
129
+ });
165
130
  }
166
131
 
167
- const state =
168
- effectiveMode === "in-process"
169
- ? await startInProcessTeam({
170
- sessionId,
171
- task,
172
- lead,
173
- agents,
174
- subtasks,
175
- hubUrl,
176
- })
177
- : effectiveMode === "headless"
178
- ? await startHeadlessTeam({
179
- sessionId,
180
- task,
181
- lead,
182
- agents,
183
- subtasks,
184
- layout,
185
- assigns,
186
- autoAttach,
187
- progressive,
188
- timeoutSec,
189
- verbose,
190
- dashboard,
191
- dashboardLayout,
192
- dashboardSize,
193
- dashboardAnchor,
194
- mcpProfile,
195
- model,
196
- cwd,
197
- nativeBridge: effectiveNativeBridge,
198
- nativeBridgeMode,
199
- })
200
- : effectiveMode === "wt"
201
- ? await startWtTeam({
202
- sessionId,
203
- task,
204
- lead,
205
- agents,
206
- subtasks,
207
- layout,
208
- hubUrl,
209
- })
210
- : await startMuxTeam({
211
- sessionId,
212
- task,
213
- lead,
214
- agents,
215
- subtasks,
216
- layout,
217
- hubUrl,
218
- teammateMode: effectiveMode,
219
- });
132
+ const state = await startMuxTeam({
133
+ sessionId,
134
+ task,
135
+ lead,
136
+ agents,
137
+ subtasks,
138
+ layout,
139
+ teammateMode: effectiveMode,
140
+ });
220
141
 
221
- if (!state) return fail("in-process supervisor 시작 실패");
142
+ if (!state) return fail("팀 세션 시작 실패");
222
143
  state.sessionId = sessionId;
223
144
  saveTeamState(state, sessionId);
224
145
  if (typeof state.postSave === "function") state.postSave();
225
- if (effectiveMode === "in-process") {
226
- ok("네이티브 in-process 팀 시작 완료");
227
- console.log(` ${DIM}tmux 없이 실행됨 (직접 CLI 프로세스)${RESET}`);
228
- console.log(` ${DIM}제어: tfx multi send/control/tasks/status${RESET}\n`);
229
- } else if (effectiveMode === "wt") {
230
- ok("Windows Terminal wt 팀 시작 완료");
231
- console.log(
232
- ` ${DIM}현재 pane 기준으로 ${state.layout} 분할 생성됨${RESET}`,
233
- );
234
- console.log(
235
- ` ${DIM}wt 모드는 자동 프롬프트 주입/Hub direct 제어(send/control)가 제한됩니다.${RESET}\n`,
236
- );
237
- }
238
146
  }
@@ -1,3 +1,4 @@
1
+ import { createRequire } from "node:module";
1
2
  import { resolve } from "node:path";
2
3
  import { parseDashboardAnchor } from "../../../dashboard-anchor.mjs";
3
4
  import { parseDashboardLayout } from "../../../dashboard-layout.mjs";
@@ -6,35 +7,7 @@ import {
6
7
  normalizeTeammateMode,
7
8
  } from "../../services/runtime-mode.mjs";
8
9
 
9
- // --assign 파싱 시 마지막 콜론 뒤를 role로 인식할 알려진 역할/CLI 이름
10
- const KNOWN_ROLES = new Set([
11
- "codex",
12
- "antigravity",
13
- "agy",
14
- "gemini",
15
- "claude",
16
- "executor",
17
- "architect",
18
- "planner",
19
- "analyst",
20
- "critic",
21
- "debugger",
22
- "verifier",
23
- "code-reviewer",
24
- "security-reviewer",
25
- "test-engineer",
26
- "designer",
27
- "writer",
28
- "researcher",
29
- "scientist",
30
- ]);
31
-
32
- const NATIVE_BRIDGE_MODES = new Set([
33
- "roster",
34
- "agents",
35
- "interactive-attach",
36
- "claude-wrapper",
37
- ]);
10
+ const AGENT_TO_CLI = createRequire(import.meta.url)("../../../agent-map.json");
38
11
 
39
12
  /**
40
13
  * --assign "cli:prompt:role" 형식을 콜론-안전하게 파싱한다.
@@ -42,7 +15,7 @@ const NATIVE_BRIDGE_MODES = new Set([
42
15
  *
43
16
  * 규칙:
44
17
  * 1. 첫 번째 콜론 앞 = CLI 이름
45
- * 2. 마지막 콜론 뒤가 KNOWN_ROLES에 있으면 role, 나머지가 prompt
18
+ * 2. 마지막 콜론 뒤가 agent-map.json에 있으면 role, 나머지가 prompt
46
19
  * 3. 그 외에는 첫 콜론 뒤 전체가 prompt, role은 빈 문자열
47
20
  */
48
21
  function parseAssignValue(raw) {
@@ -58,7 +31,7 @@ function parseAssignValue(raw) {
58
31
  .slice(lastColon + 1)
59
32
  .trim()
60
33
  .toLowerCase();
61
- if (KNOWN_ROLES.has(candidate)) {
34
+ if (Object.hasOwn(AGENT_TO_CLI, candidate)) {
62
35
  return { cli, prompt: rest.slice(0, lastColon).trim(), role: candidate };
63
36
  }
64
37
  }
@@ -72,7 +45,7 @@ export function parseTeamArgs(args = []) {
72
45
  let layout = "2x2";
73
46
  let teammateMode = "auto";
74
47
  const taskParts = [];
75
- const assigns = []; // --assign "codex:프롬프트:역할" 형식
48
+ const assigns = [];
76
49
  let autoAttach = true;
77
50
  let progressive = true;
78
51
  let timeoutSec = 0;
@@ -109,14 +82,10 @@ export function parseTeamArgs(args = []) {
109
82
  } else if (current === "--assign" && args[index + 1]) {
110
83
  const parsed = parseAssignValue(args[++index]);
111
84
  if (parsed) assigns.push(parsed);
112
- } else if (current === "--auto-attach") {
113
- autoAttach = true;
114
85
  } else if (current === "--no-auto-attach") {
115
86
  autoAttach = false;
116
87
  } else if (current === "--verbose") {
117
88
  verbose = true;
118
- } else if (current === "--dashboard") {
119
- dashboard = true;
120
89
  } else if (current === "--no-dashboard") {
121
90
  dashboard = false;
122
91
  } else if (current === "--dashboard-layout" && args[index + 1]) {
@@ -159,9 +128,9 @@ export function parseTeamArgs(args = []) {
159
128
  nativeBridgeUiOptOut = true;
160
129
  } else if (current === "--native-bridge-mode") {
161
130
  const mode = args[++index];
162
- if (!NATIVE_BRIDGE_MODES.has(mode)) {
131
+ if (mode !== "agents") {
163
132
  throw new Error(
164
- `unknown native bridge mode: ${mode || ""}; expected roster, agents, interactive-attach, or claude-wrapper`,
133
+ `unknown native bridge mode: ${mode || ""}; expected agents`,
165
134
  );
166
135
  }
167
136
  nativeBridge = true;
@@ -174,6 +143,8 @@ export function parseTeamArgs(args = []) {
174
143
  p = p[1].toUpperCase() + ":" + p.slice(2);
175
144
  }
176
145
  cwd = resolve(p);
146
+ } else if (current === "--auto-attach" || current === "--dashboard") {
147
+ // 이전 설치 스킬이 넘기는 기본값 플래그는 무시한다.
177
148
  } else if (current.startsWith("-")) {
178
149
  console.warn(` ⚠ 미인식 플래그 무시: ${current}`);
179
150
  } else {
@@ -181,7 +152,11 @@ export function parseTeamArgs(args = []) {
181
152
  }
182
153
  }
183
154
 
184
- const normalizedTeammateMode = normalizeTeammateMode(teammateMode);
155
+ const task = taskParts.join(" ").trim();
156
+ const normalizedTeammateMode =
157
+ task || assigns.length > 0
158
+ ? normalizeTeammateMode(teammateMode)
159
+ : teammateMode;
185
160
  if (
186
161
  !nativeBridgeExplicit &&
187
162
  !nativeBridgeUiOptOut &&
@@ -195,7 +170,7 @@ export function parseTeamArgs(args = []) {
195
170
  lead,
196
171
  layout: normalizeLayout(layout),
197
172
  teammateMode: normalizedTeammateMode,
198
- task: taskParts.join(" ").trim(),
173
+ task,
199
174
  assigns,
200
175
  autoAttach,
201
176
  progressive,