triflux 10.3.4 → 10.7.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 (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -1,11 +1,15 @@
1
1
  import { injectPrompt, sendKeys } from "../../pane.mjs";
2
2
  import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
+ import { ok, warn } from "../render.mjs";
3
4
  import { publishLeadControl } from "../services/hub-client.mjs";
4
5
  import { resolveMember } from "../services/member-selector.mjs";
5
6
  import { nativeRequest } from "../services/native-control.mjs";
6
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
7
+ import {
8
+ isNativeMode,
9
+ isTeamAlive,
10
+ isWtMode,
11
+ } from "../services/runtime-mode.mjs";
7
12
  import { loadTeamState } from "../services/state-store.mjs";
8
- import { ok, warn } from "../render.mjs";
9
13
 
10
14
  export async function teamControl(args = []) {
11
15
  const state = loadTeamState();
@@ -17,26 +21,43 @@ export async function teamControl(args = []) {
17
21
  const member = resolveMember(state, args[0]);
18
22
  const command = String(args[1] || "").toLowerCase();
19
23
  const reason = args.slice(2).join(" ");
20
- if (!member || !new Set(["interrupt", "stop", "pause", "resume"]).has(command)) {
21
- console.log(`\n 사용법: ${WHITE}tfx multi control <lead|이름|번호> <interrupt|stop|pause|resume> [사유]${RESET}\n`);
24
+ if (
25
+ !member ||
26
+ !new Set(["interrupt", "stop", "pause", "resume"]).has(command)
27
+ ) {
28
+ console.log(
29
+ `\n 사용법: ${WHITE}tfx multi control <lead|이름|번호> <interrupt|stop|pause|resume> [사유]${RESET}\n`,
30
+ );
22
31
  return;
23
32
  }
24
33
  if (isWtMode(state)) {
25
- console.log(`\n ${YELLOW}⚠${RESET} wt 모드는 Hub direct/control 주입 경로가 비활성입니다.\n ${DIM}수동 제어: 해당 pane에서 직접 명령/인터럽트를 수행하세요.${RESET}\n`);
34
+ console.log(
35
+ `\n ${YELLOW}⚠${RESET} wt 모드는 Hub direct/control 주입 경로가 비활성입니다.\n ${DIM}수동 제어: 해당 pane에서 직접 명령/인터럽트를 수행하세요.${RESET}\n`,
36
+ );
26
37
  return;
27
38
  }
28
39
 
29
40
  let directOk = false;
30
41
  if (isNativeMode(state)) {
31
- directOk = !!(await nativeRequest(state, "/control", { member: member.name, command, reason }))?.ok;
42
+ directOk = !!(
43
+ await nativeRequest(state, "/control", {
44
+ member: member.name,
45
+ command,
46
+ reason,
47
+ })
48
+ )?.ok;
32
49
  } else {
33
- injectPrompt(member.pane, `[LEAD CONTROL] command=${command}${reason ? ` reason=${reason}` : ""}`);
50
+ injectPrompt(
51
+ member.pane,
52
+ `[LEAD CONTROL] command=${command}${reason ? ` reason=${reason}` : ""}`,
53
+ );
34
54
  if (command === "interrupt") sendKeys(member.pane, "C-c");
35
55
  directOk = true;
36
56
  }
37
57
 
38
58
  const published = await publishLeadControl(state, member, command, reason);
39
- if (directOk && published) ok(`${member.name} 제어 전송 (${command}, direct + hub)`);
59
+ if (directOk && published)
60
+ ok(`${member.name} 제어 전송 (${command}, direct + hub)`);
40
61
  else if (directOk) ok(`${member.name} 제어 전송 (${command}, direct only)`);
41
62
  else warn(`${member.name} 제어 전송 실패 (${command})`);
42
63
  console.log("");
@@ -1,4 +1,3 @@
1
- import { AMBER, BOLD, DIM, RESET } from "../../shared.mjs";
2
1
  import {
3
2
  capturePaneOutput,
4
3
  detectMultiplexer,
@@ -7,21 +6,31 @@ import {
7
6
  hasWindowsTerminalSession,
8
7
  listSessions,
9
8
  } from "../../session.mjs";
9
+ import { AMBER, BOLD, DIM, RESET } from "../../shared.mjs";
10
+ import { formatCompletionSuffix } from "../render.mjs";
10
11
  import { getHubInfo, nativeGetStatus } from "../services/hub-client.mjs";
11
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
12
+ import {
13
+ isNativeMode,
14
+ isTeamAlive,
15
+ isWtMode,
16
+ } from "../services/runtime-mode.mjs";
12
17
  import { loadTeamState, TEAM_PROFILE } from "../services/state-store.mjs";
13
- import { formatCompletionSuffix } from "../render.mjs";
14
18
 
15
19
  export async function teamDebug(args = []) {
16
20
  const state = loadTeamState();
17
21
  const flagIndex = args.findIndex((arg) => arg === "--lines" || arg === "-n");
18
- const lines = flagIndex === -1 ? 20 : Math.max(3, parseInt(args[flagIndex + 1] || "20", 10) || 20);
22
+ const lines =
23
+ flagIndex === -1
24
+ ? 20
25
+ : Math.max(3, parseInt(args[flagIndex + 1] || "20", 10) || 20);
19
26
  const hub = await getHubInfo();
20
27
 
21
28
  console.log(`\n ${AMBER}${BOLD}⬡ Team Debug${RESET}\n`);
22
29
  console.log(` platform: ${process.platform}`);
23
30
  console.log(` node: ${process.version}`);
24
- console.log(` tty: stdout=${!!process.stdout.isTTY}, stdin=${!!process.stdin.isTTY}`);
31
+ console.log(
32
+ ` tty: stdout=${!!process.stdout.isTTY}, stdin=${!!process.stdin.isTTY}`,
33
+ );
25
34
  console.log(` mux: ${detectMultiplexer() || "none"}`);
26
35
  console.log(` hub-pid: ${hub ? `${hub.pid}` : "-"}`);
27
36
  console.log(` hub-url: ${hub?.url || "-"}`);
@@ -40,13 +49,17 @@ export async function teamDebug(args = []) {
40
49
  console.log(` lead: ${state.lead}`);
41
50
  console.log(` agents: ${(state.agents || []).join(", ")}`);
42
51
  console.log(` alive: ${isTeamAlive(state) ? "yes" : "no"}`);
43
- console.log(` attached: ${getSessionAttachedCount(state.sessionName) ?? "-"}`);
52
+ console.log(
53
+ ` attached: ${getSessionAttachedCount(state.sessionName) ?? "-"}`,
54
+ );
44
55
 
45
56
  if (isWtMode(state)) {
46
57
  console.log(`\n ${BOLD}wt-session${RESET}`);
47
58
  console.log(` window: ${state?.wt?.windowId ?? 0}`);
48
59
  console.log(` layout: ${state?.wt?.layout || state?.layout || "-"}`);
49
- console.log(` panes: ${state?.wt?.paneCount ?? (state.members || []).length}`);
60
+ console.log(
61
+ ` panes: ${state?.wt?.paneCount ?? (state.members || []).length}`,
62
+ );
50
63
  console.log(` wt.exe: ${hasWindowsTerminal() ? "yes" : "no"}`);
51
64
  console.log(` WT_SESSION:${hasWindowsTerminalSession() ? "yes" : "no"}`);
52
65
  console.log("");
@@ -57,16 +70,24 @@ export async function teamDebug(args = []) {
57
70
  console.log(`\n ${BOLD}native-members${RESET}`);
58
71
  const members = (await nativeGetStatus(state))?.data?.members || [];
59
72
  if (!members.length) console.log(` ${DIM}(no data)${RESET}`);
60
- for (const member of members) console.log(` - ${member.name}: ${member.status}${formatCompletionSuffix(member)}${member.lastPreview ? ` ${DIM}${member.lastPreview}${RESET}` : ""}`);
73
+ for (const member of members)
74
+ console.log(
75
+ ` - ${member.name}: ${member.status}${formatCompletionSuffix(member)}${member.lastPreview ? ` ${DIM}${member.lastPreview}${RESET}` : ""}`,
76
+ );
61
77
  console.log("");
62
78
  return;
63
79
  }
64
80
 
65
- console.log(`\n ${BOLD}pane-tail${RESET} ${DIM}(last ${lines} lines)${RESET}`);
66
- if (!(state.members || []).length) console.log(` ${DIM}(members 없음)${RESET}`);
81
+ console.log(
82
+ `\n ${BOLD}pane-tail${RESET} ${DIM}(last ${lines} lines)${RESET}`,
83
+ );
84
+ if (!(state.members || []).length)
85
+ console.log(` ${DIM}(members 없음)${RESET}`);
67
86
  for (const member of state.members || []) {
68
87
  console.log(`\n [${member.name}] ${member.pane}`);
69
- for (const line of (capturePaneOutput(member.pane, lines) || "(empty)").split("\n").slice(-lines)) {
88
+ for (const line of (capturePaneOutput(member.pane, lines) || "(empty)")
89
+ .split("\n")
90
+ .slice(-lines)) {
70
91
  console.log(` ${line}`);
71
92
  }
72
93
  }
@@ -1,10 +1,18 @@
1
1
  import { attachSession, focusPane, focusWtPane } from "../../session.mjs";
2
2
  import { DIM, RESET, WHITE } from "../../shared.mjs";
3
- import { buildManualAttachCommand, launchAttachInWindowsTerminal, wantsWtAttachFallback } from "../services/attach-fallback.mjs";
3
+ import { fail, ok, warn } from "../render.mjs";
4
+ import {
5
+ buildManualAttachCommand,
6
+ launchAttachInWindowsTerminal,
7
+ wantsWtAttachFallback,
8
+ } from "../services/attach-fallback.mjs";
4
9
  import { resolveMember } from "../services/member-selector.mjs";
5
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
10
+ import {
11
+ isNativeMode,
12
+ isTeamAlive,
13
+ isWtMode,
14
+ } from "../services/runtime-mode.mjs";
6
15
  import { loadTeamState } from "../services/state-store.mjs";
7
- import { fail, ok, warn } from "../render.mjs";
8
16
 
9
17
  export async function teamFocus(args = []) {
10
18
  const state = loadTeamState();
@@ -13,23 +21,34 @@ export async function teamFocus(args = []) {
13
21
  return;
14
22
  }
15
23
  if (isNativeMode(state)) {
16
- console.log(`\n ${DIM}in-process 모드는 focus/attach 개념이 없습니다.${RESET}\n ${DIM}직접 지시: tfx multi send <대상> "메시지"${RESET}\n`);
24
+ console.log(
25
+ `\n ${DIM}in-process 모드는 focus/attach 개념이 없습니다.${RESET}\n ${DIM}직접 지시: tfx multi send <대상> "메시지"${RESET}\n`,
26
+ );
17
27
  return;
18
28
  }
19
29
 
20
30
  const member = resolveMember(state, args[0]);
21
31
  if (!member) {
22
- console.log(`\n 사용법: ${WHITE}tfx multi focus <lead|이름|번호>${RESET}\n`);
32
+ console.log(
33
+ `\n 사용법: ${WHITE}tfx multi focus <lead|이름|번호>${RESET}\n`,
34
+ );
23
35
  return;
24
36
  }
25
37
 
26
38
  if (isWtMode(state)) {
27
39
  const paneIndex = Number(/^wt:(\d+)$/.exec(member.pane || "")?.[1]);
28
40
  if (!Number.isFinite(paneIndex)) {
29
- console.log(`\n ${DIM}wt pane 인덱스 파싱 실패: ${member.pane}${RESET}\n`);
41
+ console.log(
42
+ `\n ${DIM}wt pane 인덱스 파싱 실패: ${member.pane}${RESET}\n`,
43
+ );
30
44
  return;
31
45
  }
32
- if (focusWtPane(paneIndex, { layout: state?.wt?.layout || state?.layout || "1xN" })) ok(`${member.name} pane 포커스 이동 (wt)`);
46
+ if (
47
+ focusWtPane(paneIndex, {
48
+ layout: state?.wt?.layout || state?.layout || "1xN",
49
+ })
50
+ )
51
+ ok(`${member.name} pane 포커스 이동 (wt)`);
33
52
  else warn("wt pane 포커스 이동 실패 (WT_SESSION/wt.exe 상태 확인 필요)");
34
53
  console.log("");
35
54
  return;
@@ -40,14 +59,22 @@ export async function teamFocus(args = []) {
40
59
  attachSession(state.sessionName);
41
60
  } catch (error) {
42
61
  const allowWt = wantsWtAttachFallback(args);
43
- if (allowWt && await launchAttachInWindowsTerminal(state.sessionName)) {
62
+ if (allowWt && (await launchAttachInWindowsTerminal(state.sessionName))) {
44
63
  warn(`현재 터미널에서 attach 실패: ${error.message}`);
45
64
  ok("Windows Terminal split-pane로 attach 재시도 창을 열었습니다.");
46
- console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
65
+ console.log(
66
+ ` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`,
67
+ );
47
68
  return;
48
69
  }
49
70
  fail(`attach 실패: ${error.message}`);
50
- warn(allowWt ? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)" : "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.");
51
- console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
71
+ warn(
72
+ allowWt
73
+ ? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)"
74
+ : "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.",
75
+ );
76
+ console.log(
77
+ ` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`,
78
+ );
52
79
  }
53
80
  }
@@ -1,10 +1,14 @@
1
1
  import { sendKeys } from "../../pane.mjs";
2
2
  import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
+ import { ok, warn } from "../render.mjs";
3
4
  import { resolveMember } from "../services/member-selector.mjs";
4
5
  import { nativeRequest } from "../services/native-control.mjs";
5
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
6
+ import {
7
+ isNativeMode,
8
+ isTeamAlive,
9
+ isWtMode,
10
+ } from "../services/runtime-mode.mjs";
6
11
  import { loadTeamState } from "../services/state-store.mjs";
7
- import { ok, warn } from "../render.mjs";
8
12
 
9
13
  export async function teamInterrupt(args = []) {
10
14
  const state = loadTeamState();
@@ -15,17 +19,25 @@ export async function teamInterrupt(args = []) {
15
19
 
16
20
  const member = resolveMember(state, args[0] || "lead");
17
21
  if (!member) {
18
- console.log(`\n 사용법: ${WHITE}tfx multi interrupt <lead|이름|번호>${RESET}\n`);
22
+ console.log(
23
+ `\n 사용법: ${WHITE}tfx multi interrupt <lead|이름|번호>${RESET}\n`,
24
+ );
19
25
  return;
20
26
  }
21
27
  if (isWtMode(state)) {
22
- console.log(`\n ${YELLOW}⚠${RESET} wt 모드에서는 pane stdin 주입이 지원되지 않아 interrupt를 자동 전송할 수 없습니다.\n ${DIM}수동으로 해당 pane에서 Ctrl+C를 입력하세요.${RESET}\n`);
28
+ console.log(
29
+ `\n ${YELLOW}⚠${RESET} wt 모드에서는 pane stdin 주입이 지원되지 않아 interrupt를 자동 전송할 수 없습니다.\n ${DIM}수동으로 해당 pane에서 Ctrl+C를 입력하세요.${RESET}\n`,
30
+ );
23
31
  return;
24
32
  }
25
33
 
26
34
  if (isNativeMode(state)) {
27
- const result = await nativeRequest(state, "/interrupt", { member: member.name });
28
- (result?.ok ? ok : warn)(`${member.name} ${result?.ok ? "인터럽트 전송" : "인터럽트 실패"}`);
35
+ const result = await nativeRequest(state, "/interrupt", {
36
+ member: member.name,
37
+ });
38
+ (result?.ok ? ok : warn)(
39
+ `${member.name} ${result?.ok ? "인터럽트 전송" : "인터럽트 실패"}`,
40
+ );
29
41
  console.log("");
30
42
  return;
31
43
  }
@@ -1,24 +1,35 @@
1
1
  import { closeWtSession, killSession, listSessions } from "../../session.mjs";
2
2
  import { DIM, RESET } from "../../shared.mjs";
3
+ import { ok } from "../render.mjs";
3
4
  import { nativeRequest } from "../services/native-control.mjs";
4
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
5
+ import {
6
+ isNativeMode,
7
+ isTeamAlive,
8
+ isWtMode,
9
+ } from "../services/runtime-mode.mjs";
5
10
  import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
6
- import { ok } from "../render.mjs";
7
11
 
8
12
  export async function teamKill() {
9
13
  const state = loadTeamState();
10
14
  if (state && isNativeMode(state) && isTeamAlive(state)) {
11
15
  await nativeRequest(state, "/stop", {});
12
- try { process.kill(state.native.supervisorPid, "SIGTERM"); } catch {}
16
+ try {
17
+ process.kill(state.native.supervisorPid, "SIGTERM");
18
+ } catch {}
13
19
  clearTeamState(state.sessionId);
14
20
  ok(`종료: ${state.sessionName}`);
15
21
  console.log("");
16
22
  return;
17
23
  }
18
24
  if (state && isWtMode(state)) {
19
- const closed = closeWtSession({ layout: state?.wt?.layout || state?.layout || "1xN", paneCount: state?.wt?.paneCount ?? (state.members || []).length });
25
+ const closed = closeWtSession({
26
+ layout: state?.wt?.layout || state?.layout || "1xN",
27
+ paneCount: state?.wt?.paneCount ?? (state.members || []).length,
28
+ });
20
29
  clearTeamState(state.sessionId);
21
- ok(`종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`);
30
+ ok(
31
+ `종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`,
32
+ );
22
33
  console.log("");
23
34
  return;
24
35
  }
@@ -1,13 +1,19 @@
1
- import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
2
1
  import { listSessions } from "../../session.mjs";
3
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
2
+ import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
3
+ import {
4
+ isNativeMode,
5
+ isTeamAlive,
6
+ isWtMode,
7
+ } from "../services/runtime-mode.mjs";
4
8
  import { loadTeamState } from "../services/state-store.mjs";
5
9
 
6
10
  export function teamList() {
7
11
  const state = loadTeamState();
8
12
  if (state && isTeamAlive(state) && (isNativeMode(state) || isWtMode(state))) {
9
13
  console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
10
- console.log(` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`);
14
+ console.log(
15
+ ` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`,
16
+ );
11
17
  console.log("");
12
18
  return;
13
19
  }
@@ -19,6 +25,7 @@ export function teamList() {
19
25
  }
20
26
 
21
27
  console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
22
- for (const session of sessions) console.log(` ${GREEN}●${RESET} ${session}`);
28
+ for (const session of sessions)
29
+ console.log(` ${GREEN}●${RESET} ${session}`);
23
30
  console.log("");
24
31
  }
@@ -1,10 +1,14 @@
1
1
  import { injectPrompt } from "../../pane.mjs";
2
2
  import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
+ import { ok, warn } from "../render.mjs";
3
4
  import { resolveMember } from "../services/member-selector.mjs";
4
5
  import { nativeRequest } from "../services/native-control.mjs";
5
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
6
+ import {
7
+ isNativeMode,
8
+ isTeamAlive,
9
+ isWtMode,
10
+ } from "../services/runtime-mode.mjs";
6
11
  import { loadTeamState } from "../services/state-store.mjs";
7
- import { ok, warn } from "../render.mjs";
8
12
 
9
13
  export async function teamSend(args = []) {
10
14
  const state = loadTeamState();
@@ -16,17 +20,26 @@ export async function teamSend(args = []) {
16
20
  const member = resolveMember(state, args[0]);
17
21
  const message = args.slice(1).join(" ");
18
22
  if (!member || !message) {
19
- console.log(`\n 사용법: ${WHITE}tfx multi send <lead|이름|번호> "메시지"${RESET}\n`);
23
+ console.log(
24
+ `\n 사용법: ${WHITE}tfx multi send <lead|이름|번호> "메시지"${RESET}\n`,
25
+ );
20
26
  return;
21
27
  }
22
28
  if (isWtMode(state)) {
23
- console.log(`\n ${YELLOW}⚠${RESET} wt 모드는 pane 프롬프트 자동 주입(send)이 지원되지 않습니다.\n ${DIM}수동 전달: 선택한 pane에 직접 붙여넣으세요.${RESET}\n`);
29
+ console.log(
30
+ `\n ${YELLOW}⚠${RESET} wt 모드는 pane 프롬프트 자동 주입(send)이 지원되지 않습니다.\n ${DIM}수동 전달: 선택한 pane에 직접 붙여넣으세요.${RESET}\n`,
31
+ );
24
32
  return;
25
33
  }
26
34
 
27
35
  if (isNativeMode(state)) {
28
- const result = await nativeRequest(state, "/send", { member: member.name, text: message });
29
- (result?.ok ? ok : warn)(`${member.name}${result?.ok ? "에 메시지 주입 완료" : " 메시지 주입 실패"}`);
36
+ const result = await nativeRequest(state, "/send", {
37
+ member: member.name,
38
+ text: message,
39
+ });
40
+ (result?.ok ? ok : warn)(
41
+ `${member.name}${result?.ok ? "에 메시지 주입 완료" : " 메시지 주입 실패"}`,
42
+ );
30
43
  console.log("");
31
44
  return;
32
45
  }
@@ -1,31 +1,63 @@
1
1
  import { decomposeTask } from "../../../orchestrator.mjs";
2
- import { hasWindowsTerminal, hasWindowsTerminalSession } from "../../../session.mjs";
3
- import { AMBER, BOLD, DIM, GREEN, RED, RESET, WHITE } from "../../../shared.mjs";
4
- import { getDefaultHubUrl, getHubInfo, startHubDaemon } from "../../services/hub-client.mjs";
2
+ import {
3
+ hasWindowsTerminal,
4
+ hasWindowsTerminalSession,
5
+ } from "../../../session.mjs";
6
+ import {
7
+ AMBER,
8
+ BOLD,
9
+ DIM,
10
+ GREEN,
11
+ RED,
12
+ RESET,
13
+ WHITE,
14
+ } from "../../../shared.mjs";
15
+ import { fail, ok, warn } from "../../render.mjs";
16
+ import {
17
+ getDefaultHubUrl,
18
+ getHubInfo,
19
+ startHubDaemon,
20
+ } from "../../services/hub-client.mjs";
5
21
  import { ensureTmuxOrExit } from "../../services/runtime-mode.mjs";
6
22
  import { saveTeamState } from "../../services/state-store.mjs";
7
- import { fail, ok, warn } from "../../render.mjs";
8
23
  import { parseTeamArgs } from "./parse-args.mjs";
24
+ import { startHeadlessTeam } from "./start-headless.mjs";
9
25
  import { startInProcessTeam } from "./start-in-process.mjs";
10
26
  import { startMuxTeam } from "./start-mux.mjs";
11
- import { startHeadlessTeam } from "./start-headless.mjs";
12
27
  import { startWtTeam } from "./start-wt.mjs";
13
28
 
14
29
  function printStartUsage() {
15
30
  console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
16
31
  console.log(` 사용법: ${WHITE}tfx multi "작업 설명"${RESET}`);
17
- console.log(` ${WHITE}tfx multi --agents codex,gemini --lead claude "작업"${RESET}`);
18
- console.log(` ${WHITE}tfx multi --teammate-mode headless "작업"${RESET} ${DIM}(psmux 헤드리스, 기본)${RESET}`);
19
- console.log(` ${WHITE}tfx multi --dashboard-layout lite "작업"${RESET} ${DIM}(dashboard-lite 기본 뷰)${RESET}`);
20
- console.log(` ${WHITE}tfx multi --dashboard-layout auto "작업"${RESET} ${DIM}(dashboard viewer 레이아웃 자동)${RESET}`);
21
- console.log(` ${WHITE}tfx multi --dashboard-anchor window "작업"${RESET} ${DIM}(dashboard anchor: window|tab, 기본 window)${RESET}`);
22
- console.log(` ${WHITE}tfx multi --teammate-mode wt "작업"${RESET} ${DIM}(Windows Terminal split-pane)${RESET}`);
23
- console.log(` ${WHITE}tfx multi --teammate-mode in-process "작업"${RESET} ${DIM}(mux 불필요)${RESET}\n`);
32
+ console.log(
33
+ ` ${WHITE}tfx multi --agents codex,gemini --lead claude "작업"${RESET}`,
34
+ );
35
+ console.log(
36
+ ` ${WHITE}tfx multi --teammate-mode headless "작업"${RESET} ${DIM}(psmux 헤드리스, 기본)${RESET}`,
37
+ );
38
+ console.log(
39
+ ` ${WHITE}tfx multi --dashboard-layout lite "작업"${RESET} ${DIM}(dashboard-lite 기본 뷰)${RESET}`,
40
+ );
41
+ console.log(
42
+ ` ${WHITE}tfx multi --dashboard-layout auto "작업"${RESET} ${DIM}(dashboard viewer 레이아웃 자동)${RESET}`,
43
+ );
44
+ console.log(
45
+ ` ${WHITE}tfx multi --dashboard-anchor window "작업"${RESET} ${DIM}(dashboard anchor: window|tab, 기본 window)${RESET}`,
46
+ );
47
+ console.log(
48
+ ` ${WHITE}tfx multi --teammate-mode wt "작업"${RESET} ${DIM}(Windows Terminal split-pane)${RESET}`,
49
+ );
50
+ console.log(
51
+ ` ${WHITE}tfx multi --teammate-mode in-process "작업"${RESET} ${DIM}(mux 불필요)${RESET}\n`,
52
+ );
24
53
  }
25
54
 
26
55
  function printWorkerPreview(agents, subtasks) {
27
56
  for (let index = 0; index < subtasks.length; index += 1) {
28
- const preview = subtasks[index].length > 44 ? `${subtasks[index].slice(0, 44)}…` : subtasks[index];
57
+ const preview =
58
+ subtasks[index].length > 44
59
+ ? `${subtasks[index].slice(0, 44)}…`
60
+ : subtasks[index];
29
61
  console.log(` ${DIM}[${agents[index]}-${index + 1}] ${preview}${RESET}`);
30
62
  }
31
63
  console.log("");
@@ -34,16 +66,40 @@ function printWorkerPreview(agents, subtasks) {
34
66
  function renderTmuxInstallHelp() {
35
67
  console.log(`\n ${RED}${BOLD}tmux 미발견${RESET}\n`);
36
68
  console.log(" 현재 선택한 모드는 tmux 기반 팀세션이 필요합니다.\n");
37
- console.log(` 설치:\n WSL2: ${WHITE}wsl sudo apt install tmux${RESET}\n macOS: ${WHITE}brew install tmux${RESET}\n Linux: ${WHITE}apt install tmux${RESET}\n`);
38
- console.log(` Windows에서는 WSL2를 권장합니다:\n 1. ${WHITE}wsl --install${RESET}\n 2. ${WHITE}wsl sudo apt install tmux${RESET}\n 3. ${WHITE}tfx multi "작업"${RESET}\n`);
69
+ console.log(
70
+ ` 설치:\n WSL2: ${WHITE}wsl sudo apt install tmux${RESET}\n macOS: ${WHITE}brew install tmux${RESET}\n Linux: ${WHITE}apt install tmux${RESET}\n`,
71
+ );
72
+ console.log(
73
+ ` Windows에서는 WSL2를 권장합니다:\n 1. ${WHITE}wsl --install${RESET}\n 2. ${WHITE}wsl sudo apt install tmux${RESET}\n 3. ${WHITE}tfx multi "작업"${RESET}\n`,
74
+ );
39
75
  }
40
76
 
41
77
  export { parseTeamArgs };
42
78
 
43
79
  export async function teamStart(args = []) {
44
- const { agents, lead, layout, teammateMode, task: rawTask, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, dashboardLayout, dashboardSize, dashboardAnchor, mcpProfile, model, cwd } = parseTeamArgs(args);
80
+ const {
81
+ agents,
82
+ lead,
83
+ layout,
84
+ teammateMode,
85
+ task: rawTask,
86
+ assigns,
87
+ autoAttach,
88
+ progressive,
89
+ timeoutSec,
90
+ verbose,
91
+ dashboard,
92
+ dashboardLayout,
93
+ dashboardSize,
94
+ dashboardAnchor,
95
+ mcpProfile,
96
+ model,
97
+ cwd,
98
+ } = parseTeamArgs(args);
45
99
  // --assign 사용 시 task를 자동 생성
46
- const task = rawTask || (assigns.length > 0 ? assigns.map(a => a.prompt).join(" + ") : "");
100
+ const task =
101
+ rawTask ||
102
+ (assigns.length > 0 ? assigns.map((a) => a.prompt).join(" + ") : "");
47
103
  if (!task) return printStartUsage();
48
104
 
49
105
  console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
@@ -57,7 +113,12 @@ export async function teamStart(args = []) {
57
113
  hub = await getHubInfo();
58
114
  if (!hub) {
59
115
  process.stdout.write(" Hub 시작 중...");
60
- try { hub = await startHubDaemon(); } catch (error) { if (error?.code === "HUB_SERVER_MISSING") fail("hub/server.mjs 없음 — hub 모듈이 설치되지 않음"); }
116
+ try {
117
+ hub = await startHubDaemon();
118
+ } catch (error) {
119
+ if (error?.code === "HUB_SERVER_MISSING")
120
+ fail("hub/server.mjs 없음 — hub 모듈이 설치되지 않음");
121
+ }
61
122
  console.log(` ${hub ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`}`);
62
123
  if (!hub) warn("Hub 시작 실패 — 수동으로 실행: tfx hub start");
63
124
  } else ok(`Hub: ${DIM}${hub.url}${RESET}`);
@@ -69,26 +130,84 @@ export async function teamStart(args = []) {
69
130
  const subtasks = decomposeTask(task, agents.length);
70
131
  const hubUrl = hub?.url || getDefaultHubUrl();
71
132
  let effectiveMode = teammateMode;
72
- if (effectiveMode === "wt" && !hasWindowsTerminal()) { warn("wt.exe 미발견 — in-process 모드로 자동 fallback"); effectiveMode = "in-process"; }
73
- if (effectiveMode === "wt" && !hasWindowsTerminalSession()) { warn("WT_SESSION 미감지(Windows Terminal 외부) — in-process 모드로 자동 fallback"); effectiveMode = "in-process"; }
133
+ if (effectiveMode === "wt" && !hasWindowsTerminal()) {
134
+ warn("wt.exe 미발견 — in-process 모드로 자동 fallback");
135
+ effectiveMode = "in-process";
136
+ }
137
+ if (effectiveMode === "wt" && !hasWindowsTerminalSession()) {
138
+ warn(
139
+ "WT_SESSION 미감지(Windows Terminal 외부) — in-process 모드로 자동 fallback",
140
+ );
141
+ effectiveMode = "in-process";
142
+ }
74
143
 
75
144
  console.log(` 세션: ${WHITE}${sessionId}${RESET}`);
76
145
  console.log(` 모드: ${effectiveMode}`);
77
146
  console.log(` 리드: ${AMBER}${lead}${RESET}`);
78
- console.log(` 워커: ${agents.map((agent) => `${AMBER}${agent}${RESET}`).join(", ")}`);
147
+ console.log(
148
+ ` 워커: ${agents.map((agent) => `${AMBER}${agent}${RESET}`).join(", ")}`,
149
+ );
79
150
  printWorkerPreview(agents, subtasks);
80
151
 
81
152
  if (effectiveMode === "tmux") {
82
- try { ensureTmuxOrExit(); } catch { return renderTmuxInstallHelp(); }
153
+ try {
154
+ ensureTmuxOrExit();
155
+ } catch {
156
+ return renderTmuxInstallHelp();
157
+ }
83
158
  }
84
159
 
85
- const state = effectiveMode === "in-process"
86
- ? await startInProcessTeam({ sessionId, task, lead, agents, subtasks, hubUrl })
87
- : effectiveMode === "headless"
88
- ? await startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, dashboardLayout, dashboardSize, dashboardAnchor, mcpProfile, model, cwd })
89
- : effectiveMode === "wt"
90
- ? await startWtTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl })
91
- : await startMuxTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl, teammateMode: effectiveMode });
160
+ const state =
161
+ effectiveMode === "in-process"
162
+ ? await startInProcessTeam({
163
+ sessionId,
164
+ task,
165
+ lead,
166
+ agents,
167
+ subtasks,
168
+ hubUrl,
169
+ })
170
+ : effectiveMode === "headless"
171
+ ? await startHeadlessTeam({
172
+ sessionId,
173
+ task,
174
+ lead,
175
+ agents,
176
+ subtasks,
177
+ layout,
178
+ assigns,
179
+ autoAttach,
180
+ progressive,
181
+ timeoutSec,
182
+ verbose,
183
+ dashboard,
184
+ dashboardLayout,
185
+ dashboardSize,
186
+ dashboardAnchor,
187
+ mcpProfile,
188
+ model,
189
+ cwd,
190
+ })
191
+ : effectiveMode === "wt"
192
+ ? await startWtTeam({
193
+ sessionId,
194
+ task,
195
+ lead,
196
+ agents,
197
+ subtasks,
198
+ layout,
199
+ hubUrl,
200
+ })
201
+ : await startMuxTeam({
202
+ sessionId,
203
+ task,
204
+ lead,
205
+ agents,
206
+ subtasks,
207
+ layout,
208
+ hubUrl,
209
+ teammateMode: effectiveMode,
210
+ });
92
211
 
93
212
  if (!state) return fail("in-process supervisor 시작 실패");
94
213
  state.sessionId = sessionId;
@@ -100,7 +219,11 @@ export async function teamStart(args = []) {
100
219
  console.log(` ${DIM}제어: tfx multi send/control/tasks/status${RESET}\n`);
101
220
  } else if (effectiveMode === "wt") {
102
221
  ok("Windows Terminal wt 팀 시작 완료");
103
- console.log(` ${DIM}현재 pane 기준으로 ${state.layout} 분할 생성됨${RESET}`);
104
- console.log(` ${DIM}wt 모드는 자동 프롬프트 주입/Hub direct 제어(send/control)가 제한됩니다.${RESET}\n`);
222
+ console.log(
223
+ ` ${DIM}현재 pane 기준으로 ${state.layout} 분할 생성됨${RESET}`,
224
+ );
225
+ console.log(
226
+ ` ${DIM}wt 모드는 자동 프롬프트 주입/Hub direct 제어(send/control)가 제한됩니다.${RESET}\n`,
227
+ );
105
228
  }
106
229
  }