triflux 10.4.0 → 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 (391) hide show
  1. package/.claude-plugin/marketplace.json +34 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/bin/tfx-doctor-tui.mjs +1 -1
  4. package/bin/tfx-doctor.mjs +6 -1
  5. package/bin/tfx-profile.mjs +1 -1
  6. package/bin/tfx-setup-tui.mjs +1 -1
  7. package/bin/tfx-setup.mjs +6 -1
  8. package/bin/triflux.mjs +2396 -1140
  9. package/config/mcp-registry.json +29 -0
  10. package/hooks/agent-route-guard.mjs +12 -8
  11. package/hooks/cross-review-tracker.mjs +21 -8
  12. package/hooks/error-context.mjs +19 -7
  13. package/hooks/hook-adaptive-collector.mjs +18 -16
  14. package/hooks/hook-manager.mjs +93 -32
  15. package/hooks/hook-orchestrator.mjs +108 -24
  16. package/hooks/hook-registry.json +267 -256
  17. package/hooks/keyword-rules.json +6 -10
  18. package/hooks/lib/resolve-root.mjs +1 -1
  19. package/hooks/mcp-config-watcher.mjs +6 -2
  20. package/hooks/pipeline-stop.mjs +3 -6
  21. package/hooks/safety-guard.mjs +99 -28
  22. package/hooks/session-start-fast.mjs +143 -0
  23. package/hooks/subagent-verifier.mjs +5 -4
  24. package/hub/account-broker.mjs +256 -60
  25. package/hub/adaptive-diagnostic.mjs +75 -48
  26. package/hub/adaptive-inject.mjs +96 -58
  27. package/hub/adaptive-memory.mjs +156 -42
  28. package/hub/adaptive.mjs +60 -31
  29. package/hub/assign-callbacks.mjs +163 -126
  30. package/hub/bridge.mjs +0 -1
  31. package/hub/cli-adapter-base.mjs +200 -48
  32. package/hub/codex-adapter.mjs +76 -96
  33. package/hub/codex-compat.mjs +3 -3
  34. package/hub/codex-preflight.mjs +63 -37
  35. package/hub/delegator/contracts.mjs +19 -23
  36. package/hub/delegator/index.mjs +14 -14
  37. package/hub/delegator/service.mjs +88 -64
  38. package/hub/delegator/tool-definitions.mjs +35 -35
  39. package/hub/fullcycle.mjs +33 -17
  40. package/hub/gemini-adapter.mjs +69 -94
  41. package/hub/hitl.mjs +202 -143
  42. package/hub/intent.mjs +161 -38
  43. package/hub/lib/cache-guard.mjs +43 -17
  44. package/hub/lib/mcp-response-cache.mjs +66 -32
  45. package/hub/lib/memory-store.mjs +285 -111
  46. package/hub/lib/path-utils.mjs +35 -37
  47. package/hub/lib/process-utils.mjs +106 -37
  48. package/hub/lib/spawn-trace.mjs +527 -0
  49. package/hub/lib/ssh-command.mjs +34 -4
  50. package/hub/lib/ssh-retry.mjs +5 -1
  51. package/hub/lib/uuidv7.mjs +4 -3
  52. package/hub/memory-doctor.mjs +266 -106
  53. package/hub/middleware/request-logger.mjs +61 -34
  54. package/hub/paths.mjs +9 -9
  55. package/hub/pipeline/gates/confidence.mjs +34 -15
  56. package/hub/pipeline/gates/consensus.mjs +27 -15
  57. package/hub/pipeline/gates/index.mjs +7 -3
  58. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  59. package/hub/pipeline/index.mjs +77 -42
  60. package/hub/pipeline/state.mjs +10 -10
  61. package/hub/pipeline/transitions.mjs +40 -23
  62. package/hub/platform.mjs +57 -48
  63. package/hub/promote-penalties.mjs +25 -7
  64. package/hub/quality/deslop.mjs +70 -49
  65. package/hub/research.mjs +32 -25
  66. package/hub/router.mjs +924 -791
  67. package/hub/routing/complexity.mjs +132 -29
  68. package/hub/routing/index.mjs +17 -12
  69. package/hub/routing/q-learning.mjs +76 -28
  70. package/hub/server.mjs +4 -4
  71. package/hub/session-fingerprint.mjs +127 -61
  72. package/hub/state.mjs +84 -43
  73. package/hub/store-adapter.mjs +59 -26
  74. package/hub/store.mjs +356 -153
  75. package/hub/team/agent-map.json +22 -7
  76. package/hub/team/ansi.mjs +186 -122
  77. package/hub/team/backend.mjs +28 -10
  78. package/hub/team/cli/commands/attach.mjs +57 -37
  79. package/hub/team/cli/commands/control.mjs +29 -8
  80. package/hub/team/cli/commands/debug.mjs +95 -74
  81. package/hub/team/cli/commands/focus.mjs +80 -53
  82. package/hub/team/cli/commands/interrupt.mjs +18 -6
  83. package/hub/team/cli/commands/kill.mjs +16 -5
  84. package/hub/team/cli/commands/list.mjs +31 -24
  85. package/hub/team/cli/commands/send.mjs +19 -6
  86. package/hub/team/cli/commands/start/index.mjs +154 -31
  87. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  88. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  89. package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
  90. package/hub/team/cli/commands/start/start-mux.mjs +122 -73
  91. package/hub/team/cli/commands/start/start-wt.mjs +86 -69
  92. package/hub/team/cli/commands/status.mjs +43 -14
  93. package/hub/team/cli/commands/stop.mjs +11 -4
  94. package/hub/team/cli/commands/task.mjs +8 -3
  95. package/hub/team/cli/commands/tasks.mjs +13 -13
  96. package/hub/team/cli/index.mjs +2 -2
  97. package/hub/team/cli/manifest.mjs +38 -8
  98. package/hub/team/cli/render.mjs +52 -30
  99. package/hub/team/cli/services/attach-fallback.mjs +74 -54
  100. package/hub/team/cli/services/hub-client.mjs +42 -14
  101. package/hub/team/cli/services/member-selector.mjs +37 -30
  102. package/hub/team/cli/services/native-control.mjs +143 -116
  103. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  104. package/hub/team/cli/services/state-store.mjs +25 -8
  105. package/hub/team/cli/services/task-model.mjs +40 -30
  106. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  107. package/hub/team/conductor.mjs +8 -4
  108. package/hub/team/dashboard-anchor.mjs +4 -5
  109. package/hub/team/dashboard-layout.mjs +3 -1
  110. package/hub/team/dashboard-open.mjs +41 -21
  111. package/hub/team/dashboard.mjs +76 -28
  112. package/hub/team/event-log.mjs +18 -10
  113. package/hub/team/handoff.mjs +31 -15
  114. package/hub/team/headless.mjs +2 -1
  115. package/hub/team/health-probe.mjs +69 -54
  116. package/hub/team/launcher-template.mjs +16 -13
  117. package/hub/team/native-supervisor.mjs +65 -21
  118. package/hub/team/native.mjs +74 -35
  119. package/hub/team/nativeProxy.mjs +184 -113
  120. package/hub/team/notify.mjs +120 -77
  121. package/hub/team/orchestrator.mjs +166 -161
  122. package/hub/team/pane.mjs +12 -7
  123. package/hub/team/process-cleanup.mjs +25 -16
  124. package/hub/team/psmux.mjs +491 -201
  125. package/hub/team/remote-probe.mjs +68 -52
  126. package/hub/team/remote-session.mjs +117 -59
  127. package/hub/team/remote-watcher.mjs +61 -33
  128. package/hub/team/routing.mjs +51 -25
  129. package/hub/team/runtime-strategy.mjs +3 -1
  130. package/hub/team/session.mjs +675 -611
  131. package/hub/team/shared.mjs +13 -13
  132. package/hub/team/staleState.mjs +72 -30
  133. package/hub/team/swarm-locks.mjs +15 -13
  134. package/hub/team/swarm-planner.mjs +32 -21
  135. package/hub/team/swarm-reconciler.mjs +48 -23
  136. package/hub/team/tui-lite.mjs +266 -68
  137. package/hub/team/tui-remote-adapter.mjs +14 -10
  138. package/hub/team/tui-viewer.mjs +99 -43
  139. package/hub/team/tui.mjs +708 -271
  140. package/hub/team/worktree-lifecycle.mjs +152 -58
  141. package/hub/team/wt-manager.mjs +24 -14
  142. package/hub/token-mode.mjs +71 -71
  143. package/hub/tray.mjs +411 -368
  144. package/hub/workers/claude-worker.mjs +162 -118
  145. package/hub/workers/codex-mcp.mjs +558 -507
  146. package/hub/workers/delegator-mcp.mjs +507 -333
  147. package/hub/workers/factory.mjs +21 -21
  148. package/hub/workers/gemini-worker.mjs +115 -84
  149. package/hub/workers/interface.mjs +6 -1
  150. package/hub/workers/worker-utils.mjs +21 -14
  151. package/hud/colors.mjs +27 -9
  152. package/hud/constants.mjs +162 -26
  153. package/hud/context-monitor.mjs +82 -41
  154. package/hud/hud-qos-status.mjs +129 -49
  155. package/hud/mission-board.mjs +6 -3
  156. package/hud/providers/claude.mjs +226 -115
  157. package/hud/providers/codex.mjs +62 -22
  158. package/hud/providers/gemini.mjs +168 -56
  159. package/hud/renderers.mjs +384 -119
  160. package/hud/terminal.mjs +101 -31
  161. package/hud/utils.mjs +78 -38
  162. package/mesh/index.mjs +11 -5
  163. package/mesh/mesh-budget.mjs +18 -9
  164. package/mesh/mesh-heartbeat.mjs +1 -1
  165. package/mesh/mesh-queue.mjs +3 -5
  166. package/mesh/mesh-router.mjs +5 -4
  167. package/package.json +56 -21
  168. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  169. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  170. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  171. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  172. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  173. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  174. package/scripts/__tests__/skill-template.test.mjs +98 -50
  175. package/scripts/__tests__/smoke.test.mjs +1 -1
  176. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  177. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  178. package/scripts/cache-doctor.mjs +11 -4
  179. package/scripts/cache-warmup.mjs +96 -37
  180. package/scripts/claudemd-sync.mjs +27 -17
  181. package/scripts/codex-gateway-preflight.mjs +52 -37
  182. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  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 +139 -120
  193. package/scripts/keyword-detector.mjs +293 -272
  194. package/scripts/keyword-rules-expander.mjs +538 -521
  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 +355 -118
  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 +669 -554
  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 +61 -57
  229. package/scripts/tfx-batch-stats.mjs +117 -96
  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/idle-notif-cooldown.json +1 -1
  237. package/skills/.omc/state/last-tool-error.json +1 -1
  238. package/skills/.omc/state/subagent-tracking.json +1 -1
  239. package/skills/_templates/base.md +1 -6
  240. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  241. package/skills/shared/telemetry-segment.md +6 -0
  242. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  243. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  244. package/skills/tfx-analysis/skill.json +1 -6
  245. package/skills/tfx-auto/SKILL.md +1 -0
  246. package/skills/{tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md → tfx-auto-codex/SKILL.md.tmpl} +33 -4
  247. package/skills/tfx-auto-codex/skill.json +1 -3
  248. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  249. package/skills/tfx-autopilot/skill.json +1 -5
  250. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  251. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  252. package/skills/tfx-autoroute/skill.json +1 -7
  253. package/skills/tfx-codex/SKILL.md +1 -0
  254. package/skills/tfx-codex/skill.json +1 -3
  255. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  256. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  257. package/skills/tfx-codex-swarm/skill.json +1 -4
  258. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  265. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  266. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  267. package/skills/tfx-debate/skill.json +1 -7
  268. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  269. package/skills/tfx-deep-analysis/skill.json +1 -5
  270. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  271. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  272. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  273. package/skills/tfx-deep-qa/skill.json +1 -6
  274. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  275. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  276. package/skills/tfx-doctor/SKILL.md +21 -0
  277. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  278. package/skills/tfx-doctor/skill.json +1 -3
  279. package/skills/tfx-find/SKILL.md +1 -0
  280. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  281. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  282. package/skills/tfx-fullcycle/skill.json +1 -6
  283. package/skills/{tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md → tfx-gemini/SKILL.md.tmpl} +16 -7
  284. package/skills/tfx-gemini/skill.json +1 -3
  285. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  286. package/skills/tfx-hooks/skill.json +1 -3
  287. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  288. package/skills/tfx-hub/skill.json +1 -3
  289. package/skills/tfx-index/SKILL.md +1 -0
  290. package/skills/tfx-index/skill.json +1 -6
  291. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  292. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  293. package/skills/tfx-multi/skill.json +1 -3
  294. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  295. package/skills/tfx-panel/skill.json +1 -7
  296. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  297. package/skills/tfx-persist/skill.json +1 -7
  298. package/skills/tfx-plan/SKILL.md +1 -0
  299. package/skills/tfx-plan/skill.json +1 -6
  300. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  301. package/skills/tfx-profile/skill.json +1 -3
  302. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  303. package/skills/tfx-prune/skill.json +1 -7
  304. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  305. package/skills/tfx-psmux-rules/skill.json +1 -4
  306. package/skills/tfx-qa/SKILL.md +1 -0
  307. package/skills/tfx-qa/skill.json +1 -6
  308. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  309. package/skills/tfx-ralph/skill.json +1 -4
  310. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  311. package/skills/tfx-remote-setup/skill.json +1 -3
  312. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  313. package/skills/tfx-remote-spawn/references/hosts.json +16 -16
  314. package/skills/tfx-remote-spawn/skill.json +1 -4
  315. package/skills/tfx-research/SKILL.md +1 -0
  316. package/skills/tfx-review/SKILL.md +1 -0
  317. package/skills/tfx-review/skill.json +1 -6
  318. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  319. package/skills/tfx-setup/skill.json +1 -3
  320. package/skills/tfx-swarm/SKILL.md +22 -0
  321. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  322. package/tui/codex-profile.mjs +457 -0
  323. package/tui/core.mjs +266 -0
  324. package/tui/doctor.mjs +375 -0
  325. package/tui/gemini-profile.mjs +299 -0
  326. package/tui/monitor-data.mjs +152 -0
  327. package/tui/monitor.mjs +339 -0
  328. package/tui/setup.mjs +533 -0
  329. package/CLAUDE.md +0 -193
  330. package/references/hosts.json +0 -33
  331. package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
  332. package/skills/tfx-workspace/evals/evals.json +0 -79
  333. package/skills/tfx-workspace/iteration-1/benchmark.json +0 -162
  334. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
  335. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -9
  336. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
  337. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
  338. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -9
  339. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
  340. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
  341. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
  342. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -9
  343. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
  344. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
  345. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -9
  346. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
  347. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
  348. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
  349. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -8
  350. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
  351. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
  352. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -8
  353. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
  354. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
  355. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
  356. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -10
  357. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
  358. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
  359. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -10
  360. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
  361. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
  362. package/skills/tfx-workspace/iteration-1/review.html +0 -1325
  363. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
  364. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -10
  365. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
  366. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
  367. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -10
  368. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
  369. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
  370. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
  371. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -10
  372. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
  373. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
  374. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -10
  375. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
  376. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
  377. package/skills/tfx-workspace/iteration-2/benchmark.json +0 -62
  378. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
  379. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -11
  380. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
  381. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
  382. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -11
  383. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
  384. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
  385. package/skills/tfx-workspace/iteration-2/review.html +0 -1325
  386. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
  387. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
  388. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
  389. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
  390. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
  391. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
@@ -1,35 +1,45 @@
1
1
  // hub/account-broker.mjs — Multi-account CLI pool broker
2
- // Manages lease/release/cooldown for Codex and Gemini accounts.
2
+ // Manages lease/release/cooldown/circuit-breaker for Codex and Gemini accounts.
3
+ // Per-account circuit breaker: one bad account does not block others.
3
4
  // Singleton export. All state changes create new objects (immutable pattern).
4
5
 
5
- import { readFileSync, existsSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { homedir } from 'node:os';
8
- import * as z from 'zod';
6
+ import { EventEmitter } from "node:events";
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { join, sep } from "node:path";
10
+ import * as z from "zod";
9
11
 
10
12
  // ── Zod schema ───────────────────────────────────────────────────
11
13
 
12
- const AccountSchema = z.object({
13
- id: z.string().min(1),
14
- mode: z.enum(['profile', 'env', 'auth']),
15
- profile: z.string().optional(),
16
- env: z.record(z.string(), z.string()).optional(),
17
- authFile: z.string().optional(),
18
- tier: z.enum(['pro', 'plus', 'free', 'unknown']).optional().default('unknown'),
19
- }).superRefine((val, ctx) => {
20
- if (val.mode === 'auth' && !val.authFile) {
21
- ctx.addIssue({
22
- code: z.ZodIssueCode.custom,
23
- message: 'authFile is required when mode is "auth"',
24
- path: ['authFile'],
25
- });
26
- }
27
- });
14
+ const AccountSchema = z
15
+ .object({
16
+ id: z.string().min(1),
17
+ mode: z.enum(["profile", "env", "auth"]),
18
+ profile: z.string().optional(),
19
+ env: z.record(z.string(), z.string()).optional(),
20
+ authFile: z.string().optional(),
21
+ host: z.string().min(1).optional(),
22
+ tier: z
23
+ .enum(["pro", "plus", "free", "unknown"])
24
+ .optional()
25
+ .default("unknown"),
26
+ })
27
+ .superRefine((val, ctx) => {
28
+ if (val.mode === "auth" && !val.authFile) {
29
+ ctx.addIssue({
30
+ code: z.ZodIssueCode.custom,
31
+ message: 'authFile is required when mode is "auth"',
32
+ path: ["authFile"],
33
+ });
34
+ }
35
+ });
28
36
 
29
37
  const ConfigSchema = z.object({
30
- defaults: z.object({
31
- cooldownMs: z.number().int().positive().optional(),
32
- }).optional(),
38
+ defaults: z
39
+ .object({
40
+ cooldownMs: z.number().int().positive().optional(),
41
+ })
42
+ .optional(),
33
43
  codex: z.array(AccountSchema).optional(),
34
44
  gemini: z.array(AccountSchema).optional(),
35
45
  });
@@ -37,7 +47,9 @@ const ConfigSchema = z.object({
37
47
  const DEFAULT_COOLDOWN_MS = 300_000; // 5 minutes
38
48
  const TIER_PRIORITY = { pro: 0, plus: 1, unknown: 2, free: 3 };
39
49
  const LEASE_TTL_MS = 30 * 60 * 1000; // 30 minutes
40
- const AUTH_BASE_PATH = join(homedir(), '.claude', 'cache', 'tfx-hub');
50
+ const CIRCUIT_WINDOW_MS = 10 * 60_000; // 10 minutes
51
+ const CIRCUIT_MAX_FAILURES = 3;
52
+ const AUTH_BASE_PATH = join(homedir(), ".claude", "cache", "tfx-hub");
41
53
 
42
54
  // ── env var resolution ───────────────────────────────────────────
43
55
 
@@ -45,9 +57,9 @@ function resolveEnvValues(env) {
45
57
  if (!env) return undefined;
46
58
  const resolved = {};
47
59
  for (const [key, value] of Object.entries(env)) {
48
- if (typeof value === 'string' && value.startsWith('$')) {
60
+ if (typeof value === "string" && value.startsWith("$")) {
49
61
  const varName = value.slice(1);
50
- resolved[key] = process.env[varName] ?? '';
62
+ resolved[key] = process.env[varName] ?? "";
51
63
  } else {
52
64
  resolved[key] = value;
53
65
  }
@@ -55,14 +67,24 @@ function resolveEnvValues(env) {
55
67
  return resolved;
56
68
  }
57
69
 
70
+ function isRemoteAccount(account) {
71
+ return Boolean(account.host);
72
+ }
73
+
74
+ function getRemainingLeaseMs(account, now) {
75
+ if (!account.busy || account.leasedAt === null) return 0;
76
+ return Math.max(0, LEASE_TTL_MS - (now - account.leasedAt));
77
+ }
78
+
58
79
  // ── AccountBroker ────────────────────────────────────────────────
59
80
 
60
- class AccountBroker {
81
+ class AccountBroker extends EventEmitter {
61
82
  #config;
62
83
  #state; // Map<accountId, accountState>
63
84
  #roundRobinIndex; // Map<provider, number>
64
85
 
65
86
  constructor(config) {
87
+ super();
66
88
  const parsed = ConfigSchema.parse(config);
67
89
  this.#config = parsed;
68
90
 
@@ -70,8 +92,8 @@ class AccountBroker {
70
92
  this.#roundRobinIndex = new Map();
71
93
 
72
94
  const allAccounts = [
73
- ...(parsed.codex || []).map((a) => ({ ...a, provider: 'codex' })),
74
- ...(parsed.gemini || []).map((a) => ({ ...a, provider: 'gemini' })),
95
+ ...(parsed.codex || []).map((a) => ({ ...a, provider: "codex" })),
96
+ ...(parsed.gemini || []).map((a) => ({ ...a, provider: "gemini" })),
75
97
  ];
76
98
 
77
99
  for (const account of allAccounts) {
@@ -82,22 +104,73 @@ class AccountBroker {
82
104
  profile: account.profile,
83
105
  env: account.env,
84
106
  authFile: account.authFile,
85
- tier: account.tier ?? 'unknown',
107
+ host: account.host,
108
+ tier: account.tier ?? "unknown",
86
109
  busy: false,
87
110
  leasedAt: null,
88
111
  cooldownUntil: 0,
89
- failures: 0,
112
+ // per-account circuit breaker state
113
+ failureTimestamps: [], // timestamp array for window-based decay
114
+ circuitOpenedAt: 0,
115
+ circuitTrialInFlight: false,
90
116
  lastUsedAt: 0,
91
117
  totalSessions: 0,
92
118
  });
93
119
  }
94
120
  }
95
121
 
122
+ // ── per-account circuit breaker ─────────────────────────────────
123
+
124
+ #getCircuitState(acct, now) {
125
+ const validFailures = acct.failureTimestamps.filter(
126
+ (ts) => now - ts < CIRCUIT_WINDOW_MS,
127
+ );
128
+ const withinWindow =
129
+ acct.circuitOpenedAt && now - acct.circuitOpenedAt < CIRCUIT_WINDOW_MS;
130
+ if (withinWindow) return { state: "open", failures: validFailures };
131
+ if (acct.circuitOpenedAt)
132
+ return { state: "half-open", failures: validFailures };
133
+ return { state: "closed", failures: validFailures };
134
+ }
135
+
136
+ #isCircuitBlocked(acct, now) {
137
+ const circuit = this.#getCircuitState(acct, now);
138
+ if (circuit.state === "open") return true;
139
+ if (circuit.state === "half-open" && acct.circuitTrialInFlight) return true;
140
+ return false;
141
+ }
142
+
143
+ #recordCircuitFailure(acct, isHalfOpen, now) {
144
+ const validFailures = [
145
+ ...acct.failureTimestamps.filter((ts) => now - ts < CIRCUIT_WINDOW_MS),
146
+ now,
147
+ ];
148
+ const shouldOpen =
149
+ isHalfOpen || validFailures.length >= CIRCUIT_MAX_FAILURES;
150
+ return {
151
+ failureTimestamps: validFailures,
152
+ circuitOpenedAt: shouldOpen ? now : acct.circuitOpenedAt,
153
+ circuitTrialInFlight: false,
154
+ };
155
+ }
156
+
157
+ #resetCircuit() {
158
+ return {
159
+ failureTimestamps: [],
160
+ circuitOpenedAt: 0,
161
+ circuitTrialInFlight: false,
162
+ };
163
+ }
164
+
96
165
  // ── lease TTL pruning ──────────────────────────────────────────
97
166
 
98
167
  #pruneExpiredLeases(now) {
99
168
  for (const [id, acct] of this.#state) {
100
- if (acct.busy && acct.leasedAt !== null && now - acct.leasedAt > LEASE_TTL_MS) {
169
+ if (
170
+ acct.busy &&
171
+ acct.leasedAt !== null &&
172
+ now - acct.leasedAt > LEASE_TTL_MS
173
+ ) {
101
174
  this.#state.set(id, { ...acct, busy: false, leasedAt: null });
102
175
  }
103
176
  }
@@ -105,16 +178,36 @@ class AccountBroker {
105
178
 
106
179
  // ── lease ─────────────────────────────────────────────────────
107
180
 
108
- lease({ provider }) {
181
+ lease({ provider, remote = false } = {}) {
109
182
  const now = Date.now();
110
183
  this.#pruneExpiredLeases(now);
111
184
 
112
- const accounts = [...this.#state.values()].filter((a) => a.provider === provider);
185
+ const wantsRemote = remote === true;
186
+ const accounts = [...this.#state.values()].filter(
187
+ (a) => a.provider === provider && isRemoteAccount(a) === wantsRemote,
188
+ );
113
189
  if (!accounts.length) return null;
114
190
 
115
- // group available accounts by tier, preserving insertion order within each tier
116
- const available = accounts.filter((a) => !a.busy && a.cooldownUntil <= now);
117
- if (!available.length) return null;
191
+ // filter: not busy, not in cooldown, circuit not blocked
192
+ const available = accounts.filter(
193
+ (a) =>
194
+ !a.busy && a.cooldownUntil <= now && !this.#isCircuitBlocked(a, now),
195
+ );
196
+
197
+ if (!available.length) {
198
+ // check if any accounts exist but all are blocked by circuit
199
+ const circuitBlocked = accounts.filter(
200
+ (a) =>
201
+ !a.busy && a.cooldownUntil <= now && this.#isCircuitBlocked(a, now),
202
+ );
203
+ if (circuitBlocked.length) {
204
+ this.emit("noAvailableAccounts", {
205
+ provider,
206
+ count: circuitBlocked.length,
207
+ });
208
+ }
209
+ return null;
210
+ }
118
211
 
119
212
  // sort by tier priority; stable sort preserves original order within same priority
120
213
  const sorted = [...available].sort(
@@ -125,8 +218,23 @@ class AccountBroker {
125
218
  const bestTier = sorted[0].tier;
126
219
  const sameTierAccounts = sorted.filter((a) => a.tier === bestTier);
127
220
 
221
+ // detect tier fallback
222
+ const highestTier = accounts.reduce(
223
+ (best, a) => Math.min(best, TIER_PRIORITY[a.tier] ?? 2),
224
+ Infinity,
225
+ );
226
+ if ((TIER_PRIORITY[bestTier] ?? 2) > highestTier) {
227
+ this.emit("tierFallback", {
228
+ provider,
229
+ from: Object.entries(TIER_PRIORITY).find(
230
+ ([, v]) => v === highestTier,
231
+ )?.[0],
232
+ to: bestTier,
233
+ });
234
+ }
235
+
128
236
  // use a per-provider+tier round-robin key to distribute within the tier
129
- const rrKey = `${provider}:${bestTier}`;
237
+ const rrKey = `${provider}:${bestTier}:${wantsRemote ? "remote" : "local"}`;
130
238
  const rrCurrent = this.#roundRobinIndex.get(rrKey) ?? 0;
131
239
  const tierCount = sameTierAccounts.length;
132
240
  const idx = rrCurrent % tierCount;
@@ -135,6 +243,10 @@ class AccountBroker {
135
243
  // advance round-robin index for this tier
136
244
  this.#roundRobinIndex.set(rrKey, (idx + 1) % tierCount);
137
245
 
246
+ // mark half-open trial if applicable
247
+ const circuit = this.#getCircuitState(acct, now);
248
+ const isHalfOpen = circuit.state === "half-open";
249
+
138
250
  // update state (immutable)
139
251
  this.#state.set(acct.id, {
140
252
  ...acct,
@@ -142,14 +254,45 @@ class AccountBroker {
142
254
  leasedAt: now,
143
255
  lastUsedAt: now,
144
256
  totalSessions: acct.totalSessions + 1,
257
+ circuitTrialInFlight: isHalfOpen ? true : acct.circuitTrialInFlight,
258
+ });
259
+
260
+ this.emit("lease", {
261
+ id: acct.id,
262
+ provider,
263
+ tier: acct.tier,
264
+ halfOpen: isHalfOpen,
145
265
  });
146
266
 
267
+ // path traversal guard for authFile
268
+ let authFile;
269
+ if (acct.mode === "auth") {
270
+ const resolved = join(AUTH_BASE_PATH, acct.authFile);
271
+ if (!resolved.startsWith(AUTH_BASE_PATH + sep)) {
272
+ this.emit("securityViolation", {
273
+ id: acct.id,
274
+ authFile: acct.authFile,
275
+ });
276
+ // undo the lease — path traversal blocked
277
+ this.#state.set(acct.id, {
278
+ ...this.#state.get(acct.id),
279
+ busy: false,
280
+ leasedAt: null,
281
+ });
282
+ return null;
283
+ }
284
+ authFile = resolved;
285
+ }
286
+
147
287
  return {
148
288
  id: acct.id,
149
289
  mode: acct.mode,
150
- profile: acct.mode === 'profile' ? acct.profile : undefined,
151
- env: acct.mode === 'env' ? resolveEnvValues(acct.env) : undefined,
152
- authFile: acct.mode === 'auth' ? join(AUTH_BASE_PATH, acct.authFile) : undefined,
290
+ remote: isRemoteAccount(acct),
291
+ host: acct.host,
292
+ halfOpen: isHalfOpen,
293
+ profile: acct.mode === "profile" ? acct.profile : undefined,
294
+ env: acct.mode === "env" ? resolveEnvValues(acct.env) : undefined,
295
+ authFile,
153
296
  };
154
297
  }
155
298
 
@@ -157,26 +300,45 @@ class AccountBroker {
157
300
 
158
301
  release(accountId, result) {
159
302
  const acct = this.#state.get(accountId);
160
- if (!acct) return;
303
+ if (!acct?.busy) return;
161
304
 
305
+ const now = Date.now();
162
306
  const ok = result?.ok === true;
163
- const newFailures = ok ? 0 : acct.failures + 1;
307
+ const circuit = this.#getCircuitState(acct, now);
308
+ const isHalfOpen = circuit.state === "half-open";
309
+
310
+ let circuitUpdate;
311
+ if (ok) {
312
+ circuitUpdate = this.#resetCircuit();
313
+ if (isHalfOpen) {
314
+ this.emit("circuitClose", { id: accountId });
315
+ }
316
+ } else {
317
+ circuitUpdate = this.#recordCircuitFailure(acct, isHalfOpen, now);
318
+ if (circuitUpdate.circuitOpenedAt !== acct.circuitOpenedAt) {
319
+ this.emit("circuitOpen", {
320
+ id: accountId,
321
+ failures: circuitUpdate.failureTimestamps.length,
322
+ });
323
+ }
324
+ }
325
+
164
326
  const cooldownMs = this.#config.defaults?.cooldownMs ?? DEFAULT_COOLDOWN_MS;
165
327
 
328
+ // rate-limit style cooldown: if circuit just opened, also set cooldown
329
+ const shouldCooldown =
330
+ !ok && circuitUpdate.circuitOpenedAt !== acct.circuitOpenedAt;
331
+
166
332
  const updated = {
167
333
  ...acct,
168
334
  busy: false,
169
335
  leasedAt: null,
170
- failures: newFailures,
336
+ ...circuitUpdate,
337
+ cooldownUntil: shouldCooldown ? now + cooldownMs : acct.cooldownUntil,
171
338
  };
172
339
 
173
- // consecutive failure guard: 3+ failures → auto-cooldown
174
- if (newFailures >= 3) {
175
- updated.cooldownUntil = Date.now() + cooldownMs;
176
- updated.failures = 0; // reset after cooldown triggered
177
- }
178
-
179
340
  this.#state.set(accountId, updated);
341
+ this.emit("release", { id: accountId, ok });
180
342
  }
181
343
 
182
344
  // ── markRateLimited ───────────────────────────────────────────
@@ -195,7 +357,14 @@ class AccountBroker {
195
357
  // ── snapshot ──────────────────────────────────────────────────
196
358
 
197
359
  snapshot() {
198
- return [...this.#state.values()].map((acct) => ({ ...acct }));
360
+ const now = Date.now();
361
+ this.#pruneExpiredLeases(now);
362
+ return [...this.#state.values()].map((acct) => ({
363
+ ...acct,
364
+ failureTimestamps: [...acct.failureTimestamps],
365
+ remainingMs: getRemainingLeaseMs(acct, now),
366
+ circuitState: this.#getCircuitState(acct, now).state,
367
+ }));
199
368
  }
200
369
 
201
370
  // ── nextAvailableEta ──────────────────────────────────────────
@@ -204,7 +373,9 @@ class AccountBroker {
204
373
  const now = Date.now();
205
374
  this.#pruneExpiredLeases(now);
206
375
 
207
- const accounts = [...this.#state.values()].filter((a) => a.provider === provider);
376
+ const accounts = [...this.#state.values()].filter(
377
+ (a) => a.provider === provider,
378
+ );
208
379
  if (!accounts.length) return null;
209
380
 
210
381
  // find minimum cooldownUntil among accounts that are in cooldown or busy
@@ -214,7 +385,9 @@ class AccountBroker {
214
385
  // this account is available now — no ETA needed
215
386
  return null;
216
387
  }
217
- const eta = acct.busy ? (acct.leasedAt ?? now) + LEASE_TTL_MS : acct.cooldownUntil;
388
+ const eta = acct.busy
389
+ ? (acct.leasedAt ?? now) + LEASE_TTL_MS
390
+ : acct.cooldownUntil;
218
391
  if (earliest === null || eta < earliest) {
219
392
  earliest = eta;
220
393
  }
@@ -226,11 +399,21 @@ class AccountBroker {
226
399
  // ── Config loader ────────────────────────────────────────────────
227
400
 
228
401
  function loadConfig() {
229
- const configPath = join(homedir(), '.claude', 'cache', 'tfx-hub', 'accounts.json');
402
+ const configPath = join(
403
+ homedir(),
404
+ ".claude",
405
+ "cache",
406
+ "tfx-hub",
407
+ "accounts.json",
408
+ );
230
409
  if (!existsSync(configPath)) return null;
231
410
  try {
232
- return JSON.parse(readFileSync(configPath, 'utf8'));
233
- } catch {
411
+ return JSON.parse(readFileSync(configPath, "utf8"));
412
+ } catch (err) {
413
+ console.error(
414
+ "[account-broker] Failed to parse accounts.json:",
415
+ err.message,
416
+ );
234
417
  return null;
235
418
  }
236
419
  }
@@ -242,10 +425,23 @@ function createBroker() {
242
425
  if (!config) return null;
243
426
  try {
244
427
  return new AccountBroker(config);
245
- } catch {
428
+ } catch (err) {
429
+ console.error("[account-broker] Failed to create broker:", err.message);
246
430
  return null;
247
431
  }
248
432
  }
249
433
 
250
- export const broker = createBroker();
251
- export { AccountBroker };
434
+ /** Re-read config and replace the module-level singleton. ESM live binding propagates to all importers. */
435
+ function reloadBroker() {
436
+ const config = loadConfig();
437
+ if (!config) return { ok: false, error: "Config not found or invalid" };
438
+ try {
439
+ broker = new AccountBroker(config);
440
+ return { ok: true, broker };
441
+ } catch (err) {
442
+ return { ok: false, error: err.message };
443
+ }
444
+ }
445
+
446
+ export let broker = createBroker();
447
+ export { AccountBroker, reloadBroker };