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
@@ -9,9 +9,8 @@
9
9
 
10
10
  import { execFileSync } from "node:child_process";
11
11
  import { EventEmitter } from "node:events";
12
-
12
+ import { detectHostOs, shellQuoteForHost } from "../lib/ssh-command.mjs";
13
13
  import { detectInputWait, PROBE_LEVELS } from "./health-probe.mjs";
14
- import { shellQuoteForHost, detectHostOs } from "../lib/ssh-command.mjs";
15
14
 
16
15
  export const REMOTE_WATCHER_STATES = Object.freeze({
17
16
  WATCHING: "watching",
@@ -62,7 +61,10 @@ function freezeStatus(status) {
62
61
  Object.fromEntries(
63
62
  Object.entries(status.sessions || {})
64
63
  .sort(([left], [right]) => left.localeCompare(right))
65
- .map(([sessionName, record]) => [sessionName, freezeSessionRecord(record)]),
64
+ .map(([sessionName, record]) => [
65
+ sessionName,
66
+ freezeSessionRecord(record),
67
+ ]),
66
68
  ),
67
69
  );
68
70
 
@@ -117,7 +119,9 @@ function hasMeaningfulActivity(captured) {
117
119
  }
118
120
 
119
121
  function detectCompletion(captured) {
120
- const matches = Array.from(String(captured || "").matchAll(COMPLETION_TOKEN_RE));
122
+ const matches = Array.from(
123
+ String(captured || "").matchAll(COMPLETION_TOKEN_RE),
124
+ );
121
125
  const match = matches.at(-1);
122
126
  if (!match) {
123
127
  return {
@@ -128,7 +132,8 @@ function detectCompletion(captured) {
128
132
  };
129
133
  }
130
134
 
131
- const parsedExitCode = match[2] == null ? null : Number.parseInt(match[2], 10);
135
+ const parsedExitCode =
136
+ match[2] == null ? null : Number.parseInt(match[2], 10);
132
137
  return {
133
138
  detected: true,
134
139
  exitCode: Number.isFinite(parsedExitCode) ? parsedExitCode : null,
@@ -143,7 +148,10 @@ function parseSessionNames(rawOutput, sessionPrefix) {
143
148
  .map((line) => line.trim())
144
149
  .filter(Boolean)
145
150
  .map((line) => line.split(":")[0]?.trim())
146
- .filter((sessionName) => Boolean(sessionName) && sessionName.startsWith(sessionPrefix));
151
+ .filter(
152
+ (sessionName) =>
153
+ Boolean(sessionName) && sessionName.startsWith(sessionPrefix),
154
+ );
147
155
  }
148
156
 
149
157
  function buildExecOptions(config) {
@@ -187,7 +195,9 @@ function captureSpawnSession(sessionName, config) {
187
195
  );
188
196
  return {
189
197
  paneTarget,
190
- captured: String(output || "").replace(/\r/g, "").trimEnd(),
198
+ captured: String(output || "")
199
+ .replace(/\r/g, "")
200
+ .trimEnd(),
191
201
  };
192
202
  }
193
203
 
@@ -221,7 +231,10 @@ function createSessionRecord(sessionName, config, now) {
221
231
  }
222
232
 
223
233
  function isTerminalState(state) {
224
- return state === REMOTE_WATCHER_STATES.COMPLETED || state === REMOTE_WATCHER_STATES.FAILED;
234
+ return (
235
+ state === REMOTE_WATCHER_STATES.COMPLETED ||
236
+ state === REMOTE_WATCHER_STATES.FAILED
237
+ );
225
238
  }
226
239
 
227
240
  export function createRemoteWatcher(opts = {}) {
@@ -252,26 +265,30 @@ export function createRemoteWatcher(opts = {}) {
252
265
  }
253
266
 
254
267
  function emitSessionEvent(eventName, nextRecord, now) {
255
- emitter.emit(eventName, Object.freeze({
256
- exitCode: nextRecord.exitCode,
257
- host: nextRecord.host,
258
- inputWaitPattern: nextRecord.inputWaitPattern,
259
- output: nextRecord.lastOutput,
260
- paneTarget: nextRecord.paneTarget,
261
- probeLevel: nextRecord.lastProbeLevel,
262
- promptIdlePattern: nextRecord.promptIdlePattern,
263
- reason: nextRecord.reason,
264
- sessionName: nextRecord.sessionName,
265
- state: nextRecord.state,
266
- ts: now,
267
- }));
268
+ emitter.emit(
269
+ eventName,
270
+ Object.freeze({
271
+ exitCode: nextRecord.exitCode,
272
+ host: nextRecord.host,
273
+ inputWaitPattern: nextRecord.inputWaitPattern,
274
+ output: nextRecord.lastOutput,
275
+ paneTarget: nextRecord.paneTarget,
276
+ probeLevel: nextRecord.lastProbeLevel,
277
+ promptIdlePattern: nextRecord.promptIdlePattern,
278
+ reason: nextRecord.reason,
279
+ sessionName: nextRecord.sessionName,
280
+ state: nextRecord.state,
281
+ ts: now,
282
+ }),
283
+ );
268
284
  }
269
285
 
270
286
  function classifySession(previousRecord, captured, now) {
271
287
  const completion = detectCompletion(captured);
272
288
  const inputWait = detectInputWait(captured);
273
289
  const promptIdle = detectPromptIdle(captured);
274
- const hasActivity = previousRecord.hasActivity || hasMeaningfulActivity(captured);
290
+ const hasActivity =
291
+ previousRecord.hasActivity || hasMeaningfulActivity(captured);
275
292
 
276
293
  let nextState = REMOTE_WATCHER_STATES.WATCHING;
277
294
  let reason = "watching";
@@ -292,9 +309,9 @@ export function createRemoteWatcher(opts = {}) {
292
309
  eventName = "sessionCompleted";
293
310
  }
294
311
  } else if (
295
- promptIdle.detected
296
- && hasActivity
297
- && (!inputWait.detected || inputWait.pattern === BARE_INPUT_WAIT_PATTERN)
312
+ promptIdle.detected &&
313
+ hasActivity &&
314
+ (!inputWait.detected || inputWait.pattern === BARE_INPUT_WAIT_PATTERN)
298
315
  ) {
299
316
  nextState = REMOTE_WATCHER_STATES.COMPLETED;
300
317
  reason = "prompt_idle";
@@ -325,10 +342,10 @@ export function createRemoteWatcher(opts = {}) {
325
342
  };
326
343
 
327
344
  if (
328
- eventName
329
- && previousRecord.state === nextRecord.state
330
- && previousRecord.reason === nextRecord.reason
331
- && previousRecord.exitCode === nextRecord.exitCode
345
+ eventName &&
346
+ previousRecord.state === nextRecord.state &&
347
+ previousRecord.reason === nextRecord.reason &&
348
+ previousRecord.exitCode === nextRecord.exitCode
332
349
  ) {
333
350
  return { eventName: null, nextRecord };
334
351
  }
@@ -375,7 +392,10 @@ export function createRemoteWatcher(opts = {}) {
375
392
  : createSessionRecord(sessionName, config, now);
376
393
 
377
394
  try {
378
- const { paneTarget, captured } = captureSpawnSession(sessionName, config);
395
+ const { paneTarget, captured } = captureSpawnSession(
396
+ sessionName,
397
+ config,
398
+ );
379
399
  const { eventName, nextRecord } = classifySession(
380
400
  { ...previousRecord, paneTarget },
381
401
  captured,
@@ -396,7 +416,10 @@ export function createRemoteWatcher(opts = {}) {
396
416
  state: REMOTE_WATCHER_STATES.FAILED,
397
417
  };
398
418
  nextSessions[sessionName] = failedRecord;
399
- queuedEvents.push({ eventName: "sessionFailed", record: failedRecord });
419
+ queuedEvents.push({
420
+ eventName: "sessionFailed",
421
+ record: failedRecord,
422
+ });
400
423
  setStatus({
401
424
  ...status,
402
425
  lastError: toErrorRecord(error),
@@ -407,9 +430,14 @@ export function createRemoteWatcher(opts = {}) {
407
430
  }
408
431
  }
409
432
 
410
- for (const [sessionName, previousRecord] of Object.entries(status.sessions)) {
433
+ for (const [sessionName, previousRecord] of Object.entries(
434
+ status.sessions,
435
+ )) {
411
436
  if (activeSet.has(sessionName)) continue;
412
- const { eventName, nextRecord } = markMissingSession(previousRecord, now);
437
+ const { eventName, nextRecord } = markMissingSession(
438
+ previousRecord,
439
+ now,
440
+ );
413
441
  nextSessions[sessionName] = nextRecord;
414
442
  if (eventName) {
415
443
  queuedEvents.push({ eventName, record: nextRecord });
@@ -15,31 +15,55 @@
15
15
  export function resolveRoutingStrategy({ subtasks, graph_type, thorough }) {
16
16
  const N = subtasks.length;
17
17
  if (N === 0) {
18
- const dagContext = { dag_width: 0, levels: {}, edges: [], max_complexity: 'S', taskResults: {} };
19
- return { strategy: 'quick_single', reason: 'empty_subtasks', dag_width: 0, max_complexity: 'S', dagContext };
18
+ const dagContext = {
19
+ dag_width: 0,
20
+ levels: {},
21
+ edges: [],
22
+ max_complexity: "S",
23
+ taskResults: {},
24
+ };
25
+ return {
26
+ strategy: "quick_single",
27
+ reason: "empty_subtasks",
28
+ dag_width: 0,
29
+ max_complexity: "S",
30
+ dagContext,
31
+ };
20
32
  }
21
33
 
22
- const { width: dag_width, levels, edges } = computeDagInfo(subtasks, graph_type);
34
+ const {
35
+ width: dag_width,
36
+ levels,
37
+ edges,
38
+ } = computeDagInfo(subtasks, graph_type);
23
39
  const max_complexity = getMaxComplexity(subtasks);
24
- const dagContext = { dag_width, levels, edges, max_complexity, taskResults: {} };
25
- const isHighComplexity = ['L', 'XL'].includes(max_complexity);
40
+ const dagContext = {
41
+ dag_width,
42
+ levels,
43
+ edges,
44
+ max_complexity,
45
+ taskResults: {},
46
+ };
47
+ const isHighComplexity = ["L", "XL"].includes(max_complexity);
26
48
  const allSameAgent = new Set(subtasks.map((s) => s.agent)).size === 1;
27
- const allSmall = subtasks.every((s) => normalizeComplexity(s.complexity) === 'S');
49
+ const allSmall = subtasks.every(
50
+ (s) => normalizeComplexity(s.complexity) === "S",
51
+ );
28
52
 
29
53
  // N==1: 단일 νƒœμŠ€ν¬
30
54
  if (N === 1) {
31
55
  if (thorough || isHighComplexity) {
32
56
  return {
33
- strategy: 'thorough_single',
34
- reason: 'single_high_complexity',
57
+ strategy: "thorough_single",
58
+ reason: "single_high_complexity",
35
59
  dag_width,
36
60
  max_complexity,
37
61
  dagContext,
38
62
  };
39
63
  }
40
64
  return {
41
- strategy: 'quick_single',
42
- reason: 'single_low_complexity',
65
+ strategy: "quick_single",
66
+ reason: "single_low_complexity",
43
67
  dag_width,
44
68
  max_complexity,
45
69
  dagContext,
@@ -50,16 +74,16 @@ export function resolveRoutingStrategy({ subtasks, graph_type, thorough }) {
50
74
  if (dag_width === 1) {
51
75
  if (thorough || isHighComplexity) {
52
76
  return {
53
- strategy: 'thorough_single',
54
- reason: 'sequential_chain',
77
+ strategy: "thorough_single",
78
+ reason: "sequential_chain",
55
79
  dag_width,
56
80
  max_complexity,
57
81
  dagContext,
58
82
  };
59
83
  }
60
84
  return {
61
- strategy: 'quick_single',
62
- reason: 'sequential_chain',
85
+ strategy: "quick_single",
86
+ reason: "sequential_chain",
63
87
  dag_width,
64
88
  max_complexity,
65
89
  dagContext,
@@ -69,8 +93,8 @@ export function resolveRoutingStrategy({ subtasks, graph_type, thorough }) {
69
93
  // 동일 μ—μ΄μ „νŠΈ + λͺ¨λ‘ S: ν”„λ‘¬ν”„νŠΈ 병합 -> batch single
70
94
  if (allSameAgent && allSmall) {
71
95
  return {
72
- strategy: 'batch_single',
73
- reason: 'same_agent_small_batch',
96
+ strategy: "batch_single",
97
+ reason: "same_agent_small_batch",
74
98
  dag_width,
75
99
  max_complexity,
76
100
  dagContext,
@@ -80,16 +104,16 @@ export function resolveRoutingStrategy({ subtasks, graph_type, thorough }) {
80
104
  // dag_width >= 2: νŒ€
81
105
  if (thorough || isHighComplexity) {
82
106
  return {
83
- strategy: 'thorough_team',
84
- reason: 'parallel_high_complexity',
107
+ strategy: "thorough_team",
108
+ reason: "parallel_high_complexity",
85
109
  dag_width,
86
110
  max_complexity,
87
111
  dagContext,
88
112
  };
89
113
  }
90
114
  return {
91
- strategy: 'quick_team',
92
- reason: 'parallel_low_complexity',
115
+ strategy: "quick_team",
116
+ reason: "parallel_low_complexity",
93
117
  dag_width,
94
118
  max_complexity,
95
119
  dagContext,
@@ -103,7 +127,7 @@ export function resolveRoutingStrategy({ subtasks, graph_type, thorough }) {
103
127
  * @returns {{ width: number, levels: Record<number, string[]>, edges: Array<{from:string, to:string}> }}
104
128
  */
105
129
  function computeDagInfo(subtasks, graph_type) {
106
- if (graph_type === 'SEQUENTIAL') {
130
+ if (graph_type === "SEQUENTIAL") {
107
131
  const levels = {};
108
132
  const edges = [];
109
133
  subtasks.forEach((t, i) => {
@@ -112,7 +136,7 @@ function computeDagInfo(subtasks, graph_type) {
112
136
  });
113
137
  return { width: 1, levels, edges };
114
138
  }
115
- if (graph_type === 'INDEPENDENT') {
139
+ if (graph_type === "INDEPENDENT") {
116
140
  const levels = { 0: subtasks.map((t) => t.id) };
117
141
  return { width: subtasks.length, levels, edges: [] };
118
142
  }
@@ -173,7 +197,9 @@ function computeDagInfo(subtasks, graph_type) {
173
197
  export function getUpstreamResults(taskId, pipelineState) {
174
198
  const ctx = pipelineState?.dagContext;
175
199
  if (!ctx) return {};
176
- const upstreamIds = ctx.edges.filter((e) => e.to === taskId).map((e) => e.from);
200
+ const upstreamIds = ctx.edges
201
+ .filter((e) => e.to === taskId)
202
+ .map((e) => e.from);
177
203
  const results = {};
178
204
  for (const id of upstreamIds) {
179
205
  if (id in (ctx.taskResults || {})) {
@@ -205,7 +231,7 @@ export function updateTaskResult(taskId, result, pipelineState) {
205
231
  */
206
232
  function getMaxComplexity(subtasks) {
207
233
  const order = { S: 0, M: 1, L: 2, XL: 3 };
208
- let max = 'S';
234
+ let max = "S";
209
235
  for (const s of subtasks) {
210
236
  const complexity = normalizeComplexity(s.complexity);
211
237
  if (order[complexity] > order[max]) max = complexity;
@@ -219,5 +245,5 @@ function getMaxComplexity(subtasks) {
219
245
  * @returns {"S" | "M" | "L" | "XL"}
220
246
  */
221
247
  function normalizeComplexity(complexity) {
222
- return ['S', 'M', 'L', 'XL'].includes(complexity) ? complexity : 'M';
248
+ return ["S", "M", "L", "XL"].includes(complexity) ? complexity : "M";
223
249
  }
@@ -60,7 +60,9 @@ export function createPsmuxRuntime(adapter = defaultPsmuxAdapter) {
60
60
  * @returns {TeamRuntime & { name: string }}
61
61
  */
62
62
  export function createRuntime(mode) {
63
- const normalizedMode = String(mode || "").trim().toLowerCase();
63
+ const normalizedMode = String(mode || "")
64
+ .trim()
65
+ .toLowerCase();
64
66
 
65
67
  if (normalizedMode === "psmux") {
66
68
  return createPsmuxRuntime();
@@ -22,7 +22,11 @@ const GIT_BASH_CANDIDATES = [
22
22
  function findGitBashExe() {
23
23
  for (const p of GIT_BASH_CANDIDATES) {
24
24
  try {
25
- execSync(`"${p}" --version`, { stdio: "ignore", timeout: 3000, windowsHide: true });
25
+ execSync(`"${p}" --version`, {
26
+ stdio: "ignore",
27
+ timeout: 3000,
28
+ windowsHide: true,
29
+ });
26
30
  return p;
27
31
  } catch {
28
32
  // λ‹€μŒ 후보
@@ -35,7 +39,11 @@ function findGitBashExe() {
35
39
  export function hasWindowsTerminal() {
36
40
  if (process.platform !== "win32") return false;
37
41
  try {
38
- execSync("where wt.exe", { stdio: "ignore", timeout: 3000, windowsHide: true });
42
+ execSync("where wt.exe", {
43
+ stdio: "ignore",
44
+ timeout: 3000,
45
+ windowsHide: true,
46
+ });
39
47
  return true;
40
48
  } catch {
41
49
  return false;
@@ -60,7 +68,11 @@ function hasTmux() {
60
68
  /** WSL2 λ‚΄ tmux μ‚¬μš© κ°€λŠ₯ μ—¬λΆ€ (Windows μ „μš©) */
61
69
  function hasWslTmux() {
62
70
  try {
63
- execSync("wsl tmux -V", { stdio: "ignore", timeout: 5000, windowsHide: true });
71
+ execSync("wsl tmux -V", {
72
+ stdio: "ignore",
73
+ timeout: 5000,
74
+ windowsHide: true,
75
+ });
64
76
  return true;
65
77
  } catch {
66
78
  return false;
@@ -91,10 +103,22 @@ function hasGitBashTmux() {
91
103
  let _cachedMux;
92
104
  export function detectMultiplexer() {
93
105
  if (_cachedMux !== undefined) return _cachedMux;
94
- if (hasPsmux()) { _cachedMux = "psmux"; return _cachedMux; }
95
- if (hasTmux()) { _cachedMux = "tmux"; return _cachedMux; }
96
- if (process.platform === "win32" && hasGitBashTmux()) { _cachedMux = "git-bash-tmux"; return _cachedMux; }
97
- if (process.platform === "win32" && hasWslTmux()) { _cachedMux = "wsl-tmux"; return _cachedMux; }
106
+ if (hasPsmux()) {
107
+ _cachedMux = "psmux";
108
+ return _cachedMux;
109
+ }
110
+ if (hasTmux()) {
111
+ _cachedMux = "tmux";
112
+ return _cachedMux;
113
+ }
114
+ if (process.platform === "win32" && hasGitBashTmux()) {
115
+ _cachedMux = "git-bash-tmux";
116
+ return _cachedMux;
117
+ }
118
+ if (process.platform === "win32" && hasWslTmux()) {
119
+ _cachedMux = "wsl-tmux";
120
+ return _cachedMux;
121
+ }
98
122
  _cachedMux = null;
99
123
  return _cachedMux;
100
124
  }
@@ -110,15 +134,15 @@ function tmux(args, opts = {}) {
110
134
  if (!mux) {
111
135
  throw new Error(
112
136
  "tmux/psmux 미발견.\n\n" +
113
- "tfx multi은 tmux 계열 λ©€ν‹°ν”Œλ ‰μ„œκ°€ ν•„μš”ν•©λ‹ˆλ‹€:\n" +
114
- " Windows: psmux μ„€μΉ˜ λ˜λŠ” WSL2 tmux μ‚¬μš©\n" +
115
- " WSL2: wsl sudo apt install tmux\n" +
116
- " macOS: brew install tmux\n" +
117
- " Linux: apt install tmux\n\n" +
118
- "Windowsμ—μ„œλŠ” WSL2λ₯Ό ꢌμž₯ν•©λ‹ˆλ‹€:\n" +
119
- " 1. wsl --install\n" +
120
- " 2. wsl sudo apt install tmux\n" +
121
- " 3. tfx multi \"μž‘μ—…\" (μžλ™μœΌλ‘œ WSL tmux μ‚¬μš©)"
137
+ "tfx multi은 tmux 계열 λ©€ν‹°ν”Œλ ‰μ„œκ°€ ν•„μš”ν•©λ‹ˆλ‹€:\n" +
138
+ " Windows: psmux μ„€μΉ˜ λ˜λŠ” WSL2 tmux μ‚¬μš©\n" +
139
+ " WSL2: wsl sudo apt install tmux\n" +
140
+ " macOS: brew install tmux\n" +
141
+ " Linux: apt install tmux\n\n" +
142
+ "Windowsμ—μ„œλŠ” WSL2λ₯Ό ꢌμž₯ν•©λ‹ˆλ‹€:\n" +
143
+ " 1. wsl --install\n" +
144
+ " 2. wsl sudo apt install tmux\n" +
145
+ ' 3. tfx multi "μž‘μ—…" (μžλ™μœΌλ‘œ WSL tmux μ‚¬μš©)',
122
146
  );
123
147
  }
124
148
  if (mux === "psmux") {
@@ -272,7 +296,17 @@ export function createWtSession(sessionName, opts = {}) {
272
296
  const cwd = pane.cwd || process.cwd();
273
297
  if (!command) continue;
274
298
 
275
- wt(["-w", "0", "sp", splitFlag, "--title", title, "-d", cwd, ...buildWtCmdArgs(command)]);
299
+ wt([
300
+ "-w",
301
+ "0",
302
+ "sp",
303
+ splitFlag,
304
+ "--title",
305
+ title,
306
+ "-d",
307
+ cwd,
308
+ ...buildWtCmdArgs(command),
309
+ ]);
276
310
  panes.push(`wt:${i}`);
277
311
  titles.push(title);
278
312
  }
@@ -305,7 +339,11 @@ export function focusWtPane(paneIndex, opts = {}) {
305
339
 
306
340
  // μ•΅μ»€λ‘œ μ΅œλŒ€ν•œ 볡귀
307
341
  for (let i = 0; i < 10; i++) {
308
- try { wt(["-w", "0", "move-focus", backDir]); } catch { break; }
342
+ try {
343
+ wt(["-w", "0", "move-focus", backDir]);
344
+ } catch {
345
+ break;
346
+ }
309
347
  }
310
348
 
311
349
  for (let i = 0; i <= idx; i++) {
@@ -334,7 +372,11 @@ export function closeWtSession(opts = {}) {
334
372
 
335
373
  // 액컀(μ›λž˜ tfx μ‹€ν–‰ pane)둜 μ΅œλŒ€ν•œ 볡귀
336
374
  for (let i = 0; i < 10; i++) {
337
- try { wt(["-w", "0", "move-focus", backDir]); } catch { break; }
375
+ try {
376
+ wt(["-w", "0", "move-focus", backDir]);
377
+ } catch {
378
+ break;
379
+ }
338
380
  }
339
381
 
340
382
  for (let i = 0; i < paneCount; i++) {
@@ -424,7 +466,9 @@ export function focusPane(target, opts = {}) {
424
466
  const { zoom = false } = opts;
425
467
  tmux(`select-pane -t ${target}`);
426
468
  if (zoom) {
427
- try { tmux(`resize-pane -t ${target} -Z`); } catch {}
469
+ try {
470
+ tmux(`resize-pane -t ${target} -Z`);
471
+ } catch {}
428
472
  }
429
473
  }
430
474
 
@@ -461,29 +505,49 @@ export function configureTeammateKeybindings(sessionName, opts = {}) {
461
505
 
462
506
  if (inProcess) {
463
507
  // 단일 λ·°(zoom) μƒνƒœμ—μ„œ νŒ€λ©”μ΄νŠΈ μˆœν™˜
464
- tmux(`bind-key -T root -n S-Down if-shell -F '${cond}' ${bindNext} 'send-keys S-Down'`);
465
- tmux(`bind-key -T root -n S-Up if-shell -F '${cond}' ${bindPrev} 'send-keys S-Up'`);
508
+ tmux(
509
+ `bind-key -T root -n S-Down if-shell -F '${cond}' ${bindNext} 'send-keys S-Down'`,
510
+ );
511
+ tmux(
512
+ `bind-key -T root -n S-Up if-shell -F '${cond}' ${bindPrev} 'send-keys S-Up'`,
513
+ );
466
514
  } else {
467
515
  // λΆ„ν•  λ·°μ—μ„œ νŒ€λ©”μ΄νŠΈ μˆœν™˜
468
- tmux(`bind-key -T root -n S-Down if-shell -F '${cond}' ${bindNext} 'send-keys S-Down'`);
469
- tmux(`bind-key -T root -n S-Up if-shell -F '${cond}' ${bindPrev} 'send-keys S-Up'`);
516
+ tmux(
517
+ `bind-key -T root -n S-Down if-shell -F '${cond}' ${bindNext} 'send-keys S-Down'`,
518
+ );
519
+ tmux(
520
+ `bind-key -T root -n S-Up if-shell -F '${cond}' ${bindPrev} 'send-keys S-Up'`,
521
+ );
470
522
  }
471
523
 
472
524
  // λŒ€μ²΄ ν‚€: 일뢀 ν™˜κ²½μ—μ„œ S-Up이 λˆ„λ½λ  λ•Œ μ‚¬μš©
473
- tmux(`bind-key -T root -n S-Right if-shell -F '${cond}' ${bindNext} 'send-keys S-Right'`);
474
- tmux(`bind-key -T root -n S-Left if-shell -F '${cond}' ${bindPrev} 'send-keys S-Left'`);
475
- tmux(`bind-key -T root -n BTab if-shell -F '${cond}' ${bindPrev} 'send-keys BTab'`);
525
+ tmux(
526
+ `bind-key -T root -n S-Right if-shell -F '${cond}' ${bindNext} 'send-keys S-Right'`,
527
+ );
528
+ tmux(
529
+ `bind-key -T root -n S-Left if-shell -F '${cond}' ${bindPrev} 'send-keys S-Left'`,
530
+ );
531
+ tmux(
532
+ `bind-key -T root -n BTab if-shell -F '${cond}' ${bindPrev} 'send-keys BTab'`,
533
+ );
476
534
 
477
535
  // ν˜„μž¬ ν™œμ„± pane μΈν„°λŸ½νŠΈ
478
- tmux(`bind-key -T root -n Escape if-shell -F '${cond}' 'send-keys C-c' 'send-keys Escape'`);
536
+ tmux(
537
+ `bind-key -T root -n Escape if-shell -F '${cond}' 'send-keys C-c' 'send-keys Escape'`,
538
+ );
479
539
 
480
540
  // νƒœμŠ€ν¬ λͺ©λ‘ ν† κΈ€ (tmux 3.2+ popup μš°μ„ , μ‹€νŒ¨ μ‹œ μ•ˆλ‚΄ λ©”μ‹œμ§€)
481
541
  if (taskListCommand) {
482
542
  const escaped = taskListCommand.replace(/'/g, "'\\''");
483
543
  try {
484
- tmux(`bind-key -T root -n C-t if-shell -F '${cond}' "display-popup -E '${escaped}'" "send-keys C-t"`);
544
+ tmux(
545
+ `bind-key -T root -n C-t if-shell -F '${cond}' "display-popup -E '${escaped}'" "send-keys C-t"`,
546
+ );
485
547
  } catch {
486
- tmux(`bind-key -T root -n C-t if-shell -F '${cond}' 'display-message "tfx multi tasks λͺ…λ ΉμœΌλ‘œ νƒœμŠ€ν¬ 확인"' 'send-keys C-t'`);
548
+ tmux(
549
+ `bind-key -T root -n C-t if-shell -F '${cond}' 'display-message "tfx multi tasks λͺ…λ ΉμœΌλ‘œ νƒœμŠ€ν¬ 확인"' 'send-keys C-t'`,
550
+ );
487
551
  }
488
552
  }
489
553
  }
@@ -558,9 +622,7 @@ export function listSessions() {
558
622
 
559
623
  try {
560
624
  const output = tmux('list-sessions -F "#{session_name}"');
561
- return output
562
- .split("\n")
563
- .filter((s) => s.startsWith("tfx-multi-"));
625
+ return output.split("\n").filter((s) => s.startsWith("tfx-multi-"));
564
626
  } catch {
565
627
  return [];
566
628
  }
@@ -577,7 +639,9 @@ export function getSessionAttachedCount(sessionName) {
577
639
  }
578
640
 
579
641
  try {
580
- const output = tmux('list-sessions -F "#{session_name} #{session_attached}"');
642
+ const output = tmux(
643
+ 'list-sessions -F "#{session_name} #{session_attached}"',
644
+ );
581
645
  const line = output
582
646
  .split("\n")
583
647
  .find((l) => l.startsWith(`${sessionName} `));