triflux 10.0.0 → 10.0.2

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 (426) hide show
  1. package/CLAUDE.md +171 -0
  2. package/README.md +32 -15
  3. package/bin/triflux.mjs +62 -5
  4. package/hooks/agent-route-guard.mjs +109 -0
  5. package/hooks/cross-review-tracker.mjs +122 -0
  6. package/hooks/error-context.mjs +148 -0
  7. package/hooks/hook-adaptive-collector.mjs +86 -0
  8. package/hooks/hook-manager.mjs +365 -0
  9. package/hooks/hook-orchestrator.mjs +312 -0
  10. package/hooks/hook-registry.json +246 -0
  11. package/hooks/hooks.json +89 -0
  12. package/hooks/keyword-rules.json +574 -0
  13. package/hooks/lib/resolve-root.mjs +59 -0
  14. package/hooks/mcp-config-watcher.mjs +80 -0
  15. package/hooks/pipeline-stop.mjs +76 -0
  16. package/hooks/safety-guard.mjs +169 -0
  17. package/hooks/subagent-verifier.mjs +80 -0
  18. package/hub/account-broker.mjs +251 -0
  19. package/hub/adaptive-diagnostic.mjs +323 -0
  20. package/hub/adaptive-inject.mjs +186 -0
  21. package/hub/adaptive-memory.mjs +163 -0
  22. package/hub/adaptive.mjs +143 -0
  23. package/hub/assign-callbacks.mjs +133 -0
  24. package/hub/bridge.mjs +799 -0
  25. package/hub/cli-adapter-base.mjs +280 -0
  26. package/hub/codex-adapter.mjs +199 -0
  27. package/hub/codex-compat.mjs +11 -0
  28. package/hub/codex-preflight.mjs +166 -0
  29. package/hub/delegator/contracts.mjs +37 -0
  30. package/hub/delegator/index.mjs +14 -0
  31. package/hub/delegator/schema/delegator-tools.schema.json +250 -0
  32. package/hub/delegator/service.mjs +307 -0
  33. package/hub/delegator/tool-definitions.mjs +35 -0
  34. package/hub/fullcycle.mjs +96 -0
  35. package/hub/gemini-adapter.mjs +180 -0
  36. package/hub/hitl.mjs +143 -0
  37. package/hub/intent.mjs +193 -0
  38. package/hub/lib/cache-guard.mjs +114 -0
  39. package/hub/lib/known-errors.json +72 -0
  40. package/hub/lib/memory-store.mjs +748 -0
  41. package/hub/lib/process-utils.mjs +361 -0
  42. package/hub/lib/ssh-command.mjs +211 -0
  43. package/hub/lib/ssh-retry.mjs +59 -0
  44. package/hub/lib/uuidv7.mjs +44 -0
  45. package/hub/memory-doctor.mjs +480 -0
  46. package/hub/middleware/request-logger.mjs +161 -0
  47. package/hub/paths.mjs +30 -0
  48. package/hub/pipe.mjs +664 -0
  49. package/hub/pipeline/gates/confidence.mjs +56 -0
  50. package/hub/pipeline/gates/consensus.mjs +94 -0
  51. package/hub/pipeline/gates/index.mjs +5 -0
  52. package/hub/pipeline/gates/selfcheck.mjs +82 -0
  53. package/hub/pipeline/index.mjs +318 -0
  54. package/hub/pipeline/state.mjs +191 -0
  55. package/hub/pipeline/transitions.mjs +124 -0
  56. package/hub/platform.mjs +225 -0
  57. package/hub/public/dashboard.html +355 -0
  58. package/hub/public/tray-icon.ico +0 -0
  59. package/hub/public/tray-icon.png +0 -0
  60. package/hub/quality/deslop.mjs +253 -0
  61. package/hub/reflexion.mjs +372 -0
  62. package/hub/research.mjs +146 -0
  63. package/hub/router.mjs +791 -0
  64. package/hub/routing/complexity.mjs +166 -0
  65. package/hub/routing/index.mjs +117 -0
  66. package/hub/routing/q-learning.mjs +336 -0
  67. package/hub/schema.sql +148 -0
  68. package/hub/server.mjs +1264 -0
  69. package/hub/session-fingerprint.mjs +352 -0
  70. package/hub/state.mjs +258 -0
  71. package/hub/store-adapter.mjs +118 -0
  72. package/hub/store.mjs +857 -0
  73. package/hub/team/agent-map.json +11 -0
  74. package/hub/team/ansi.mjs +379 -0
  75. package/hub/team/backend.mjs +90 -0
  76. package/hub/team/cli/commands/attach.mjs +37 -0
  77. package/hub/team/cli/commands/control.mjs +43 -0
  78. package/hub/team/cli/commands/debug.mjs +74 -0
  79. package/hub/team/cli/commands/focus.mjs +53 -0
  80. package/hub/team/cli/commands/interrupt.mjs +36 -0
  81. package/hub/team/cli/commands/kill.mjs +37 -0
  82. package/hub/team/cli/commands/list.mjs +24 -0
  83. package/hub/team/cli/commands/send.mjs +37 -0
  84. package/hub/team/cli/commands/start/index.mjs +106 -0
  85. package/hub/team/cli/commands/start/parse-args.mjs +130 -0
  86. package/hub/team/cli/commands/start/start-headless.mjs +109 -0
  87. package/hub/team/cli/commands/start/start-in-process.mjs +40 -0
  88. package/hub/team/cli/commands/start/start-mux.mjs +73 -0
  89. package/hub/team/cli/commands/start/start-wt.mjs +69 -0
  90. package/hub/team/cli/commands/status.mjs +87 -0
  91. package/hub/team/cli/commands/stop.mjs +31 -0
  92. package/hub/team/cli/commands/task.mjs +30 -0
  93. package/hub/team/cli/commands/tasks.mjs +13 -0
  94. package/hub/team/cli/help.mjs +42 -0
  95. package/hub/team/cli/index.mjs +41 -0
  96. package/hub/team/cli/manifest.mjs +29 -0
  97. package/hub/team/cli/render.mjs +30 -0
  98. package/hub/team/cli/services/attach-fallback.mjs +54 -0
  99. package/hub/team/cli/services/hub-client.mjs +227 -0
  100. package/hub/team/cli/services/member-selector.mjs +30 -0
  101. package/hub/team/cli/services/native-control.mjs +117 -0
  102. package/hub/team/cli/services/runtime-mode.mjs +62 -0
  103. package/hub/team/cli/services/state-store.mjs +48 -0
  104. package/hub/team/cli/services/task-model.mjs +30 -0
  105. package/hub/team/conductor-mesh-bridge.mjs +121 -0
  106. package/hub/team/conductor.mjs +671 -0
  107. package/hub/team/dashboard-anchor.mjs +14 -0
  108. package/hub/team/dashboard-layout.mjs +33 -0
  109. package/hub/team/dashboard-open.mjs +153 -0
  110. package/hub/team/dashboard.mjs +274 -0
  111. package/hub/team/event-log.mjs +76 -0
  112. package/hub/team/handoff.mjs +303 -0
  113. package/hub/team/headless.mjs +1156 -0
  114. package/hub/team/health-probe.mjs +272 -0
  115. package/hub/team/launcher-template.mjs +95 -0
  116. package/hub/team/lead-control.mjs +104 -0
  117. package/hub/team/native-supervisor.mjs +392 -0
  118. package/hub/team/native.mjs +649 -0
  119. package/hub/team/nativeProxy.mjs +688 -0
  120. package/hub/team/notify.mjs +293 -0
  121. package/hub/team/orchestrator.mjs +161 -0
  122. package/hub/team/pane.mjs +153 -0
  123. package/hub/team/process-cleanup.mjs +342 -0
  124. package/hub/team/psmux.mjs +1354 -0
  125. package/hub/team/remote-probe.mjs +276 -0
  126. package/hub/team/remote-session.mjs +299 -0
  127. package/hub/team/remote-watcher.mjs +478 -0
  128. package/hub/team/routing.mjs +223 -0
  129. package/hub/team/session-sync.mjs +169 -0
  130. package/hub/team/session.mjs +611 -0
  131. package/hub/team/shared.mjs +13 -0
  132. package/hub/team/staleState.mjs +361 -0
  133. package/hub/team/swarm-hypervisor.mjs +589 -0
  134. package/hub/team/swarm-locks.mjs +204 -0
  135. package/hub/team/swarm-planner.mjs +260 -0
  136. package/hub/team/swarm-reconciler.mjs +137 -0
  137. package/hub/team/tui-lite.mjs +380 -0
  138. package/hub/team/tui-remote-adapter.mjs +393 -0
  139. package/hub/team/tui-viewer.mjs +463 -0
  140. package/hub/team/tui.mjs +1449 -0
  141. package/hub/team/worktree-lifecycle.mjs +193 -0
  142. package/hub/team/wt-manager.mjs +407 -0
  143. package/hub/team/wt-templates.json +43 -0
  144. package/hub/team-bridge.mjs +27 -0
  145. package/hub/token-mode.mjs +224 -0
  146. package/hub/tools.mjs +636 -0
  147. package/hub/tray.mjs +376 -0
  148. package/hub/workers/claude-worker.mjs +475 -0
  149. package/hub/workers/codex-mcp.mjs +507 -0
  150. package/hub/workers/delegator-mcp.mjs +1076 -0
  151. package/hub/workers/factory.mjs +21 -0
  152. package/hub/workers/gemini-worker.mjs +374 -0
  153. package/hub/workers/interface.mjs +52 -0
  154. package/hub/workers/worker-utils.mjs +104 -0
  155. package/hud/colors.mjs +88 -0
  156. package/hud/constants.mjs +88 -0
  157. package/hud/context-monitor.mjs +403 -0
  158. package/hud/hud-qos-status.mjs +210 -0
  159. package/hud/providers/claude.mjs +314 -0
  160. package/hud/providers/codex.mjs +151 -0
  161. package/hud/providers/gemini.mjs +320 -0
  162. package/hud/renderers.mjs +442 -0
  163. package/hud/terminal.mjs +140 -0
  164. package/hud/utils.mjs +313 -0
  165. package/mesh/index.mjs +63 -0
  166. package/mesh/mesh-budget.mjs +128 -0
  167. package/mesh/mesh-heartbeat.mjs +100 -0
  168. package/mesh/mesh-protocol.mjs +96 -0
  169. package/mesh/mesh-queue.mjs +165 -0
  170. package/mesh/mesh-registry.mjs +78 -0
  171. package/mesh/mesh-router.mjs +76 -0
  172. package/package.json +8 -1
  173. package/references/hosts.json +33 -0
  174. package/scripts/__tests__/gen-skill-docs.test.mjs +87 -0
  175. package/scripts/__tests__/keyword-detector.test.mjs +234 -0
  176. package/scripts/__tests__/mcp-guard-engine.test.mjs +118 -0
  177. package/scripts/__tests__/remote-spawn-transfer.test.mjs +117 -0
  178. package/scripts/__tests__/remote-spawn.test.mjs +92 -0
  179. package/scripts/__tests__/skill-template.test.mjs +193 -0
  180. package/scripts/__tests__/smoke.test.mjs +34 -0
  181. package/scripts/cache-buildup.mjs +30 -0
  182. package/scripts/cache-doctor.mjs +149 -0
  183. package/scripts/cache-warmup.mjs +557 -0
  184. package/scripts/claudemd-sync.mjs +148 -0
  185. package/scripts/cli-route.sh +3 -0
  186. package/scripts/completions/tfx.bash +47 -0
  187. package/scripts/completions/tfx.fish +44 -0
  188. package/scripts/completions/tfx.zsh +83 -0
  189. package/scripts/cross-review-gate.mjs +126 -0
  190. package/scripts/cross-review-tracker.mjs +238 -0
  191. package/scripts/gen-skill-docs.mjs +111 -0
  192. package/scripts/headless-guard-fast.sh +21 -0
  193. package/scripts/headless-guard.mjs +360 -0
  194. package/scripts/hub-ensure.mjs +120 -0
  195. package/scripts/keyword-detector.mjs +272 -0
  196. package/scripts/keyword-rules-expander.mjs +521 -0
  197. package/scripts/lib/claudemd-scanner.mjs +218 -0
  198. package/scripts/lib/context.mjs +67 -0
  199. package/scripts/lib/cross-review-utils.mjs +51 -0
  200. package/scripts/lib/env-probe.mjs +241 -0
  201. package/scripts/lib/gemini-profiles.mjs +85 -0
  202. package/scripts/lib/handoff.mjs +171 -0
  203. package/scripts/lib/hook-utils.mjs +14 -0
  204. package/scripts/lib/keyword-rules.mjs +166 -0
  205. package/scripts/lib/logger.mjs +105 -0
  206. package/scripts/lib/mcp-filter.mjs +739 -0
  207. package/scripts/lib/mcp-guard-engine.mjs +954 -0
  208. package/scripts/lib/mcp-manifest.mjs +79 -0
  209. package/scripts/lib/mcp-server-catalog.mjs +118 -0
  210. package/scripts/lib/psmux-info.mjs +119 -0
  211. package/scripts/lib/remote-spawn-transfer.mjs +196 -0
  212. package/scripts/lib/skill-template.mjs +326 -0
  213. package/scripts/mcp-check.mjs +237 -0
  214. package/scripts/mcp-cleanup.ps1 +17 -0
  215. package/scripts/mcp-gateway-config.mjs +207 -0
  216. package/scripts/mcp-gateway-ensure.mjs +85 -0
  217. package/scripts/mcp-gateway-integration-test.mjs +228 -0
  218. package/scripts/mcp-gateway-start.mjs +226 -0
  219. package/scripts/mcp-gateway-start.ps1 +141 -0
  220. package/scripts/mcp-gateway-verify.mjs +77 -0
  221. package/scripts/mcp-safety-guard.mjs +44 -0
  222. package/scripts/notion-read.mjs +556 -0
  223. package/scripts/pack.mjs +295 -0
  224. package/scripts/preflight-cache.mjs +69 -0
  225. package/scripts/preinstall.mjs +96 -0
  226. package/scripts/remote-spawn.mjs +1376 -0
  227. package/scripts/run.cjs +79 -0
  228. package/scripts/session-spawn-helper.mjs +185 -0
  229. package/scripts/setup.mjs +1178 -0
  230. package/scripts/test-lock.mjs +71 -0
  231. package/scripts/test-tfx-route-no-claude-native.mjs +57 -0
  232. package/scripts/tfx-batch-stats.mjs +96 -0
  233. package/scripts/tfx-gate-activate.mjs +89 -0
  234. package/scripts/tfx-route-post.mjs +505 -0
  235. package/scripts/tfx-route-worker.mjs +223 -0
  236. package/scripts/tfx-route.sh +2014 -0
  237. package/scripts/tmp-cleanup.mjs +103 -0
  238. package/scripts/token-snapshot.mjs +575 -0
  239. package/skills/tfx-auto/SKILL.md.tmpl +2 -3
  240. package/skills/tfx-autoresearch/SKILL.md +6 -5
  241. package/skills/tfx-codex/SKILL.md.tmpl +2 -3
  242. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +33 -0
  243. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/eval_metadata.json +42 -0
  244. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +11 -0
  245. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/analysis.md +87 -0
  246. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/classification.md +35 -0
  247. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/commands.sh +275 -0
  248. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/routing.md +56 -0
  249. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/timing.json +5 -0
  250. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +11 -0
  251. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/analysis.md +92 -0
  252. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/classification.md +71 -0
  253. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/commands.sh +264 -0
  254. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/routing.md +113 -0
  255. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/timing.json +5 -0
  256. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/eval_metadata.json +32 -0
  257. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +9 -0
  258. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/analysis.md +96 -0
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/classification.md +38 -0
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/commands.sh +151 -0
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/routing.md +51 -0
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/timing.json +5 -0
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +9 -0
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/analysis.md +127 -0
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/classification.md +57 -0
  266. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/commands.sh +129 -0
  267. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/routing.md +84 -0
  268. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/timing.json +5 -0
  269. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/eval_metadata.json +27 -0
  270. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +8 -0
  271. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/analysis.md +98 -0
  272. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/classification.md +65 -0
  273. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/commands.sh +123 -0
  274. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/routing.md +66 -0
  275. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/timing.json +5 -0
  276. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +8 -0
  277. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/analysis.md +88 -0
  278. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/classification.md +40 -0
  279. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/commands.sh +130 -0
  280. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/routing.md +61 -0
  281. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/timing.json +5 -0
  282. package/skills/tfx-deep-interview/SKILL.md +1 -2
  283. package/skills/tfx-plan/SKILL.md.tmpl +2 -3
  284. package/skills/tfx-psmux-rules/SKILL.md +11 -2
  285. package/skills/tfx-qa/SKILL.md.tmpl +2 -3
  286. package/skills/tfx-remote-spawn/SKILL.md +8 -11
  287. package/skills/tfx-research/SKILL.md.tmpl +2 -3
  288. package/skills/tfx-review/SKILL.md.tmpl +2 -3
  289. package/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
  290. package/skills/tfx-workspace/evals/evals.json +79 -0
  291. package/skills/tfx-workspace/iteration-1/benchmark.json +162 -0
  292. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
  293. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +9 -0
  294. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
  295. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
  296. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +9 -0
  297. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
  298. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
  299. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
  300. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +9 -0
  301. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
  302. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
  303. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +9 -0
  304. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
  305. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
  306. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
  307. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +8 -0
  308. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
  309. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
  310. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +8 -0
  311. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
  312. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
  313. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
  314. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +10 -0
  315. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
  316. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
  317. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +10 -0
  318. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
  319. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
  320. package/skills/tfx-workspace/iteration-1/review.html +1325 -0
  321. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
  322. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +10 -0
  323. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
  324. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
  325. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +10 -0
  326. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
  327. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
  328. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
  329. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +10 -0
  330. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
  331. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
  332. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +10 -0
  333. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
  334. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
  335. package/skills/tfx-workspace/iteration-2/benchmark.json +62 -0
  336. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
  337. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +11 -0
  338. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
  339. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
  340. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +11 -0
  341. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
  342. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
  343. package/skills/tfx-workspace/iteration-2/review.html +1325 -0
  344. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
  345. package/skills/{tfx-auto-codex/SKILL.md.tmpl → tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md} +3 -31
  346. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
  347. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
  348. package/skills/{tfx-gemini/SKILL.md.tmpl → tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md} +6 -14
  349. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
  350. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
  351. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
  352. package/skills/merge-worktree/SKILL.md.tmpl +0 -144
  353. package/skills/shared/arguments-processing.md +0 -2
  354. package/skills/shared/mandatory-rules.md +0 -6
  355. package/skills/shared/telemetry-segment.md +0 -6
  356. package/skills/star-prompt/SKILL.md.tmpl +0 -122
  357. package/skills/tfx-analysis/SKILL.md.tmpl +0 -106
  358. package/skills/tfx-analysis/skill.json +0 -11
  359. package/skills/tfx-auto/skill.json +0 -26
  360. package/skills/tfx-auto-codex/skill.json +0 -8
  361. package/skills/tfx-autopilot/SKILL.md.tmpl +0 -115
  362. package/skills/tfx-autopilot/skill.json +0 -10
  363. package/skills/tfx-autoresearch/SKILL.md.tmpl +0 -135
  364. package/skills/tfx-autoresearch/skill.json +0 -14
  365. package/skills/tfx-autoroute/SKILL.md.tmpl +0 -188
  366. package/skills/tfx-autoroute/skill.json +0 -12
  367. package/skills/tfx-codex/skill.json +0 -8
  368. package/skills/tfx-codex-swarm/SKILL.md.tmpl +0 -16
  369. package/skills/tfx-codex-swarm/skill.json +0 -5
  370. package/skills/tfx-consensus/SKILL.md.tmpl +0 -145
  371. package/skills/tfx-consensus/skill.json +0 -8
  372. package/skills/tfx-debate/SKILL.md.tmpl +0 -191
  373. package/skills/tfx-debate/skill.json +0 -12
  374. package/skills/tfx-deep-analysis/SKILL.md.tmpl +0 -227
  375. package/skills/tfx-deep-analysis/skill.json +0 -10
  376. package/skills/tfx-deep-interview/SKILL.md.tmpl +0 -203
  377. package/skills/tfx-deep-interview/skill.json +0 -12
  378. package/skills/tfx-deep-plan/SKILL.md.tmpl +0 -281
  379. package/skills/tfx-deep-plan/skill.json +0 -13
  380. package/skills/tfx-deep-qa/SKILL.md.tmpl +0 -164
  381. package/skills/tfx-deep-qa/skill.json +0 -11
  382. package/skills/tfx-deep-research/SKILL.md.tmpl +0 -216
  383. package/skills/tfx-deep-research/skill.json +0 -14
  384. package/skills/tfx-deep-review/SKILL.md.tmpl +0 -178
  385. package/skills/tfx-deep-review/skill.json +0 -12
  386. package/skills/tfx-doctor/SKILL.md.tmpl +0 -172
  387. package/skills/tfx-doctor/skill.json +0 -8
  388. package/skills/tfx-find/skill.json +0 -12
  389. package/skills/tfx-forge/SKILL.md.tmpl +0 -187
  390. package/skills/tfx-forge/skill.json +0 -12
  391. package/skills/tfx-fullcycle/SKILL.md.tmpl +0 -285
  392. package/skills/tfx-fullcycle/skill.json +0 -11
  393. package/skills/tfx-gemini/skill.json +0 -8
  394. package/skills/tfx-hooks/SKILL.md.tmpl +0 -216
  395. package/skills/tfx-hooks/skill.json +0 -8
  396. package/skills/tfx-hub/SKILL.md.tmpl +0 -212
  397. package/skills/tfx-hub/skill.json +0 -8
  398. package/skills/tfx-index/skill.json +0 -11
  399. package/skills/tfx-interview/SKILL.md.tmpl +0 -284
  400. package/skills/tfx-interview/skill.json +0 -12
  401. package/skills/tfx-multi/SKILL.md.tmpl +0 -183
  402. package/skills/tfx-multi/skill.json +0 -8
  403. package/skills/tfx-panel/SKILL.md.tmpl +0 -188
  404. package/skills/tfx-panel/skill.json +0 -12
  405. package/skills/tfx-persist/SKILL.md.tmpl +0 -269
  406. package/skills/tfx-persist/skill.json +0 -12
  407. package/skills/tfx-plan/skill.json +0 -11
  408. package/skills/tfx-profile/SKILL.md.tmpl +0 -239
  409. package/skills/tfx-profile/skill.json +0 -8
  410. package/skills/tfx-prune/SKILL.md.tmpl +0 -199
  411. package/skills/tfx-prune/skill.json +0 -12
  412. package/skills/tfx-psmux-rules/SKILL.md.tmpl +0 -317
  413. package/skills/tfx-psmux-rules/skill.json +0 -8
  414. package/skills/tfx-qa/skill.json +0 -11
  415. package/skills/tfx-ralph/SKILL.md.tmpl +0 -27
  416. package/skills/tfx-ralph/skill.json +0 -8
  417. package/skills/tfx-remote-setup/SKILL.md.tmpl +0 -576
  418. package/skills/tfx-remote-setup/skill.json +0 -8
  419. package/skills/tfx-remote-spawn/SKILL.md.tmpl +0 -263
  420. package/skills/tfx-remote-spawn/skill.json +0 -9
  421. package/skills/tfx-research/skill.json +0 -13
  422. package/skills/tfx-review/skill.json +0 -11
  423. package/skills/tfx-setup/SKILL.md.tmpl +0 -380
  424. package/skills/tfx-setup/skill.json +0 -8
  425. package/skills/tfx-swarm/SKILL.md.tmpl +0 -154
  426. package/skills/tfx-swarm/skill.json +0 -5
@@ -0,0 +1,193 @@
1
+ // hub/team/worktree-lifecycle.mjs — Git worktree lifecycle management
2
+ // Replaces shell commands in tfx-codex-swarm Step 5 + merge-worktree Phase 6.
3
+ // Convention: .codex-swarm/wt-{slug} paths, swarm/{runId}/{slug} branches.
4
+ // Remote support: host option → SSH-based git operations via remote-session.mjs.
5
+
6
+ import { execFile } from 'node:child_process';
7
+ import { resolve, normalize } from 'node:path';
8
+ import { mkdir, rm, access } from 'node:fs/promises';
9
+ import { remoteGit } from './remote-session.mjs';
10
+
11
+ const SWARM_ROOT = '.codex-swarm';
12
+ const SLEEP_MS = 2000; // WT race-guard (MEMORY.md: wt-attach-spacing)
13
+
14
+ function git(args, cwd) {
15
+ return new Promise((res, rej) => {
16
+ execFile('git', args, { cwd, windowsHide: true, timeout: 30_000 }, (err, stdout, stderr) => {
17
+ if (err) {
18
+ const msg = `git ${args[0]} failed: ${stderr?.trim() || err.message}`;
19
+ rej(new Error(msg));
20
+ } else {
21
+ res(stdout.trim());
22
+ }
23
+ });
24
+ });
25
+ }
26
+
27
+ function sleep(ms) {
28
+ return new Promise((r) => setTimeout(r, ms));
29
+ }
30
+
31
+ /** Normalize path for Windows compatibility. */
32
+ function normPath(p) {
33
+ return normalize(p).replace(/\\/g, '/');
34
+ }
35
+
36
+ /**
37
+ * Create a worktree for a shard.
38
+ *
39
+ * @param {object} opts
40
+ * @param {string} opts.slug — shard identifier (e.g. "issue-42" or "auth-refactor")
41
+ * @param {string} opts.runId — swarm run ID
42
+ * @param {string} [opts.rootDir=process.cwd()] — repo root
43
+ * @param {string} [opts.baseBranch='main'] — base branch to branch from
44
+ * @param {string} [opts.host] — SSH host for remote worktree creation
45
+ * @param {object} [opts.remoteEnv] — remote environment from probeRemoteEnv()
46
+ * @returns {Promise<{ worktreePath: string, branchName: string, remote: boolean }>}
47
+ */
48
+ export async function ensureWorktree({ slug, runId, rootDir = process.cwd(), baseBranch = 'main', host, remoteEnv }) {
49
+ const branchName = `swarm/${runId}/${slug}`;
50
+
51
+ // ── Remote path: SSH-based worktree creation ──
52
+ if (host && remoteEnv) {
53
+ const remoteRoot = rootDir.replace(/\\/g, '/');
54
+ const remoteWtDir = `${remoteRoot}/${SWARM_ROOT}/wt-${slug}`;
55
+
56
+ try { remoteGit(host, remoteEnv, ['worktree', 'prune'], remoteRoot); } catch { /* best-effort */ }
57
+
58
+ try {
59
+ remoteGit(host, remoteEnv, ['worktree', 'add', remoteWtDir, '-b', branchName, baseBranch], remoteRoot);
60
+ } catch {
61
+ try {
62
+ remoteGit(host, remoteEnv, ['worktree', 'add', remoteWtDir, branchName], remoteRoot);
63
+ } catch { /* already exists — acceptable */ }
64
+ }
65
+
66
+ return { worktreePath: remoteWtDir, branchName, remote: true };
67
+ }
68
+
69
+ // ── Local path (existing logic) ──
70
+ const wtDir = resolve(rootDir, SWARM_ROOT, `wt-${slug}`);
71
+
72
+ await mkdir(resolve(rootDir, SWARM_ROOT), { recursive: true });
73
+
74
+ // Check if worktree already exists
75
+ try {
76
+ await access(wtDir);
77
+ await git(['rev-parse', '--is-inside-work-tree'], wtDir);
78
+ return { worktreePath: normPath(wtDir), branchName, remote: false };
79
+ } catch {
80
+ // Doesn't exist or invalid — create fresh
81
+ }
82
+
83
+ try {
84
+ await git(['worktree', 'prune'], rootDir);
85
+ } catch { /* best-effort */ }
86
+
87
+ await sleep(SLEEP_MS);
88
+
89
+ try {
90
+ await git(['worktree', 'add', wtDir, '-b', branchName, baseBranch], rootDir);
91
+ } catch {
92
+ await git(['worktree', 'add', wtDir, branchName], rootDir);
93
+ }
94
+
95
+ return { worktreePath: normPath(wtDir), branchName, remote: false };
96
+ }
97
+
98
+ /**
99
+ * Create a temporary integration branch for merge operations.
100
+ *
101
+ * @param {object} opts
102
+ * @param {string} opts.runId
103
+ * @param {string} opts.baseBranch
104
+ * @param {string} [opts.rootDir=process.cwd()]
105
+ * @returns {Promise<{ integrationBranch: string, baseCommit: string }>}
106
+ */
107
+ export async function prepareIntegrationBranch({ runId, baseBranch, rootDir = process.cwd() }) {
108
+ const integrationBranch = `swarm/${runId}/merge`;
109
+
110
+ // Record base commit for rollback
111
+ const baseCommit = await git(['rev-parse', baseBranch], rootDir);
112
+
113
+ // Create integration branch from base
114
+ try {
115
+ await git(['branch', integrationBranch, baseBranch], rootDir);
116
+ } catch {
117
+ // Branch may already exist — reset to base
118
+ await git(['branch', '-f', integrationBranch, baseBranch], rootDir);
119
+ }
120
+
121
+ return { integrationBranch, baseCommit };
122
+ }
123
+
124
+ /**
125
+ * Rebase a shard branch onto the integration branch.
126
+ * Uses rebase + fast-forward only (no merge commits).
127
+ *
128
+ * @param {object} opts
129
+ * @param {string} opts.shardBranch — the shard's branch name
130
+ * @param {string} opts.integrationBranch — target integration branch
131
+ * @param {string} [opts.rootDir=process.cwd()]
132
+ * @returns {Promise<{ ok: boolean, headCommit?: string, error?: string }>}
133
+ */
134
+ export async function rebaseShardOntoIntegration({ shardBranch, integrationBranch, rootDir = process.cwd() }) {
135
+ // Backup integration HEAD for rollback
136
+ const backupCommit = await git(['rev-parse', integrationBranch], rootDir);
137
+
138
+ try {
139
+ // Rebase shard onto integration
140
+ await git(['rebase', integrationBranch, shardBranch], rootDir);
141
+
142
+ // Fast-forward integration to include shard changes
143
+ await git(['checkout', integrationBranch], rootDir);
144
+ await git(['merge', '--ff-only', shardBranch], rootDir);
145
+
146
+ const headCommit = await git(['rev-parse', 'HEAD'], rootDir);
147
+ return { ok: true, headCommit };
148
+ } catch (err) {
149
+ // Abort rebase and restore integration branch
150
+ try { await git(['rebase', '--abort'], rootDir); } catch { /* already clean */ }
151
+ try { await git(['checkout', integrationBranch], rootDir); } catch { /* best-effort */ }
152
+ try { await git(['reset', '--hard', backupCommit], rootDir); } catch { /* best-effort */ }
153
+
154
+ return { ok: false, error: err.message };
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Remove a worktree and its branch.
160
+ * Follows WT race-guard: sleep between operations.
161
+ *
162
+ * @param {object} opts
163
+ * @param {string} opts.worktreePath
164
+ * @param {string} [opts.branchName] — optional branch to delete
165
+ * @param {string} [opts.rootDir=process.cwd()]
166
+ * @param {boolean} [opts.force=false]
167
+ */
168
+ export async function pruneWorktree({ worktreePath, branchName, rootDir = process.cwd(), force = false }) {
169
+ const forceFlag = force ? '--force' : '';
170
+
171
+ // Remove worktree (with retry for Windows file handle issues — E5)
172
+ for (let attempt = 0; attempt < 3; attempt++) {
173
+ try {
174
+ await git(['worktree', 'remove', worktreePath, ...(forceFlag ? [forceFlag] : [])], rootDir);
175
+ break;
176
+ } catch (err) {
177
+ if (attempt === 2) {
178
+ // Last resort: rm the directory and prune
179
+ try { await rm(worktreePath, { recursive: true, force: true }); } catch { /* ignore */ }
180
+ }
181
+ await sleep(SLEEP_MS);
182
+ }
183
+ }
184
+
185
+ // Prune stale worktree references
186
+ await sleep(SLEEP_MS);
187
+ try { await git(['worktree', 'prune'], rootDir); } catch { /* best-effort */ }
188
+
189
+ // Delete branch if specified
190
+ if (branchName) {
191
+ try { await git(['branch', '-D', branchName], rootDir); } catch { /* may not exist */ }
192
+ }
193
+ }
@@ -0,0 +1,407 @@
1
+ import childProcess from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
3
+ import { platform as osPlatform, tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ import { sendKeysToPane } from "./psmux.mjs";
7
+
8
+ const DEFAULT_WINDOW_NAME = "triflux";
9
+ const DEFAULT_MAX_TABS = 8;
10
+ const DEFAULT_TAB_CREATE_DELAY_MS = 500;
11
+ const DEFAULT_WAIT_TIMEOUT_MS = 5_000;
12
+ const DEFAULT_WAIT_POLL_MS = 300;
13
+
14
+ function sleep(ms) {
15
+ return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
16
+ }
17
+
18
+ function resolvePositiveInteger(...values) {
19
+ for (const value of values) {
20
+ const parsed = Number.parseInt(String(value ?? ""), 10);
21
+ if (Number.isInteger(parsed) && parsed > 0) {
22
+ return parsed;
23
+ }
24
+ }
25
+ return null;
26
+ }
27
+
28
+ function sanitizeTitleForPidFile(title) {
29
+ const sanitized = [...String(title).trim()]
30
+ .map((character) => {
31
+ const code = character.charCodeAt(0);
32
+ return code <= 31 || '<>:"/\\|?*'.includes(character) ? "_" : character;
33
+ })
34
+ .join("");
35
+
36
+ return (
37
+ sanitized
38
+ .replace(/\s+/gu, "-")
39
+ .replace(/_+/gu, "_")
40
+ .replace(/-+/gu, "-")
41
+ .replace(/^[-_]+|[-_]+$/gu, "") || "wt-tab"
42
+ );
43
+ }
44
+
45
+ function escapePowerShellSingleQuoted(value) {
46
+ return String(value).replace(/'/g, "''");
47
+ }
48
+
49
+ function buildPidFilePath(pidDir, title) {
50
+ return join(pidDir, `${sanitizeTitleForPidFile(title)}.pid`);
51
+ }
52
+
53
+ function buildWrappedCommand(pidFile, command) {
54
+ const escapedPidFile = escapePowerShellSingleQuoted(pidFile);
55
+ const pidWrite = `$PID | Set-Content '${escapedPidFile}'`;
56
+ return command ? `${pidWrite}; ${command}` : pidWrite;
57
+ }
58
+
59
+ /**
60
+ * PowerShell -EncodedCommand용 Base64 인코딩.
61
+ * CreateProcess 이중 쿼팅 문제를 완전히 회피한다.
62
+ */
63
+ function encodeForPowerShell(script) {
64
+ const buf = Buffer.from(script, 'utf16le');
65
+ return buf.toString('base64');
66
+ }
67
+
68
+ function matchesTitlePattern(title, pattern) {
69
+ if (!pattern) return true;
70
+ if (pattern instanceof RegExp) return pattern.test(title);
71
+ return String(title).includes(String(pattern));
72
+ }
73
+
74
+ function defaultIsPidAlive(pid, execFileSyncFn = childProcess.execFileSync) {
75
+ try {
76
+ process.kill(pid, 0);
77
+ return true;
78
+ } catch {
79
+ // tasklist fallback — process.kill(..., 0)이 권한/플랫폼 차이로 실패할 수 있음.
80
+ }
81
+
82
+ try {
83
+ const output = execFileSyncFn(
84
+ "tasklist",
85
+ ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"],
86
+ {
87
+ encoding: "utf8",
88
+ timeout: 3_000,
89
+ stdio: ["ignore", "pipe", "ignore"],
90
+ windowsHide: true,
91
+ },
92
+ );
93
+ return (
94
+ !/No tasks are running/u.test(String(output)) &&
95
+ String(output).includes(`"${pid}"`)
96
+ );
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Windows Terminal 탭/세션 라이프사이클 관리자.
104
+ *
105
+ * @param {object} [opts]
106
+ * @param {string} [opts.windowName='triflux']
107
+ * @param {number} [opts.maxTabs=8]
108
+ * @param {string} [opts.pidDir=os.tmpdir()/wt-manager-pids]
109
+ * @param {number} [opts.tabCreateDelayMs=500]
110
+ * @param {object} [opts.deps] — 테스트용 의존성 주입
111
+ */
112
+ export function createWtManager(opts = {}) {
113
+ const deps = opts.deps || {};
114
+ const platform = deps.platform || osPlatform;
115
+
116
+ if (platform() !== "win32") {
117
+ throw new Error("wt-manager.mjs is Windows-only");
118
+ }
119
+
120
+ const now = deps.now || Date.now;
121
+ const sleepFn = deps.sleep || sleep;
122
+ const spawnFn = deps.spawn || childProcess.spawn;
123
+ const execFileSyncFn = deps.execFileSync || childProcess.execFileSync;
124
+ const killFn = deps.kill || ((pid) => process.kill(pid));
125
+ const sendKeysFn = deps.sendKeysToPane || sendKeysToPane;
126
+ const isPidAlive =
127
+ deps.isPidAlive || ((pid) => defaultIsPidAlive(pid, execFileSyncFn));
128
+ const ensureDir =
129
+ deps.ensureDir || ((dir) => mkdirSync(dir, { recursive: true }));
130
+ const exists = deps.exists || existsSync;
131
+ const readText =
132
+ deps.readText || ((filePath) => readFileSync(filePath, "utf8"));
133
+ const removeFile =
134
+ deps.removeFile || ((filePath) => rmSync(filePath, { force: true }));
135
+
136
+ const windowName = String(opts.windowName || DEFAULT_WINDOW_NAME);
137
+ const maxTabs =
138
+ resolvePositiveInteger(
139
+ opts.maxTabs,
140
+ process.env.WTM_MAX_TABS,
141
+ DEFAULT_MAX_TABS,
142
+ ) || DEFAULT_MAX_TABS;
143
+ const pidDir = String(opts.pidDir || join(tmpdir(), "wt-manager-pids"));
144
+ const tabCreateDelayMs =
145
+ resolvePositiveInteger(
146
+ opts.tabCreateDelayMs,
147
+ DEFAULT_TAB_CREATE_DELAY_MS,
148
+ ) || DEFAULT_TAB_CREATE_DELAY_MS;
149
+
150
+ ensureDir(pidDir);
151
+
152
+ /** @type {Map<string, { pid: number, createdAt: number, pidFile: string }>} */
153
+ const tabs = new Map();
154
+ let lastTabCreateAt = null;
155
+
156
+ function forgetTab(title) {
157
+ const entry = tabs.get(title);
158
+ if (entry) {
159
+ tabs.delete(title);
160
+ try {
161
+ removeFile(entry.pidFile);
162
+ } catch {
163
+ /* ignore */
164
+ }
165
+ }
166
+ }
167
+
168
+ function pruneDeadTabs() {
169
+ for (const [title, entry] of tabs.entries()) {
170
+ if (!isPidAlive(entry.pid)) {
171
+ forgetTab(title);
172
+ }
173
+ }
174
+ }
175
+
176
+ async function throttleTabCreate() {
177
+ if (lastTabCreateAt == null) return;
178
+ const elapsed = now() - lastTabCreateAt;
179
+ if (elapsed < tabCreateDelayMs) {
180
+ await sleepFn(tabCreateDelayMs - elapsed);
181
+ }
182
+ }
183
+
184
+ async function waitTabReady(title, pidFile) {
185
+ const deadline = now() + DEFAULT_WAIT_TIMEOUT_MS;
186
+
187
+ while (now() <= deadline) {
188
+ if (exists(pidFile)) {
189
+ const pid = Number.parseInt(String(readText(pidFile)).trim(), 10);
190
+ if (Number.isInteger(pid) && pid > 0 && isPidAlive(pid)) {
191
+ return pid;
192
+ }
193
+ }
194
+
195
+ const remaining = deadline - now();
196
+ if (remaining <= 0) break;
197
+ await sleepFn(Math.min(DEFAULT_WAIT_POLL_MS, remaining));
198
+ }
199
+
200
+ throw new Error(`WT tab ready timeout: ${title}`);
201
+ }
202
+
203
+ async function createTab(tab = {}) {
204
+ const title = String(tab.title || "").trim();
205
+ if (!title) {
206
+ throw new Error("title is required");
207
+ }
208
+
209
+ pruneDeadTabs();
210
+
211
+ if (tabs.has(title)) {
212
+ throw new Error(`WT tab already exists: ${title}`);
213
+ }
214
+
215
+ if (tabs.size >= maxTabs) {
216
+ throw new Error(`WT max tabs exceeded (${maxTabs})`);
217
+ }
218
+
219
+ await throttleTabCreate();
220
+
221
+ const pidFile = buildPidFilePath(pidDir, title);
222
+ try {
223
+ removeFile(pidFile);
224
+ } catch {
225
+ /* ignore */
226
+ }
227
+
228
+ const args = ["-w", windowName, "nt", "--title", title];
229
+ if (tab.profile) {
230
+ args.push("--profile", String(tab.profile));
231
+ }
232
+ if (tab.cwd) {
233
+ args.push("-d", String(tab.cwd));
234
+ }
235
+ const script = buildWrappedCommand(pidFile, tab.command);
236
+ args.push(
237
+ "--",
238
+ "powershell.exe",
239
+ "-NoExit",
240
+ "-EncodedCommand",
241
+ encodeForPowerShell(script),
242
+ );
243
+
244
+ const child = spawnFn("wt.exe", args, {
245
+ detached: true,
246
+ stdio: "ignore",
247
+ });
248
+ child?.unref?.();
249
+
250
+ lastTabCreateAt = now();
251
+ const pid = await waitTabReady(title, pidFile);
252
+ const entry = Object.freeze({
253
+ pid,
254
+ createdAt: now(),
255
+ pidFile,
256
+ });
257
+ tabs.set(title, entry);
258
+ }
259
+
260
+ async function closeTab(title) {
261
+ const normalizedTitle = String(title || "").trim();
262
+ if (!normalizedTitle) return;
263
+
264
+ const entry = tabs.get(normalizedTitle);
265
+ if (!entry) return;
266
+
267
+ try {
268
+ killFn(entry.pid);
269
+ } catch {
270
+ // 이미 종료된 PID는 map 정리만 수행한다.
271
+ }
272
+
273
+ forgetTab(normalizedTitle);
274
+ }
275
+
276
+ function listTabs() {
277
+ pruneDeadTabs();
278
+ return [...tabs.entries()].map(([title, entry]) =>
279
+ Object.freeze({
280
+ title,
281
+ pid: entry.pid,
282
+ createdAt: entry.createdAt,
283
+ }),
284
+ );
285
+ }
286
+
287
+ async function closeStale(closeOpts = {}) {
288
+ pruneDeadTabs();
289
+
290
+ const olderThanMs = Number.isFinite(closeOpts.olderThanMs)
291
+ ? Math.max(0, Math.trunc(closeOpts.olderThanMs))
292
+ : 0;
293
+ const titlePattern = closeOpts.titlePattern;
294
+ const snapshot = [...tabs.entries()];
295
+ let closed = 0;
296
+
297
+ for (const [title, entry] of snapshot) {
298
+ const ageMs = now() - entry.createdAt;
299
+ if (ageMs < olderThanMs) continue;
300
+ if (!matchesTitlePattern(title, titlePattern)) continue;
301
+ await closeTab(title);
302
+ closed += 1;
303
+ }
304
+
305
+ return closed;
306
+ }
307
+
308
+ /**
309
+ * 현재 탭을 split-pane으로 분할.
310
+ * @param {object} opts
311
+ * @param {'H'|'V'} [opts.direction='V'] — H=좌우, V=상하
312
+ * @param {string} [opts.title]
313
+ * @param {string} [opts.profile]
314
+ * @param {string} [opts.cwd]
315
+ * @param {string} [opts.command] — pane에서 실행할 명령
316
+ * @param {number} [opts.size] — 퍼센트 (0-100)
317
+ */
318
+ async function splitPane(opts = {}) {
319
+ const direction = opts.direction === 'H' ? '-H' : '-V';
320
+ const args = ['-w', windowName, 'sp', direction];
321
+
322
+ if (opts.title) {
323
+ args.push('--title', String(opts.title));
324
+ }
325
+ if (opts.profile) {
326
+ args.push('--profile', String(opts.profile));
327
+ }
328
+ if (opts.size && Number.isFinite(opts.size)) {
329
+ args.push('-s', String(opts.size / 100));
330
+ }
331
+ if (opts.cwd) {
332
+ args.push('-d', String(opts.cwd));
333
+ }
334
+ if (opts.command) {
335
+ const script = opts.command;
336
+ args.push('--', 'powershell.exe', '-NoExit', '-EncodedCommand', encodeForPowerShell(script));
337
+ }
338
+
339
+ const child = spawnFn('wt.exe', args, {
340
+ detached: true,
341
+ stdio: 'ignore',
342
+ });
343
+ child?.unref?.();
344
+
345
+ // split-pane은 기존 탭 안에서 분할하므로 throttle 적용
346
+ await sleepFn(tabCreateDelayMs);
347
+ }
348
+
349
+ /**
350
+ * 여러 세션을 split-pane 레이아웃으로 한번에 배치.
351
+ * @param {Array<{title: string, command: string, direction?: 'H'|'V', size?: number}>} panes
352
+ */
353
+ async function applySplitLayout(panes) {
354
+ if (!panes || panes.length === 0) return;
355
+
356
+ // 첫 번째는 새 탭으로 생성
357
+ const first = panes[0];
358
+ await createTab({ title: first.title, command: first.command, profile: first.profile });
359
+
360
+ // 나머지는 split-pane으로 분할
361
+ for (let i = 1; i < panes.length; i++) {
362
+ await splitPane({
363
+ direction: panes[i].direction || 'V',
364
+ title: panes[i].title,
365
+ command: panes[i].command,
366
+ profile: panes[i].profile,
367
+ size: panes[i].size,
368
+ });
369
+ }
370
+ }
371
+
372
+ async function createSession(sessionOpts = {}) {
373
+ const tab =
374
+ typeof sessionOpts.tab === "string"
375
+ ? { title: sessionOpts.tab }
376
+ : sessionOpts.tab;
377
+
378
+ if (!tab || typeof tab !== "object") {
379
+ throw new Error("tab is required");
380
+ }
381
+ if (!sessionOpts.pane) {
382
+ throw new Error("pane is required");
383
+ }
384
+ if (!sessionOpts.command) {
385
+ throw new Error("command is required");
386
+ }
387
+
388
+ await createTab(tab);
389
+ sendKeysFn(String(sessionOpts.pane), String(sessionOpts.command), true);
390
+ }
391
+
392
+ function getTabCount() {
393
+ pruneDeadTabs();
394
+ return tabs.size;
395
+ }
396
+
397
+ return Object.freeze({
398
+ createTab,
399
+ closeTab,
400
+ listTabs,
401
+ closeStale,
402
+ createSession,
403
+ splitPane,
404
+ applySplitLayout,
405
+ getTabCount,
406
+ });
407
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "version": 1,
3
+ "templates": {
4
+ "swarm-3": {
5
+ "description": "Lead tab plus two worker tabs in the shared triflux window.",
6
+ "windowName": "triflux",
7
+ "tabs": [
8
+ {
9
+ "title": "swarm lead",
10
+ "profile": "triflux"
11
+ },
12
+ {
13
+ "title": "swarm worker-1",
14
+ "profile": "triflux"
15
+ },
16
+ {
17
+ "title": "swarm worker-2",
18
+ "profile": "triflux"
19
+ }
20
+ ]
21
+ },
22
+ "monitor": {
23
+ "description": "Single monitoring tab for dashboard, log tailing, or observer flows.",
24
+ "windowName": "triflux",
25
+ "tabs": [
26
+ {
27
+ "title": "swarm monitor",
28
+ "profile": "triflux"
29
+ }
30
+ ]
31
+ },
32
+ "single": {
33
+ "description": "One focused tab for direct operator work.",
34
+ "windowName": "triflux",
35
+ "tabs": [
36
+ {
37
+ "title": "single worker",
38
+ "profile": "triflux"
39
+ }
40
+ ]
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,27 @@
1
+ // @triflux/core — team-bridge 인터페이스
2
+ // remote 패키지가 런타임에 구현을 주입한다.
3
+
4
+ /**
5
+ * @typedef {object} TeamBridge
6
+ * @property {(args?: object) => Promise<object>} teamInfo
7
+ * @property {(args?: object) => Promise<object>} teamTaskList
8
+ * @property {(args?: object) => Promise<object>} teamTaskUpdate
9
+ * @property {(args?: object) => Promise<object>} teamSendMessage
10
+ */
11
+
12
+ /** @type {TeamBridge | null} */
13
+ let _bridge = null;
14
+
15
+ /**
16
+ * @param {TeamBridge | null} impl
17
+ */
18
+ export function registerTeamBridge(impl) {
19
+ _bridge = impl;
20
+ }
21
+
22
+ /**
23
+ * @returns {TeamBridge | null}
24
+ */
25
+ export function getTeamBridge() {
26
+ return _bridge;
27
+ }