@triflux/remote 10.1.0 → 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 +61 -0
  3. package/hub/server.mjs +2165 -708
  4. package/hub/store-adapter.mjs +59 -26
  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 +125 -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 +44 -16
  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 +20 -118
  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 +1020 -279
  70. package/hub/team/recovery-store.mjs +149 -0
  71. package/hub/team/remote-probe.mjs +68 -52
  72. package/hub/team/remote-session.mjs +131 -59
  73. package/hub/team/remote-watcher.mjs +61 -33
  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 +1522 -193
  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 +18 -34
  97. package/hub/team/tui-synapse.mjs +223 -0
  98. package/hub/team/tui-viewer.mjs +104 -44
  99. package/hub/team/tui-widgets.mjs +262 -0
  100. package/hub/team/tui.mjs +927 -437
  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 +517 -63
  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 +662 -335
  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 { DIM, GREEN, RESET, AMBER } from "../../../shared.mjs";
2
- import { runHeadlessInteractive, resolveCliType } from "../../../headless.mjs";
3
- import { ok, } 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
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,17 +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
+ );
49
110
  // 머신 파싱용 완료 마커 — background task에서 결과 회수 시점 판단에 사용
50
- console.log(`=== HEADLESS_COMPLETE succeeded=${succeeded.length} failed=${failed.length} total=${results.length} ===`);
111
+ console.log(
112
+ `=== HEADLESS_COMPLETE succeeded=${succeeded.length} failed=${failed.length} total=${results.length} ===`,
113
+ );
51
114
 
52
115
  if (failed.length > 0) {
53
- 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
+ );
54
120
  }
55
121
 
56
122
  // handoff 요약 (Lead 토큰 절약 포맷)
57
123
  for (const r of results) {
58
- const icon = r.matched && r.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
124
+ const icon =
125
+ r.matched && r.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
59
126
  if (r.handoffFormatted) {
60
127
  const tag = r.handoffFallback ? `${DIM}(fallback)${RESET}` : "";
61
128
  console.log(` ${icon} ${r.paneName} ${tag}`);
@@ -71,8 +138,11 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
71
138
  if (verbose) {
72
139
  for (const r of results) {
73
140
  if (r.output) {
74
- const preview = r.output.length > 200 ? `${r.output.slice(0, 200)}…` : r.output;
75
- 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
+ );
76
146
  console.log(` ${preview}`);
77
147
  }
78
148
  }
@@ -80,28 +150,46 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
80
150
 
81
151
  // dashboard 모드: tui-viewer가 최종 상태를 렌더링할 시간 확보
82
152
  // WT pane spawn (~1s) + node 기동 (~500ms) + 첫 폴링 (~500ms) + 렌더 여유
83
- if (dashboard) await new Promise(r => setTimeout(r, 5000));
153
+ if (dashboard) await new Promise((r) => setTimeout(r, 5000));
84
154
 
85
155
  // 세션 정리
86
156
  handle.kill();
87
157
 
88
158
  const members = [
89
- { role: "lead", name: "lead", cli: lead, pane: `${handle.sessionName}:0.0` },
90
- ...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
+ })),
91
172
  ];
92
173
 
93
174
  return {
94
175
  sessionName: handle.sessionName,
95
176
  task,
96
177
  lead,
97
- agents: assignments.map(a => a.cli),
178
+ agents: assignments.map((a) => a.cli),
98
179
  layout,
99
180
  teammateMode: "headless",
100
181
  startedAt: Date.now(),
101
182
  members,
102
183
  headlessResults: results,
103
- handoffs: results.map((r) => ({ paneName: r.paneName, cli: r.cli, ...r.handoff })),
104
- 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
+ ),
105
193
  postSave() {
106
194
  // headless는 실행 완료 후 즉시 정리 — HUD에 잔존 방지
107
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
+ }