@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,43 +1,102 @@
1
- import { BOLD, DIM, GREEN, RESET, AMBER } from "../../../shared.mjs";
2
- import { runHeadlessInteractive, resolveCliType } from "../../../headless.mjs";
3
- import { ok, warn } from "../../render.mjs";
4
- import { buildTasks } from "../../services/task-model.mjs";
1
+ import { resolveCliType, runHeadlessInteractive } from "../../../headless.mjs";
2
+ import { AMBER, DIM, GREEN, RESET } from "../../../shared.mjs";
3
+ import { ok } from "../../render.mjs";
5
4
  import { clearTeamState } from "../../services/state-store.mjs";
5
+ import { buildTasks } from "../../services/task-model.mjs";
6
6
 
7
- export async function startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, dashboardLayout, dashboardSize, dashboardAnchor, mcpProfile, model }) {
7
+ export async function startHeadlessTeam({
8
+ sessionId,
9
+ task,
10
+ lead,
11
+ agents,
12
+ subtasks,
13
+ layout,
14
+ assigns,
15
+ autoAttach,
16
+ progressive,
17
+ timeoutSec,
18
+ verbose,
19
+ dashboard,
20
+ dashboardLayout,
21
+ dashboardSize,
22
+ dashboardAnchor,
23
+ mcpProfile,
24
+ model,
25
+ cwd,
26
+ nativeBridge,
27
+ nativeBridgeMode,
28
+ }) {
8
29
  // --assign이 있으면 그것을 사용, 없으면 agents+subtasks 조합
9
- const assignments = assigns && assigns.length > 0
10
- ? assigns.map((a, i) => ({ cli: resolveCliType(a.cli), prompt: a.prompt, role: a.role || `worker-${i + 1}`, mcp: mcpProfile, model }))
11
- : subtasks.map((subtask, i) => ({ cli: resolveCliType(agents[i] || agents[0]), prompt: subtask, role: `worker-${i + 1}`, mcp: mcpProfile, model }));
30
+ const assignments =
31
+ assigns && assigns.length > 0
32
+ ? assigns.map((a, i) => ({
33
+ cli: resolveCliType(a.cli),
34
+ prompt: a.prompt,
35
+ role: a.role || `worker-${i + 1}`,
36
+ mcp: mcpProfile,
37
+ model,
38
+ cwd,
39
+ }))
40
+ : subtasks.map((subtask, i) => ({
41
+ cli: resolveCliType(agents[i] || agents[0]),
42
+ prompt: subtask,
43
+ role: `worker-${i + 1}`,
44
+ mcp: mcpProfile,
45
+ model,
46
+ cwd,
47
+ }));
12
48
 
13
- const startedAt = Date.now();
49
+ const _startedAt = Date.now();
14
50
  ok(`headless ${assignments.length}워커 시작`);
51
+ if (nativeBridge) {
52
+ process.stderr.write(
53
+ `[headless] Claude native bridge mode: ${nativeBridgeMode || "roster"}\n`,
54
+ );
55
+ }
15
56
 
16
57
  const handle = await runHeadlessInteractive(sessionId, assignments, {
17
- timeoutSec: timeoutSec || 300,
58
+ timeoutSec: timeoutSec || 900,
18
59
  layout,
19
60
  autoAttach: !!autoAttach,
20
61
  dashboard: !!dashboard,
21
62
  dashboardLayout,
22
- dashboardSize: dashboardSize ?? 0.50,
63
+ dashboardSize: dashboardSize ?? 0.5,
23
64
  dashboardAnchor,
24
65
  progressive: progressive !== false,
66
+ nativeBridge,
67
+ nativeBridgeMode,
25
68
  progressIntervalSec: verbose ? 10 : 0,
26
- onProgress: verbose ? function onProgress(event) {
27
- if (event.type === "session_created") {
28
- console.log(` ${DIM}세션: ${event.sessionName}${RESET}`);
29
- } else if (event.type === "worker_added") {
30
- console.log(` ${DIM}[+] ${event.paneTitle}${RESET}`);
31
- } else if (event.type === "dispatched") {
32
- console.log(` ${DIM}[${event.paneName}] ${event.cli} dispatch${RESET}`);
33
- } else if (event.type === "progress") {
34
- const last = (event.snapshot || "").split("\n").filter(l => l.trim()).pop() || "";
35
- if (last) console.log(` ${DIM}[${event.paneName}] ${last.slice(0, 60)}${RESET}`);
36
- } else if (event.type === "completed") {
37
- const icon = event.matched && event.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
38
- console.log(` ${icon} [${event.paneName}] ${event.cli} exit=${event.exitCode}${event.sessionDead ? " (dead)" : ""}`);
39
- }
40
- } : undefined,
69
+ onProgress: verbose
70
+ ? function onProgress(event) {
71
+ if (event.type === "session_created") {
72
+ console.log(` ${DIM}세션: ${event.sessionName}${RESET}`);
73
+ } else if (event.type === "worker_added") {
74
+ console.log(` ${DIM}[+] ${event.paneTitle}${RESET}`);
75
+ } else if (event.type === "dispatched") {
76
+ console.log(
77
+ ` ${DIM}[${event.paneName}] ${event.cli} dispatch${RESET}`,
78
+ );
79
+ } else if (event.type === "progress") {
80
+ const last =
81
+ (event.snapshot || "")
82
+ .split("\n")
83
+ .filter((l) => l.trim())
84
+ .pop() || "";
85
+ if (last)
86
+ console.log(
87
+ ` ${DIM}[${event.paneName}] ${last.slice(0, 60)}${RESET}`,
88
+ );
89
+ } else if (event.type === "completed") {
90
+ const icon =
91
+ event.matched && event.exitCode === 0
92
+ ? `${GREEN}✓${RESET}`
93
+ : `${AMBER}✗${RESET}`;
94
+ console.log(
95
+ ` ${icon} [${event.paneName}] ${event.cli} exit=${event.exitCode}${event.sessionDead ? " (dead)" : ""}`,
96
+ );
97
+ }
98
+ }
99
+ : undefined,
41
100
  });
42
101
 
43
102
  // 최소 결과 요약
@@ -45,15 +104,25 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
45
104
  const succeeded = results.filter((r) => r.matched && r.exitCode === 0);
46
105
  const failed = results.filter((r) => !r.matched || r.exitCode !== 0);
47
106
 
48
- ok(`헤드리스 완료: ${succeeded.length}성공 / ${failed.length}실패 / ${results.length}전체`);
107
+ ok(
108
+ `헤드리스 완료: ${succeeded.length}성공 / ${failed.length}실패 / ${results.length}전체`,
109
+ );
110
+ // 머신 파싱용 완료 마커 — background task에서 결과 회수 시점 판단에 사용
111
+ console.log(
112
+ `=== HEADLESS_COMPLETE succeeded=${succeeded.length} failed=${failed.length} total=${results.length} ===`,
113
+ );
49
114
 
50
115
  if (failed.length > 0) {
51
- for (const r of failed) console.log(` ${AMBER}✗${RESET} ${r.paneName} (${r.cli}) exit=${r.exitCode}`);
116
+ for (const r of failed)
117
+ console.log(
118
+ ` ${AMBER}✗${RESET} ${r.paneName} (${r.cli}) exit=${r.exitCode}`,
119
+ );
52
120
  }
53
121
 
54
122
  // handoff 요약 (Lead 토큰 절약 포맷)
55
123
  for (const r of results) {
56
- const icon = r.matched && r.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
124
+ const icon =
125
+ r.matched && r.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
57
126
  if (r.handoffFormatted) {
58
127
  const tag = r.handoffFallback ? `${DIM}(fallback)${RESET}` : "";
59
128
  console.log(` ${icon} ${r.paneName} ${tag}`);
@@ -69,8 +138,11 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
69
138
  if (verbose) {
70
139
  for (const r of results) {
71
140
  if (r.output) {
72
- const preview = r.output.length > 200 ? `${r.output.slice(0, 200)}…` : r.output;
73
- console.log(`\n ${DIM}── ${r.paneName} (${r.cli}${r.role ? `, ${r.role}` : ""}) ──${RESET}`);
141
+ const preview =
142
+ r.output.length > 200 ? `${r.output.slice(0, 200)}…` : r.output;
143
+ console.log(
144
+ `\n ${DIM}── ${r.paneName} (${r.cli}${r.role ? `, ${r.role}` : ""}) ──${RESET}`,
145
+ );
74
146
  console.log(` ${preview}`);
75
147
  }
76
148
  }
@@ -78,28 +150,46 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
78
150
 
79
151
  // dashboard 모드: tui-viewer가 최종 상태를 렌더링할 시간 확보
80
152
  // WT pane spawn (~1s) + node 기동 (~500ms) + 첫 폴링 (~500ms) + 렌더 여유
81
- if (dashboard) await new Promise(r => setTimeout(r, 5000));
153
+ if (dashboard) await new Promise((r) => setTimeout(r, 5000));
82
154
 
83
155
  // 세션 정리
84
156
  handle.kill();
85
157
 
86
158
  const members = [
87
- { role: "lead", name: "lead", cli: lead, pane: `${handle.sessionName}:0.0` },
88
- ...results.map((r, i) => ({ role: "worker", name: r.paneName, cli: r.cli, pane: r.paneId || "", subtask: assignments[i]?.prompt })),
159
+ {
160
+ role: "lead",
161
+ name: "lead",
162
+ cli: lead,
163
+ pane: `${handle.sessionName}:0.0`,
164
+ },
165
+ ...results.map((r, i) => ({
166
+ role: "worker",
167
+ name: r.paneName,
168
+ cli: r.cli,
169
+ pane: r.paneId || "",
170
+ subtask: assignments[i]?.prompt,
171
+ })),
89
172
  ];
90
173
 
91
174
  return {
92
175
  sessionName: handle.sessionName,
93
176
  task,
94
177
  lead,
95
- agents: assignments.map(a => a.cli),
178
+ agents: assignments.map((a) => a.cli),
96
179
  layout,
97
180
  teammateMode: "headless",
98
181
  startedAt: Date.now(),
99
182
  members,
100
183
  headlessResults: results,
101
- handoffs: results.map((r) => ({ paneName: r.paneName, cli: r.cli, ...r.handoff })),
102
- tasks: buildTasks(assignments.map(a => a.prompt), members.filter((m) => m.role === "worker")),
184
+ handoffs: results.map((r) => ({
185
+ paneName: r.paneName,
186
+ cli: r.cli,
187
+ ...r.handoff,
188
+ })),
189
+ tasks: buildTasks(
190
+ assignments.map((a) => a.prompt),
191
+ members.filter((m) => m.role === "worker"),
192
+ ),
103
193
  postSave() {
104
194
  // headless는 실행 완료 후 즉시 정리 — HUD에 잔존 방지
105
195
  clearTeamState(sessionId);
@@ -1,40 +1,50 @@
1
- import { startNativeSupervisor } from "../../services/native-control.mjs";
2
- import { buildTasks } from "../../services/task-model.mjs";
3
-
4
- export async function startInProcessTeam({ sessionId, task, lead, agents, subtasks, hubUrl }) {
5
- const { runtime, members } = await startNativeSupervisor({
6
- sessionId,
7
- task,
8
- lead,
9
- agents,
10
- subtasks,
11
- hubUrl,
12
- });
13
-
14
- if (!runtime?.controlUrl) return null;
15
-
16
- return {
17
- sessionName: sessionId,
18
- task,
19
- lead,
20
- agents,
21
- layout: "native",
22
- teammateMode: "in-process",
23
- startedAt: Date.now(),
24
- hubUrl,
25
- members: members.map((member, index) => ({
26
- role: member.role,
27
- name: member.name,
28
- cli: member.cli,
29
- agentId: member.agentId,
30
- pane: `native:${index}`,
31
- subtask: member.subtask || null,
32
- })),
33
- panes: {},
34
- tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
35
- native: {
36
- controlUrl: runtime.controlUrl,
37
- supervisorPid: runtime.supervisorPid,
38
- },
39
- };
40
- }
1
+ import { startNativeSupervisor } from "../../services/native-control.mjs";
2
+ import { buildTasks } from "../../services/task-model.mjs";
3
+
4
+ export async function startInProcessTeam({
5
+ sessionId,
6
+ task,
7
+ lead,
8
+ agents,
9
+ subtasks,
10
+ hubUrl,
11
+ }) {
12
+ const { runtime, members } = await startNativeSupervisor({
13
+ sessionId,
14
+ task,
15
+ lead,
16
+ agents,
17
+ subtasks,
18
+ hubUrl,
19
+ });
20
+
21
+ if (!runtime?.controlUrl) return null;
22
+
23
+ return {
24
+ sessionName: sessionId,
25
+ task,
26
+ lead,
27
+ agents,
28
+ layout: "native",
29
+ teammateMode: "in-process",
30
+ startedAt: Date.now(),
31
+ hubUrl,
32
+ members: members.map((member, index) => ({
33
+ role: member.role,
34
+ name: member.name,
35
+ cli: member.cli,
36
+ agentId: member.agentId,
37
+ pane: `native:${index}`,
38
+ subtask: member.subtask || null,
39
+ })),
40
+ panes: {},
41
+ tasks: buildTasks(
42
+ subtasks,
43
+ members.filter((member) => member.role === "worker"),
44
+ ),
45
+ native: {
46
+ controlUrl: runtime.controlUrl,
47
+ supervisorPid: runtime.supervisorPid,
48
+ },
49
+ };
50
+ }
@@ -1,73 +1,122 @@
1
- import { join } from "node:path";
2
-
3
- import { buildCliCommand, startCliInPane } from "../../../pane.mjs";
4
- import { orchestrate } from "../../../orchestrator.mjs";
5
- import { attachSession, configureTeammateKeybindings, createSession } from "../../../session.mjs";
6
- import { BOLD, DIM, GREEN, RESET } from "../../../shared.mjs";
7
- import { toAgentId } from "../../services/member-selector.mjs";
8
- import { PKG_ROOT, TEAM_PROFILE } from "../../services/state-store.mjs";
9
- import { buildTasks } from "../../services/task-model.mjs";
10
- import { ok, warn } from "../../render.mjs";
11
-
12
- export async function startMuxTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl, teammateMode }) {
13
- const paneCount = agents.length + 1;
14
- const effectiveLayout = paneCount <= 4 ? layout : (layout === "Nx1" ? "Nx1" : "1xN");
15
- console.log(` 레이아웃: ${effectiveLayout} (${paneCount} panes)`);
16
-
17
- const session = createSession(sessionId, { layout: effectiveLayout, paneCount });
18
- const leadTarget = session.panes[0];
19
- startCliInPane(leadTarget, buildCliCommand(lead));
20
-
21
- const members = [{ role: "lead", name: "lead", cli: lead, pane: leadTarget, agentId: toAgentId(lead, leadTarget) }];
22
- const assignments = [];
23
- for (let index = 0; index < agents.length; index += 1) {
24
- const cli = agents[index];
25
- const pane = session.panes[index + 1];
26
- startCliInPane(pane, buildCliCommand(cli));
27
- const worker = { role: "worker", name: `${cli}-${index + 1}`, cli, pane, subtask: subtasks[index], agentId: toAgentId(cli, pane) };
28
- members.push(worker);
29
- assignments.push({ target: pane, cli, subtask: subtasks[index] });
30
- }
31
-
32
- ok("CLI 초기화 대기 (3초)...");
33
- await new Promise((resolve) => setTimeout(resolve, 3000));
34
- await orchestrate(sessionId, assignments, { hubUrl, teammateMode, lead: { target: leadTarget, cli: lead, task } });
35
- ok("리드/워커 프롬프트 주입 완료");
36
-
37
- return {
38
- sessionName: sessionId,
39
- task,
40
- lead,
41
- agents,
42
- layout: effectiveLayout,
43
- teammateMode,
44
- startedAt: Date.now(),
45
- hubUrl,
46
- members,
47
- panes: Object.fromEntries(members.map((member) => [member.pane, {
48
- role: member.role,
49
- name: member.name,
50
- cli: member.cli,
51
- agentId: member.agentId,
52
- subtask: member.subtask || null,
53
- }])),
54
- tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
55
- postSave() {
56
- const profilePrefix = TEAM_PROFILE === "team" ? "" : `TFX_TEAM_PROFILE=${TEAM_PROFILE} `;
57
- const taskListCommand = `${profilePrefix}${process.execPath} ${join(PKG_ROOT, "bin", "triflux.mjs")} team tasks`;
58
- configureTeammateKeybindings(sessionId, { inProcess: false, taskListCommand });
59
- console.log(`\n ${GREEN}${BOLD}팀 세션 준비 완료${RESET}`);
60
- console.log(` ${DIM}Shift+Down: 다음 팀메이트 전환${RESET}`);
61
- console.log(` ${DIM}Shift+Tab / Shift+Left: 이전 팀메이트 전환${RESET}`);
62
- console.log(` ${DIM}Escape: 현재 팀메이트 인터럽트${RESET}`);
63
- console.log(` ${DIM}Ctrl+T: 태스크 목록${RESET}`);
64
- console.log(` ${DIM}참고: Shift+Up은 Claude Code 미지원 (scroll-up 충돌). Shift+Tab 사용${RESET}`);
65
- console.log(` ${DIM}Ctrl+B → D: 세션 분리 (백그라운드)${RESET}\n`);
66
- if (process.stdout.isTTY && process.stdin.isTTY) attachSession(sessionId);
67
- else {
68
- warn("TTY 미지원 환경이라 자동 attach를 생략함");
69
- console.log(` ${DIM}수동 연결: tfx multi attach${RESET}\n`);
70
- }
71
- },
72
- };
73
- }
1
+ import { join } from "node:path";
2
+ import { orchestrate } from "../../../orchestrator.mjs";
3
+ import { buildCliCommand, startCliInPane } from "../../../pane.mjs";
4
+ import {
5
+ attachSession,
6
+ configureTeammateKeybindings,
7
+ createSession,
8
+ } from "../../../session.mjs";
9
+ import { BOLD, DIM, GREEN, RESET } from "../../../shared.mjs";
10
+ import { ok, warn } from "../../render.mjs";
11
+ import { toAgentId } from "../../services/member-selector.mjs";
12
+ import { PKG_ROOT, TEAM_PROFILE } from "../../services/state-store.mjs";
13
+ import { buildTasks } from "../../services/task-model.mjs";
14
+
15
+ export async function startMuxTeam({
16
+ sessionId,
17
+ task,
18
+ lead,
19
+ agents,
20
+ subtasks,
21
+ layout,
22
+ hubUrl,
23
+ teammateMode,
24
+ }) {
25
+ const paneCount = agents.length + 1;
26
+ const effectiveLayout =
27
+ paneCount <= 4 ? layout : layout === "Nx1" ? "Nx1" : "1xN";
28
+ console.log(` 레이아웃: ${effectiveLayout} (${paneCount} panes)`);
29
+
30
+ const session = createSession(sessionId, {
31
+ layout: effectiveLayout,
32
+ paneCount,
33
+ });
34
+ const leadTarget = session.panes[0];
35
+ startCliInPane(leadTarget, buildCliCommand(lead));
36
+
37
+ const members = [
38
+ {
39
+ role: "lead",
40
+ name: "lead",
41
+ cli: lead,
42
+ pane: leadTarget,
43
+ agentId: toAgentId(lead, leadTarget),
44
+ },
45
+ ];
46
+ const assignments = [];
47
+ for (let index = 0; index < agents.length; index += 1) {
48
+ const cli = agents[index];
49
+ const pane = session.panes[index + 1];
50
+ startCliInPane(pane, buildCliCommand(cli));
51
+ const worker = {
52
+ role: "worker",
53
+ name: `${cli}-${index + 1}`,
54
+ cli,
55
+ pane,
56
+ subtask: subtasks[index],
57
+ agentId: toAgentId(cli, pane),
58
+ };
59
+ members.push(worker);
60
+ assignments.push({ target: pane, cli, subtask: subtasks[index] });
61
+ }
62
+
63
+ ok("CLI 초기화 대기 (3초)...");
64
+ await new Promise((resolve) => setTimeout(resolve, 3000));
65
+ await orchestrate(sessionId, assignments, {
66
+ hubUrl,
67
+ teammateMode,
68
+ lead: { target: leadTarget, cli: lead, task },
69
+ });
70
+ ok("리드/워커 프롬프트 주입 완료");
71
+
72
+ return {
73
+ sessionName: sessionId,
74
+ task,
75
+ lead,
76
+ agents,
77
+ layout: effectiveLayout,
78
+ teammateMode,
79
+ startedAt: Date.now(),
80
+ hubUrl,
81
+ members,
82
+ panes: Object.fromEntries(
83
+ members.map((member) => [
84
+ member.pane,
85
+ {
86
+ role: member.role,
87
+ name: member.name,
88
+ cli: member.cli,
89
+ agentId: member.agentId,
90
+ subtask: member.subtask || null,
91
+ },
92
+ ]),
93
+ ),
94
+ tasks: buildTasks(
95
+ subtasks,
96
+ members.filter((member) => member.role === "worker"),
97
+ ),
98
+ postSave() {
99
+ const profilePrefix =
100
+ TEAM_PROFILE === "team" ? "" : `TFX_TEAM_PROFILE=${TEAM_PROFILE} `;
101
+ const taskListCommand = `${profilePrefix}${process.execPath} ${join(PKG_ROOT, "bin", "triflux.mjs")} team tasks`;
102
+ configureTeammateKeybindings(sessionId, {
103
+ inProcess: false,
104
+ taskListCommand,
105
+ });
106
+ console.log(`\n ${GREEN}${BOLD}팀 세션 준비 완료${RESET}`);
107
+ console.log(` ${DIM}Shift+Down: 다음 팀메이트 전환${RESET}`);
108
+ console.log(` ${DIM}Shift+Tab / Shift+Left: 이전 팀메이트 전환${RESET}`);
109
+ console.log(` ${DIM}Escape: 현재 팀메이트 인터럽트${RESET}`);
110
+ console.log(` ${DIM}Ctrl+T: 태스크 목록${RESET}`);
111
+ console.log(
112
+ ` ${DIM}참고: Shift+Up은 Claude Code 미지원 (scroll-up 충돌). Shift+Tab 사용${RESET}`,
113
+ );
114
+ console.log(` ${DIM}Ctrl+B → D: 세션 분리 (백그라운드)${RESET}\n`);
115
+ if (process.stdout.isTTY && process.stdin.isTTY) attachSession(sessionId);
116
+ else {
117
+ warn("TTY 미지원 환경이라 자동 attach를 생략함");
118
+ console.log(` ${DIM}수동 연결: tfx multi attach${RESET}\n`);
119
+ }
120
+ },
121
+ };
122
+ }