@triflux/remote 10.1.0 → 10.26.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 (162) hide show
  1. package/hub/pipe.mjs +239 -121
  2. package/hub/public/dashboard.html +61 -0
  3. package/hub/server.mjs +2179 -709
  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 +78 -17
  9. package/hub/team/build-worker-prompt.mjs +73 -0
  10. package/hub/team/check-mcp-hub.mjs +49 -0
  11. package/hub/team/claude-daemon-control.mjs +1130 -0
  12. package/hub/team/claude-native-bridge.mjs +1011 -0
  13. package/hub/team/claude-session-projection.mjs +74 -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 +151 -33
  23. package/hub/team/cli/commands/start/parse-args.mjs +97 -13
  24. package/hub/team/cli/commands/start/start-headless.mjs +155 -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/help.mjs +1 -1
  33. package/hub/team/cli/index.mjs +2 -2
  34. package/hub/team/cli/manifest.mjs +38 -8
  35. package/hub/team/cli/render.mjs +52 -30
  36. package/hub/team/cli/services/attach-fallback.mjs +74 -54
  37. package/hub/team/cli/services/hub-client.mjs +44 -16
  38. package/hub/team/cli/services/member-selector.mjs +37 -30
  39. package/hub/team/cli/services/native-control.mjs +144 -116
  40. package/hub/team/cli/services/runtime-mode.mjs +61 -4
  41. package/hub/team/cli/services/state-store.mjs +66 -9
  42. package/hub/team/cli/services/task-model.mjs +40 -30
  43. package/hub/team/codex-review.mjs +521 -0
  44. package/hub/team/conductor-mesh-bridge.mjs +25 -24
  45. package/hub/team/conductor-registry.mjs +121 -0
  46. package/hub/team/conductor.mjs +1379 -671
  47. package/hub/team/consensus-meta.mjs +176 -0
  48. package/hub/team/dashboard-anchor.mjs +4 -5
  49. package/hub/team/dashboard-layout.mjs +3 -1
  50. package/hub/team/dashboard-open.mjs +20 -118
  51. package/hub/team/dashboard.mjs +76 -28
  52. package/hub/team/event-log.mjs +18 -10
  53. package/hub/team/execution-mode.mjs +256 -0
  54. package/hub/team/extract-completion-payload.mjs +110 -0
  55. package/hub/team/git-preflight.mjs +286 -0
  56. package/hub/team/handoff.mjs +49 -18
  57. package/hub/team/headless-bridge-session.mjs +17 -0
  58. package/hub/team/headless.mjs +2213 -1156
  59. package/hub/team/health-probe.mjs +232 -69
  60. package/hub/team/interactive-native-launcher.mjs +106 -0
  61. package/hub/team/interactive-native-worker.mjs +80 -0
  62. package/hub/team/interactive-tui-transport.mjs +465 -0
  63. package/hub/team/launcher-template.mjs +49 -17
  64. package/hub/team/lead-control.mjs +91 -4
  65. package/hub/team/mcp-selector.mjs +145 -0
  66. package/hub/team/native-supervisor.mjs +90 -26
  67. package/hub/team/native.mjs +120 -42
  68. package/hub/team/nativeProxy.mjs +184 -113
  69. package/hub/team/notify.mjs +147 -78
  70. package/hub/team/orchestrator.mjs +173 -161
  71. package/hub/team/pane.mjs +43 -20
  72. package/hub/team/process-cleanup.mjs +69 -25
  73. package/hub/team/psmux.mjs +1022 -281
  74. package/hub/team/recovery-store.mjs +149 -0
  75. package/hub/team/remote-probe.mjs +68 -52
  76. package/hub/team/remote-session.mjs +131 -59
  77. package/hub/team/remote-watcher.mjs +61 -33
  78. package/hub/team/retry-state-machine.mjs +336 -0
  79. package/hub/team/routing.mjs +51 -25
  80. package/hub/team/runtime-strategy.mjs +200 -0
  81. package/hub/team/schemas/worker-completion.json +39 -0
  82. package/hub/team/sentinel-capture.mjs +127 -0
  83. package/hub/team/session-sync.mjs +153 -6
  84. package/hub/team/session.mjs +574 -611
  85. package/hub/team/shared.mjs +13 -13
  86. package/hub/team/staleState.mjs +72 -30
  87. package/hub/team/swarm-cli.mjs +390 -0
  88. package/hub/team/swarm-hypervisor.mjs +1544 -193
  89. package/hub/team/swarm-intent.mjs +216 -0
  90. package/hub/team/swarm-locks.mjs +140 -25
  91. package/hub/team/swarm-planner.mjs +121 -21
  92. package/hub/team/swarm-preflight.mjs +319 -0
  93. package/hub/team/swarm-reconciler.mjs +128 -23
  94. package/hub/team/synapse-cli.mjs +458 -0
  95. package/hub/team/synapse-http.mjs +60 -0
  96. package/hub/team/synapse-registry.mjs +329 -0
  97. package/hub/team/terminal-opener.mjs +216 -0
  98. package/hub/team/tui-core.mjs +292 -0
  99. package/hub/team/tui-lite.mjs +237 -148
  100. package/hub/team/tui-remote-adapter.mjs +18 -34
  101. package/hub/team/tui-synapse.mjs +223 -0
  102. package/hub/team/tui-viewer.mjs +108 -48
  103. package/hub/team/tui-widgets.mjs +262 -0
  104. package/hub/team/tui.mjs +931 -439
  105. package/hub/team/worker-completion-validator.mjs +84 -0
  106. package/hub/team/worker-sandbox.mjs +97 -0
  107. package/hub/team/worker-signal.mjs +263 -0
  108. package/hub/team/worker-signal.types.d.ts +52 -0
  109. package/hub/team/worktree-lifecycle.mjs +517 -63
  110. package/hub/team/wt-manager.mjs +297 -26
  111. package/hub/tools.mjs +477 -252
  112. package/hub/tray.mjs +411 -368
  113. package/hub/workers/antigravity-route-worker.mjs +169 -0
  114. package/hub/workers/claude-worker.mjs +162 -118
  115. package/hub/workers/codex-app-server-worker.mjs +1015 -0
  116. package/hub/workers/codex-mcp.mjs +724 -507
  117. package/hub/workers/delegator-mcp.mjs +570 -499
  118. package/hub/workers/factory.mjs +103 -21
  119. package/hub/workers/gemini-worker.mjs +126 -284
  120. package/hub/workers/interface.mjs +24 -2
  121. package/hub/workers/lib/jsonrpc-stdio.mjs +463 -0
  122. package/hub/workers/worker-utils.mjs +23 -17
  123. package/package.json +4 -3
  124. package/scripts/lib/agent-json.mjs +27 -0
  125. package/scripts/lib/async.mjs +174 -0
  126. package/scripts/lib/claudemd-scanner.mjs +223 -0
  127. package/scripts/lib/cli-agy.mjs +62 -0
  128. package/scripts/lib/cli-claude.mjs +78 -0
  129. package/scripts/lib/cli-codex.mjs +199 -0
  130. package/scripts/lib/cli-gemini.mjs +37 -0
  131. package/scripts/lib/codex-recovery.sh +50 -0
  132. package/scripts/lib/context.mjs +67 -0
  133. package/scripts/lib/cross-review-utils.mjs +59 -0
  134. package/scripts/lib/doctor-env-checks.mjs +121 -0
  135. package/scripts/lib/dynamic-route-cli.mjs +107 -0
  136. package/scripts/lib/env-probe.mjs +269 -0
  137. package/scripts/lib/env.mjs +14 -0
  138. package/scripts/lib/gemini-profiles.mjs +119 -0
  139. package/scripts/lib/handoff.mjs +187 -0
  140. package/scripts/lib/hook-utils.mjs +16 -0
  141. package/scripts/lib/hub.mjs +48 -0
  142. package/scripts/lib/keyword-rules.mjs +195 -0
  143. package/scripts/lib/logger.mjs +105 -0
  144. package/scripts/lib/mcp-filter.mjs +883 -0
  145. package/scripts/lib/mcp-gateway-health-check.mjs +159 -0
  146. package/scripts/lib/mcp-gateway-servers.mjs +70 -0
  147. package/scripts/lib/mcp-gateway-wrapper-check.mjs +35 -0
  148. package/scripts/lib/mcp-guard-engine.mjs +2120 -0
  149. package/scripts/lib/mcp-health.mjs +773 -0
  150. package/scripts/lib/mcp-manifest.mjs +89 -0
  151. package/scripts/lib/mcp-server-catalog.mjs +355 -0
  152. package/scripts/lib/pid.mjs +63 -0
  153. package/scripts/lib/process-utils.mjs +26 -0
  154. package/scripts/lib/psmux-info.mjs +224 -0
  155. package/scripts/lib/quota.mjs +47 -0
  156. package/scripts/lib/remote-spawn-transfer.mjs +226 -0
  157. package/scripts/lib/skill-state.mjs +220 -0
  158. package/scripts/lib/skill-template.mjs +375 -0
  159. package/scripts/lib/team.mjs +75 -0
  160. package/scripts/lib/timeout.mjs +66 -0
  161. package/scripts/lib/tmp.mjs +26 -0
  162. package/scripts/lib/toml.mjs +113 -0
@@ -1,43 +1,132 @@
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 {
2
+ normalizeHeadlessRole,
3
+ resolveCliType,
4
+ resolveHeadlessDisplayName,
5
+ runHeadlessInteractive,
6
+ } from "../../../headless.mjs";
7
+ import { AMBER, DIM, GREEN, RESET } from "../../../shared.mjs";
8
+ import { ok } from "../../render.mjs";
5
9
  import { clearTeamState } from "../../services/state-store.mjs";
10
+ import { buildTasks } from "../../services/task-model.mjs";
6
11
 
7
- export async function startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, dashboardLayout, dashboardSize, dashboardAnchor, mcpProfile, model }) {
12
+ export function buildHeadlessAssignments({
13
+ agents = [],
14
+ subtasks = [],
15
+ assigns = [],
16
+ mcpProfile,
17
+ model,
18
+ cwd,
19
+ } = {}) {
8
20
  // --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 }));
21
+ return assigns && assigns.length > 0
22
+ ? assigns.map((a, i) => {
23
+ const paneName = `worker-${i + 1}`;
24
+ return {
25
+ cli: resolveCliType(a.cli),
26
+ prompt: a.prompt,
27
+ displayName: resolveHeadlessDisplayName(a, paneName),
28
+ role: normalizeHeadlessRole(a.role),
29
+ mcp: mcpProfile,
30
+ model,
31
+ cwd,
32
+ };
33
+ })
34
+ : subtasks.map((subtask, i) => {
35
+ const paneName = `worker-${i + 1}`;
36
+ return {
37
+ cli: resolveCliType(agents[i] || agents[0]),
38
+ prompt: subtask,
39
+ displayName: paneName,
40
+ role: "",
41
+ mcp: mcpProfile,
42
+ model,
43
+ cwd,
44
+ };
45
+ });
46
+ }
47
+
48
+ export async function startHeadlessTeam({
49
+ sessionId,
50
+ task,
51
+ lead,
52
+ agents,
53
+ subtasks,
54
+ layout,
55
+ assigns,
56
+ autoAttach,
57
+ progressive,
58
+ timeoutSec,
59
+ verbose,
60
+ dashboard,
61
+ dashboardLayout,
62
+ dashboardSize,
63
+ dashboardAnchor,
64
+ mcpProfile,
65
+ model,
66
+ cwd,
67
+ nativeBridge,
68
+ nativeBridgeMode,
69
+ }) {
70
+ const assignments = buildHeadlessAssignments({
71
+ agents,
72
+ subtasks,
73
+ assigns,
74
+ mcpProfile,
75
+ model,
76
+ cwd,
77
+ });
12
78
 
13
79
  const _startedAt = Date.now();
14
80
  ok(`headless ${assignments.length}워커 시작`);
81
+ if (nativeBridge) {
82
+ process.stderr.write(
83
+ `[headless] Claude native bridge mode: ${nativeBridgeMode || "roster"}\n`,
84
+ );
85
+ }
15
86
 
16
87
  const handle = await runHeadlessInteractive(sessionId, assignments, {
17
- timeoutSec: timeoutSec || 300,
88
+ timeoutSec: timeoutSec || 900,
18
89
  layout,
19
90
  autoAttach: !!autoAttach,
20
91
  dashboard: !!dashboard,
21
92
  dashboardLayout,
22
- dashboardSize: dashboardSize ?? 0.50,
93
+ dashboardSize: dashboardSize ?? 0.5,
23
94
  dashboardAnchor,
24
95
  progressive: progressive !== false,
96
+ nativeBridge,
97
+ nativeBridgeMode,
25
98
  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,
99
+ onProgress: verbose
100
+ ? function onProgress(event) {
101
+ if (event.type === "session_created") {
102
+ console.log(` ${DIM}세션: ${event.sessionName}${RESET}`);
103
+ } else if (event.type === "worker_added") {
104
+ console.log(` ${DIM}[+] ${event.paneTitle}${RESET}`);
105
+ } else if (event.type === "dispatched") {
106
+ console.log(
107
+ ` ${DIM}[${event.paneName}] ${event.cli} dispatch${RESET}`,
108
+ );
109
+ } else if (event.type === "progress") {
110
+ const last =
111
+ (event.snapshot || "")
112
+ .split("\n")
113
+ .filter((l) => l.trim())
114
+ .pop() || "";
115
+ if (last)
116
+ console.log(
117
+ ` ${DIM}[${event.paneName}] ${last.slice(0, 60)}${RESET}`,
118
+ );
119
+ } else if (event.type === "completed") {
120
+ const icon =
121
+ event.matched && event.exitCode === 0
122
+ ? `${GREEN}✓${RESET}`
123
+ : `${AMBER}✗${RESET}`;
124
+ console.log(
125
+ ` ${icon} [${event.paneName}] ${event.cli} exit=${event.exitCode}${event.sessionDead ? " (dead)" : ""}`,
126
+ );
127
+ }
128
+ }
129
+ : undefined,
41
130
  });
42
131
 
43
132
  // 최소 결과 요약
@@ -45,17 +134,25 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
45
134
  const succeeded = results.filter((r) => r.matched && r.exitCode === 0);
46
135
  const failed = results.filter((r) => !r.matched || r.exitCode !== 0);
47
136
 
48
- ok(`헤드리스 완료: ${succeeded.length}성공 / ${failed.length}실패 / ${results.length}전체`);
137
+ ok(
138
+ `헤드리스 완료: ${succeeded.length}성공 / ${failed.length}실패 / ${results.length}전체`,
139
+ );
49
140
  // 머신 파싱용 완료 마커 — background task에서 결과 회수 시점 판단에 사용
50
- console.log(`=== HEADLESS_COMPLETE succeeded=${succeeded.length} failed=${failed.length} total=${results.length} ===`);
141
+ console.log(
142
+ `=== HEADLESS_COMPLETE succeeded=${succeeded.length} failed=${failed.length} total=${results.length} ===`,
143
+ );
51
144
 
52
145
  if (failed.length > 0) {
53
- for (const r of failed) console.log(` ${AMBER}✗${RESET} ${r.paneName} (${r.cli}) exit=${r.exitCode}`);
146
+ for (const r of failed)
147
+ console.log(
148
+ ` ${AMBER}✗${RESET} ${r.paneName} (${r.cli}) exit=${r.exitCode}`,
149
+ );
54
150
  }
55
151
 
56
152
  // handoff 요약 (Lead 토큰 절약 포맷)
57
153
  for (const r of results) {
58
- const icon = r.matched && r.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
154
+ const icon =
155
+ r.matched && r.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
59
156
  if (r.handoffFormatted) {
60
157
  const tag = r.handoffFallback ? `${DIM}(fallback)${RESET}` : "";
61
158
  console.log(` ${icon} ${r.paneName} ${tag}`);
@@ -71,8 +168,11 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
71
168
  if (verbose) {
72
169
  for (const r of results) {
73
170
  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}`);
171
+ const preview =
172
+ r.output.length > 200 ? `${r.output.slice(0, 200)}…` : r.output;
173
+ console.log(
174
+ `\n ${DIM}── ${r.paneName} (${r.cli}${r.role ? `, ${r.role}` : ""}) ──${RESET}`,
175
+ );
76
176
  console.log(` ${preview}`);
77
177
  }
78
178
  }
@@ -80,28 +180,46 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
80
180
 
81
181
  // dashboard 모드: tui-viewer가 최종 상태를 렌더링할 시간 확보
82
182
  // WT pane spawn (~1s) + node 기동 (~500ms) + 첫 폴링 (~500ms) + 렌더 여유
83
- if (dashboard) await new Promise(r => setTimeout(r, 5000));
183
+ if (dashboard) await new Promise((r) => setTimeout(r, 5000));
84
184
 
85
185
  // 세션 정리
86
186
  handle.kill();
87
187
 
88
188
  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 })),
189
+ {
190
+ role: "lead",
191
+ name: "lead",
192
+ cli: lead,
193
+ pane: `${handle.sessionName}:0.0`,
194
+ },
195
+ ...results.map((r, i) => ({
196
+ role: "worker",
197
+ name: r.paneName,
198
+ cli: r.cli,
199
+ pane: r.paneId || "",
200
+ subtask: assignments[i]?.prompt,
201
+ })),
91
202
  ];
92
203
 
93
204
  return {
94
205
  sessionName: handle.sessionName,
95
206
  task,
96
207
  lead,
97
- agents: assignments.map(a => a.cli),
208
+ agents: assignments.map((a) => a.cli),
98
209
  layout,
99
210
  teammateMode: "headless",
100
211
  startedAt: Date.now(),
101
212
  members,
102
213
  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")),
214
+ handoffs: results.map((r) => ({
215
+ paneName: r.paneName,
216
+ cli: r.cli,
217
+ ...r.handoff,
218
+ })),
219
+ tasks: buildTasks(
220
+ assignments.map((a) => a.prompt),
221
+ members.filter((m) => m.role === "worker"),
222
+ ),
105
223
  postSave() {
106
224
  // headless는 실행 완료 후 즉시 정리 — HUD에 잔존 방지
107
225
  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
+ }