triflux 10.3.4 → 10.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -1,25 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { execSync } from "node:child_process";
3
4
  import {
4
5
  existsSync,
5
6
  mkdirSync,
6
- readFileSync,
7
7
  readdirSync,
8
+ readFileSync,
8
9
  statSync,
9
10
  writeFileSync,
10
11
  } from "node:fs";
11
- import { execSync } from "node:child_process";
12
- import { join, basename, dirname } from "node:path";
13
12
  import { homedir } from "node:os";
13
+ import { basename, dirname, join } from "node:path";
14
14
  import { fileURLToPath } from "node:url";
15
-
15
+ import {
16
+ checkCliSync,
17
+ checkHub,
18
+ detectCodexAuthState,
19
+ } from "./lib/env-probe.mjs";
20
+ import {
21
+ MCP_SERVER_DOMAIN_TAGS,
22
+ SEARCH_SERVER_ORDER,
23
+ } from "./lib/mcp-server-catalog.mjs";
16
24
  import { readPreflightCache } from "./preflight-cache.mjs";
17
- import { checkCliSync, checkHub, detectCodexAuthState } from "./lib/env-probe.mjs";
18
- import { SEARCH_SERVER_ORDER, MCP_SERVER_DOMAIN_TAGS } from "./lib/mcp-server-catalog.mjs";
19
25
 
20
26
  export const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000;
21
27
  const WARMUP_METADATA_FILE = ["state", "warmup-metadata.json"];
22
- const AUTH_SENSITIVE_TARGETS = new Set(["codexSkills", "tierEnvironment", "searchEngines"]);
28
+ const AUTH_SENSITIVE_TARGETS = new Set([
29
+ "codexSkills",
30
+ "tierEnvironment",
31
+ "searchEngines",
32
+ ]);
23
33
 
24
34
  export const CACHE_TARGETS = Object.freeze({
25
35
  codexSkills: Object.freeze({
@@ -118,7 +128,7 @@ function isFresh(target, options = {}) {
118
128
  const ttlMs = resolveTtlMs(target, options);
119
129
  const now = options.now ?? Date.now();
120
130
  const stat = statSync(filePath);
121
- return ttlMs > 0 && (now - stat.mtimeMs) < ttlMs;
131
+ return ttlMs > 0 && now - stat.mtimeMs < ttlMs;
122
132
  } catch {
123
133
  return false;
124
134
  }
@@ -138,7 +148,8 @@ function parseSkillFrontmatter(content) {
138
148
 
139
149
  const frontmatter = match[1];
140
150
  const name = frontmatter.match(/^name:\s*(.+)$/m)?.[1]?.trim() || null;
141
- const description = frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim() || null;
151
+ const description =
152
+ frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim() || null;
142
153
  return name ? { name, description } : null;
143
154
  }
144
155
 
@@ -179,10 +190,18 @@ export function scanCodexSkills(options = {}) {
179
190
  }
180
191
 
181
192
  const builtinSkills = [
182
- { name: "web-clone", role: "general", description: "Clone and analyze web pages" },
193
+ {
194
+ name: "web-clone",
195
+ role: "general",
196
+ description: "Clone and analyze web pages",
197
+ },
183
198
  { name: "help", role: "general", description: "Show available commands" },
184
199
  { name: "note", role: "general", description: "Save notes during session" },
185
- { name: "worker", role: "auto", description: "Spawn background worker for tasks" },
200
+ {
201
+ name: "worker",
202
+ role: "auto",
203
+ description: "Spawn background worker for tasks",
204
+ },
186
205
  ];
187
206
 
188
207
  for (const builtin of builtinSkills) {
@@ -207,15 +226,21 @@ export function probeTierEnvironment(options = {}) {
207
226
  const execSyncFn = options.execSyncFn || execSync;
208
227
  const codexAuth = preflight?.codex_plan ?? detectCodexAuthState({ homeDir });
209
228
 
210
- const codexCheck = preflight?.codex || checkCliSync("codex", { whichCommandFn: options.whichCommandFn });
211
- const geminiCheck = preflight?.gemini || checkCliSync("gemini", { whichCommandFn: options.whichCommandFn });
212
- const hubCheck = preflight?.hub || checkHub({
213
- pkgRoot: options.pkgRoot,
214
- restart: options.hubRestart === true,
215
- requestTimeoutMs: options.hubTimeoutMs ?? 1000,
216
- pollAttempts: options.hubRestart === true ? 8 : 0,
217
- execSyncFn,
218
- });
229
+ const codexCheck =
230
+ preflight?.codex ||
231
+ checkCliSync("codex", { whichCommandFn: options.whichCommandFn });
232
+ const geminiCheck =
233
+ preflight?.gemini ||
234
+ checkCliSync("gemini", { whichCommandFn: options.whichCommandFn });
235
+ const hubCheck =
236
+ preflight?.hub ||
237
+ checkHub({
238
+ pkgRoot: options.pkgRoot,
239
+ restart: options.hubRestart === true,
240
+ requestTimeoutMs: options.hubTimeoutMs ?? 1000,
241
+ pollAttempts: options.hubRestart === true ? 8 : 0,
242
+ execSyncFn,
243
+ });
219
244
  const checks = {
220
245
  psmux: false,
221
246
  hub: !!hubCheck?.ok,
@@ -246,7 +271,8 @@ export function probeTierEnvironment(options = {}) {
246
271
 
247
272
  let tier = "minimal";
248
273
  if (checks.codex || checks.gemini) tier = "standard";
249
- if (checks.psmux && checks.hub && (checks.codex || checks.gemini)) tier = "full";
274
+ if (checks.psmux && checks.hub && (checks.codex || checks.gemini))
275
+ tier = "full";
250
276
 
251
277
  const agents = ["claude"];
252
278
  if (checks.codex) agents.push("codex");
@@ -257,9 +283,10 @@ export function probeTierEnvironment(options = {}) {
257
283
  tier,
258
284
  checks,
259
285
  available_agents: agents,
260
- codex_plan: codexAuth.source == null
261
- ? { plan: codexAuth.plan }
262
- : { plan: codexAuth.plan, source: codexAuth.source },
286
+ codex_plan:
287
+ codexAuth.source == null
288
+ ? { plan: codexAuth.plan }
289
+ : { plan: codexAuth.plan, source: codexAuth.source },
263
290
  source: {
264
291
  preflight: !!preflight,
265
292
  home_dir: homeDir,
@@ -272,13 +299,15 @@ function getCodexAuthFingerprint(options = {}) {
272
299
  if (typeof options.preflight?.codex_plan?.fingerprint === "string") {
273
300
  return options.preflight.codex_plan.fingerprint;
274
301
  }
275
- return detectCodexAuthState({ homeDir: resolveHomeDir(options.homeDir) }).fingerprint;
302
+ return detectCodexAuthState({ homeDir: resolveHomeDir(options.homeDir) })
303
+ .fingerprint;
276
304
  }
277
305
 
278
306
  function hasAuthFingerprintChanged(target, options = {}) {
279
307
  if (!AUTH_SENSITIVE_TARGETS.has(target)) return false;
280
308
  const nextFingerprint = getCodexAuthFingerprint(options);
281
- const previousFingerprint = readWarmupMetadata(options)?.codex_auth_fingerprint || null;
309
+ const previousFingerprint =
310
+ readWarmupMetadata(options)?.codex_auth_fingerprint || null;
282
311
  if (previousFingerprint === null) return false;
283
312
  return previousFingerprint !== nextFingerprint;
284
313
  }
@@ -314,7 +343,10 @@ export function extractProjectMeta(options = {}) {
314
343
  testCmd = pkg.scripts?.test || null;
315
344
  lang = "JavaScript/ESM (Node.js)";
316
345
  } catch {}
317
- } else if (existsSync(join(cwd, "pyproject.toml")) || existsSync(join(cwd, "setup.py"))) {
346
+ } else if (
347
+ existsSync(join(cwd, "pyproject.toml")) ||
348
+ existsSync(join(cwd, "setup.py"))
349
+ ) {
318
350
  lang = "Python";
319
351
  } else if (existsSync(join(cwd, "Cargo.toml"))) {
320
352
  lang = "Rust";
@@ -333,7 +365,12 @@ export function extractProjectMeta(options = {}) {
333
365
  }
334
366
 
335
367
  function loadMcpInventory(options = {}) {
336
- const inventoryPath = join(resolveHomeDir(options.homeDir), ".claude", "cache", "mcp-inventory.json");
368
+ const inventoryPath = join(
369
+ resolveHomeDir(options.homeDir),
370
+ ".claude",
371
+ "cache",
372
+ "mcp-inventory.json",
373
+ );
337
374
  try {
338
375
  return JSON.parse(readFileSync(inventoryPath, "utf8"));
339
376
  } catch {
@@ -372,7 +409,8 @@ function loadMcpConfigs(options = {}) {
372
409
 
373
410
  export function checkSearchEngines(options = {}) {
374
411
  const inventory = options.inventory ?? loadMcpInventory(options);
375
- const configuredServers = options.configuredServers ?? loadMcpConfigs(options);
412
+ const configuredServers =
413
+ options.configuredServers ?? loadMcpConfigs(options);
376
414
  const engines = [];
377
415
 
378
416
  const knownSearchServers = [...SEARCH_SERVER_ORDER, "context7"];
@@ -383,8 +421,13 @@ export function checkSearchEngines(options = {}) {
383
421
  const scopeData = inventory[scope];
384
422
  if (!scopeData?.servers) continue;
385
423
  for (const server of scopeData.servers) {
386
- const tags = server.domain_tags || MCP_SERVER_DOMAIN_TAGS[server.name] || [];
387
- if (tags.includes("search") || tags.includes("web") || tags.includes("research")) {
424
+ const tags =
425
+ server.domain_tags || MCP_SERVER_DOMAIN_TAGS[server.name] || [];
426
+ if (
427
+ tags.includes("search") ||
428
+ tags.includes("web") ||
429
+ tags.includes("research")
430
+ ) {
388
431
  allServerNames.add(server.name);
389
432
  }
390
433
  }
@@ -393,7 +436,11 @@ export function checkSearchEngines(options = {}) {
393
436
 
394
437
  for (const name of Object.keys(configuredServers)) {
395
438
  const tags = MCP_SERVER_DOMAIN_TAGS[name] || [];
396
- if (tags.includes("search") || tags.includes("web") || knownSearchServers.includes(name)) {
439
+ if (
440
+ tags.includes("search") ||
441
+ tags.includes("web") ||
442
+ knownSearchServers.includes(name)
443
+ ) {
397
444
  allServerNames.add(name);
398
445
  }
399
446
  }
@@ -405,7 +452,9 @@ export function checkSearchEngines(options = {}) {
405
452
  let inventoryStatus = null;
406
453
  if (inventory) {
407
454
  for (const scope of ["codex", "gemini", "claude"]) {
408
- const server = inventory[scope]?.servers?.find((item) => item.name === name);
455
+ const server = inventory[scope]?.servers?.find(
456
+ (item) => item.name === name,
457
+ );
409
458
  if (server) {
410
459
  inventoryStatus = {
411
460
  scope,
@@ -418,7 +467,10 @@ export function checkSearchEngines(options = {}) {
418
467
  }
419
468
 
420
469
  let status = "unavailable";
421
- if (inventoryStatus?.status === "enabled" || inventoryStatus?.status === "configured") {
470
+ if (
471
+ inventoryStatus?.status === "enabled" ||
472
+ inventoryStatus?.status === "configured"
473
+ ) {
422
474
  status = "available";
423
475
  } else if (configured) {
424
476
  status = "configured";
@@ -456,13 +508,18 @@ export function checkSearchEngines(options = {}) {
456
508
 
457
509
  function buildTarget(target, options = {}) {
458
510
  const filePath = resolveTargetPath(target, options);
459
- if (!options.force && isFresh(target, options) && !hasAuthFingerprintChanged(target, options)) {
511
+ if (
512
+ !options.force &&
513
+ isFresh(target, options) &&
514
+ !hasAuthFingerprintChanged(target, options)
515
+ ) {
460
516
  return { target, status: "skipped", file: filePath, reason: "fresh" };
461
517
  }
462
518
 
463
519
  let payload;
464
520
  if (target === "codexSkills") payload = scanCodexSkills(options);
465
- else if (target === "tierEnvironment") payload = probeTierEnvironment(options);
521
+ else if (target === "tierEnvironment")
522
+ payload = probeTierEnvironment(options);
466
523
  else if (target === "projectMeta") payload = extractProjectMeta(options);
467
524
  else if (target === "searchEngines") payload = checkSearchEngines(options);
468
525
  else throw new Error(`unknown cache target: ${target}`);
@@ -508,7 +565,9 @@ export function buildAll(options = {}) {
508
565
  }
509
566
 
510
567
  const built = results.filter((result) => result.status === "built").length;
511
- const skipped = results.filter((result) => result.status === "skipped").length;
568
+ const skipped = results.filter(
569
+ (result) => result.status === "skipped",
570
+ ).length;
512
571
  const failed = results.filter((result) => result.status === "failed").length;
513
572
  const authFingerprint = getCodexAuthFingerprint(options);
514
573
 
@@ -1,8 +1,8 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
2
3
  import { join, resolve } from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
- import { homedir } from "node:os";
5
- import { TFX_START, OMC_END, writeSection } from "./lib/claudemd-scanner.mjs";
5
+ import { OMC_END, TFX_START, writeSection } from "./lib/claudemd-scanner.mjs";
6
6
 
7
7
  const PKG_ROOT = fileURLToPath(new URL("..", import.meta.url));
8
8
  const GLOBAL_CLAUDE_MD_PATH = join(homedir(), ".claude", "CLAUDE.md");
@@ -29,7 +29,10 @@ function findRoutingSection(markdown) {
29
29
  }
30
30
 
31
31
  // Legacy heading fallback
32
- const headingPattern = new RegExp(`(^|\\n)${ROUTING_SECTION_HEADING.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}(?=\\n|$)`, "u");
32
+ const headingPattern = new RegExp(
33
+ `(^|\\n)${ROUTING_SECTION_HEADING.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}(?=\\n|$)`,
34
+ "u",
35
+ );
33
36
  const match = headingPattern.exec(content);
34
37
 
35
38
  if (!match) {
@@ -37,8 +40,12 @@ function findRoutingSection(markdown) {
37
40
  }
38
41
 
39
42
  const startIndex = match.index + match[1].length;
40
- const nextHeadingIndex = content.indexOf("\n## ", startIndex + ROUTING_SECTION_HEADING.length);
41
- const endIndex = nextHeadingIndex === -1 ? content.length : nextHeadingIndex + 1;
43
+ const nextHeadingIndex = content.indexOf(
44
+ "\n## ",
45
+ startIndex + ROUTING_SECTION_HEADING.length,
46
+ );
47
+ const endIndex =
48
+ nextHeadingIndex === -1 ? content.length : nextHeadingIndex + 1;
42
49
 
43
50
  return {
44
51
  found: true,
@@ -66,7 +73,11 @@ function buildNextMarkdown(currentMarkdown, routingSection) {
66
73
  return nextSection;
67
74
  }
68
75
 
69
- const separator = current.endsWith("\n\n") ? "" : current.endsWith("\n") ? "\n" : "\n\n";
76
+ const separator = current.endsWith("\n\n")
77
+ ? ""
78
+ : current.endsWith("\n")
79
+ ? "\n"
80
+ : "\n\n";
70
81
  return `${current}${separator}${nextSection}`;
71
82
  }
72
83
 
@@ -82,7 +93,9 @@ export function getLatestRoutingTable() {
82
93
  if (section.found) return section.section.trim();
83
94
  }
84
95
  // 2차 fallback: 패키지 CLAUDE.md도 없으면 에러
85
- throw new Error(`routing section not found in: ${GLOBAL_CLAUDE_MD_PATH} or ${PKG_CLAUDE_MD_PATH}`);
96
+ throw new Error(
97
+ `routing section not found in: ${GLOBAL_CLAUDE_MD_PATH} or ${PKG_CLAUDE_MD_PATH}`,
98
+ );
86
99
  }
87
100
 
88
101
  export function ensureTfxSection(claudeMdPath, routingTable) {
@@ -132,15 +145,12 @@ export function ensureTfxCrown(claudeMdPath, options = {}) {
132
145
  }
133
146
 
134
147
  export function ensureGlobalClaudeRoutingSection(claudeDir) {
148
+ // routing은 프로젝트 CLAUDE.md에만 유지. global 중복 주입 중단.
135
149
  const claudeMdPath = join(claudeDir, "CLAUDE.md");
136
- if (!existsSync(claudeMdPath)) {
137
- return toSkippedResult(claudeMdPath, "missing_file");
138
- }
139
-
140
- try {
141
- return ensureTfxSection(claudeMdPath, getLatestRoutingTable());
142
- } catch (error) {
143
- const reason = error instanceof Error ? error.message : "routing_table_unavailable";
144
- return toSkippedResult(claudeMdPath, reason);
145
- }
150
+ return {
151
+ action: "unchanged",
152
+ path: claudeMdPath,
153
+ skipped: true,
154
+ reason: "global_sync_disabled",
155
+ };
146
156
  }
@@ -4,10 +4,10 @@
4
4
  // gateway가 alive인지 확인하고 죽었으면 기동한다.
5
5
  // 실제 MCP 기능은 없음 (no-op). 역할은 gateway 보장뿐.
6
6
 
7
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
8
- import { join, dirname } from 'node:path';
9
- import { fileURLToPath } from 'node:url';
10
- import { tmpdir } from 'node:os';
7
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { dirname, join } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
11
 
12
12
  const PLUGIN_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
13
13
  const PROBE_PORT = 8100;
@@ -15,21 +15,25 @@ const PROBE_TIMEOUT_MS = 2000;
15
15
  const STARTUP_WAIT_MS = 6000;
16
16
  const POLL_MS = 500;
17
17
  const CACHE_TTL_MS = 5 * 60 * 1000; // 5분
18
- const CACHE_FILE = join(tmpdir(), 'tfx-gateway-alive.json');
18
+ const CACHE_FILE = join(tmpdir(), "tfx-gateway-alive.json");
19
19
 
20
20
  function readCache() {
21
21
  try {
22
22
  if (!existsSync(CACHE_FILE)) return null;
23
- const data = JSON.parse(readFileSync(CACHE_FILE, 'utf8'));
23
+ const data = JSON.parse(readFileSync(CACHE_FILE, "utf8"));
24
24
  if (Date.now() - data.ts < CACHE_TTL_MS) return data;
25
- } catch { /* ignore */ }
25
+ } catch {
26
+ /* ignore */
27
+ }
26
28
  return null;
27
29
  }
28
30
 
29
31
  function writeCache() {
30
32
  try {
31
33
  writeFileSync(CACHE_FILE, JSON.stringify({ ts: Date.now(), alive: true }));
32
- } catch { /* ignore */ }
34
+ } catch {
35
+ /* ignore */
36
+ }
33
37
  }
34
38
 
35
39
  async function isGatewayAlive() {
@@ -41,7 +45,10 @@ async function isGatewayAlive() {
41
45
  const res = await fetch(`http://127.0.0.1:${PROBE_PORT}/healthz`, {
42
46
  signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
43
47
  });
44
- if (res.ok) { writeCache(); return true; }
48
+ if (res.ok) {
49
+ writeCache();
50
+ return true;
51
+ }
45
52
  return false;
46
53
  } catch {
47
54
  return false;
@@ -49,16 +56,18 @@ async function isGatewayAlive() {
49
56
  }
50
57
 
51
58
  async function startGateway() {
52
- const script = join(PLUGIN_ROOT, 'scripts', 'mcp-gateway-start.mjs');
59
+ const script = join(PLUGIN_ROOT, "scripts", "mcp-gateway-start.mjs");
53
60
  if (!existsSync(script)) {
54
- process.stderr.write('[gateway-preflight] mcp-gateway-start.mjs not found\n');
61
+ process.stderr.write(
62
+ "[gateway-preflight] mcp-gateway-start.mjs not found\n",
63
+ );
55
64
  return false;
56
65
  }
57
66
 
58
- const { execSync } = await import('node:child_process');
67
+ const { execSync } = await import("node:child_process");
59
68
  try {
60
69
  execSync(`node "${script}"`, {
61
- stdio: 'ignore',
70
+ stdio: "ignore",
62
71
  timeout: STARTUP_WAIT_MS + 2000,
63
72
  cwd: PLUGIN_ROOT,
64
73
  });
@@ -81,53 +90,59 @@ async function main() {
81
90
  // 1) gateway 보장
82
91
  const alive = await isGatewayAlive();
83
92
  if (!alive) {
84
- process.stderr.write('[gateway-preflight] gateway down, starting...\n');
93
+ process.stderr.write("[gateway-preflight] gateway down, starting...\n");
85
94
  const ok = await startGateway();
86
- process.stderr.write(ok
87
- ? '[gateway-preflight] gateway started\n'
88
- : '[gateway-preflight] gateway start failed\n');
95
+ process.stderr.write(
96
+ ok
97
+ ? "[gateway-preflight] gateway started\n"
98
+ : "[gateway-preflight] gateway start failed\n",
99
+ );
89
100
  }
90
101
 
91
102
  // 2) MCP JSON-RPC stdio — initialize 핸드셰이크만 처리하고 idle 유지
92
- const { createInterface } = await import('node:readline');
103
+ const { createInterface } = await import("node:readline");
93
104
  const rl = createInterface({ input: process.stdin });
94
105
 
95
- rl.on('line', (line) => {
106
+ rl.on("line", (line) => {
96
107
  try {
97
108
  const msg = JSON.parse(line);
98
- if (msg.method === 'initialize') {
109
+ if (msg.method === "initialize") {
99
110
  const response = {
100
- jsonrpc: '2.0',
111
+ jsonrpc: "2.0",
101
112
  id: msg.id,
102
113
  result: {
103
- protocolVersion: '2024-11-05',
114
+ protocolVersion: "2024-11-05",
104
115
  capabilities: {},
105
- serverInfo: { name: 'tfx-gateway-preflight', version: '1.0.0' },
116
+ serverInfo: { name: "tfx-gateway-preflight", version: "1.0.0" },
106
117
  },
107
118
  };
108
- process.stdout.write(JSON.stringify(response) + '\n');
109
- } else if (msg.method === 'notifications/initialized') {
119
+ process.stdout.write(JSON.stringify(response) + "\n");
120
+ } else if (msg.method === "notifications/initialized") {
110
121
  // ack, no response needed
111
- } else if (msg.method === 'tools/list') {
112
- process.stdout.write(JSON.stringify({
113
- jsonrpc: '2.0',
114
- id: msg.id,
115
- result: { tools: [] },
116
- }) + '\n');
122
+ } else if (msg.method === "tools/list") {
123
+ process.stdout.write(
124
+ JSON.stringify({
125
+ jsonrpc: "2.0",
126
+ id: msg.id,
127
+ result: { tools: [] },
128
+ }) + "\n",
129
+ );
117
130
  } else if (msg.id !== undefined) {
118
131
  // unknown method with id — respond empty
119
- process.stdout.write(JSON.stringify({
120
- jsonrpc: '2.0',
121
- id: msg.id,
122
- result: {},
123
- }) + '\n');
132
+ process.stdout.write(
133
+ JSON.stringify({
134
+ jsonrpc: "2.0",
135
+ id: msg.id,
136
+ result: {},
137
+ }) + "\n",
138
+ );
124
139
  }
125
140
  } catch {
126
141
  // ignore parse errors
127
142
  }
128
143
  });
129
144
 
130
- rl.on('close', () => process.exit(0));
145
+ rl.on("close", () => process.exit(0));
131
146
  }
132
147
 
133
148
  main();