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
package/hub/intent.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  // hub/intent.mjs — Intent Classification Engine
2
2
  // 사용자 요청의 "진짜 의도"를 분석 → 카테고리 분류 → 최적 에이전트/모델 자동 선택
3
3
 
4
- import { execFileSync } from 'node:child_process';
5
- import crypto from 'node:crypto';
6
- import { whichCommand } from './platform.mjs';
4
+ import { execFileSync } from "node:child_process";
5
+ import crypto from "node:crypto";
6
+ import { whichCommand } from "./platform.mjs";
7
7
 
8
8
  /** 캐시 엔트리: { category, confidence, ts } */
9
9
  const _intentCache = new Map();
@@ -15,12 +15,12 @@ let _codexAvailable = null;
15
15
  /** @experimental 런타임 미연결 — 향후 통합 예정 */
16
16
  function _isCodexAvailable() {
17
17
  if (_codexAvailable !== null) return _codexAvailable;
18
- _codexAvailable = Boolean(whichCommand('codex'));
18
+ _codexAvailable = Boolean(whichCommand("codex"));
19
19
  return _codexAvailable;
20
20
  }
21
21
 
22
22
  function _promptHash(prompt) {
23
- return crypto.createHash('md5').update(prompt).digest('hex');
23
+ return crypto.createHash("md5").update(prompt).digest("hex");
24
24
  }
25
25
 
26
26
  function _getCached(hash) {
@@ -36,16 +36,17 @@ function _getCached(hash) {
36
36
  function _tryCodexClassify(prompt) {
37
37
  try {
38
38
  const raw = execFileSync(
39
- 'codex',
40
- ['exec', `Classify intent: ${prompt}. Reply JSON: {intent, confidence}`],
41
- { timeout: 8000, encoding: 'utf8' }
39
+ "codex",
40
+ ["exec", `Classify intent: ${prompt}. Reply JSON: {intent, confidence}`],
41
+ { timeout: 8000, encoding: "utf8" },
42
42
  );
43
43
  // JSON 블록 추출 (응답에 다른 텍스트가 섞일 수 있음)
44
44
  const match = raw.match(/\{[\s\S]*?\}/);
45
45
  if (!match) return null;
46
46
  const parsed = JSON.parse(match[0]);
47
- const intent = typeof parsed.intent === 'string' ? parsed.intent : null;
48
- const confidence = typeof parsed.confidence === 'number' ? parsed.confidence : null;
47
+ const intent = typeof parsed.intent === "string" ? parsed.intent : null;
48
+ const confidence =
49
+ typeof parsed.confidence === "number" ? parsed.confidence : null;
49
50
  if (!intent || confidence === null) return null;
50
51
  // intent가 알려진 카테고리여야 함
51
52
  if (!INTENT_CATEGORIES[intent]) return null;
@@ -60,30 +61,144 @@ function _tryCodexClassify(prompt) {
60
61
  * triflux 특화 의도 카테고리 (10개)
61
62
  */
62
63
  export const INTENT_CATEGORIES = {
63
- implement: { agent: 'executor', mcp: 'implement', effort: 'codex53_high' },
64
- debug: { agent: 'debugger', mcp: 'implement', effort: 'codex53_high' },
65
- analyze: { agent: 'analyst', mcp: 'analyze', effort: 'gpt54_xhigh' },
66
- design: { agent: 'architect', mcp: 'analyze', effort: 'gpt54_xhigh' },
67
- review: { agent: 'code-reviewer', mcp: 'review', effort: 'codex53_high' },
68
- document: { agent: 'writer', mcp: 'docs', effort: 'pro' },
69
- research: { agent: 'scientist', mcp: 'analyze', effort: 'codex53_high' },
70
- 'quick-fix':{ agent: 'build-fixer', mcp: 'implement', effort: 'codex53_low' },
71
- explain: { agent: 'writer', mcp: 'docs', effort: 'flash' },
72
- test: { agent: 'test-engineer', mcp: null, effort: null },
64
+ implement: { agent: "executor", mcp: "implement", effort: "codex53_high" },
65
+ debug: { agent: "debugger", mcp: "implement", effort: "codex53_high" },
66
+ analyze: { agent: "analyst", mcp: "analyze", effort: "gpt54_xhigh" },
67
+ design: { agent: "architect", mcp: "analyze", effort: "gpt54_xhigh" },
68
+ review: { agent: "code-reviewer", mcp: "review", effort: "codex53_high" },
69
+ document: { agent: "writer", mcp: "docs", effort: "pro" },
70
+ research: { agent: "scientist", mcp: "analyze", effort: "codex53_high" },
71
+ "quick-fix": {
72
+ agent: "build-fixer",
73
+ mcp: "implement",
74
+ effort: "codex53_low",
75
+ },
76
+ explain: { agent: "writer", mcp: "docs", effort: "flash" },
77
+ test: { agent: "test-engineer", mcp: null, effort: null },
73
78
  };
74
79
 
75
80
  /** @internal 키워드 → 카테고리 매핑 패턴 */
76
81
  const KEYWORD_PATTERNS = [
77
- { category: 'implement', keywords: ['구현', '만들', '추가', '생성', '작성', '빌드', 'implement', 'create', 'add', 'build', 'make', 'develop'], weight: 1.0 },
78
- { category: 'debug', keywords: ['버그', '에러', '오류', '고쳐', '수정', '디버그', 'fix', 'bug', 'error', 'debug', 'troubleshoot', 'crash', 'broken'], weight: 1.0 },
79
- { category: 'analyze', keywords: ['분석', '조사', '파악', 'analyze', 'investigate', 'examine', 'inspect'], weight: 0.9 },
80
- { category: 'design', keywords: ['설계', '아키텍처', '디자인', '구조', 'design', 'architect', 'structure'], weight: 0.9 },
81
- { category: 'review', keywords: ['리뷰', '검토', '코드리뷰', 'review', 'code review', 'audit'], weight: 1.0 },
82
- { category: 'document', keywords: ['문서', '도큐먼트', '문서화', 'document', 'docs', 'documentation', 'readme'], weight: 0.9 },
83
- { category: 'research', keywords: ['리서치', '연구', '탐색', 'research', 'explore', 'study'], weight: 0.8 },
84
- { category: 'quick-fix', keywords: ['빠르게', '간단히', '급한', 'quick fix', 'hotfix', 'quick'], weight: 0.85 },
85
- { category: 'explain', keywords: ['설명', '뭐야', '알려', '이해', 'explain', 'what is', 'how does', 'tell me', 'describe'], weight: 1.0 },
86
- { category: 'test', keywords: ['테스트', '테스팅', '시험', '검증', 'test', 'testing', 'spec', 'unit test'], weight: 1.0 },
82
+ {
83
+ category: "implement",
84
+ keywords: [
85
+ "구현",
86
+ "만들",
87
+ "추가",
88
+ "생성",
89
+ "작성",
90
+ "빌드",
91
+ "implement",
92
+ "create",
93
+ "add",
94
+ "build",
95
+ "make",
96
+ "develop",
97
+ ],
98
+ weight: 1.0,
99
+ },
100
+ {
101
+ category: "debug",
102
+ keywords: [
103
+ "버그",
104
+ "에러",
105
+ "오류",
106
+ "고쳐",
107
+ "수정",
108
+ "디버그",
109
+ "fix",
110
+ "bug",
111
+ "error",
112
+ "debug",
113
+ "troubleshoot",
114
+ "crash",
115
+ "broken",
116
+ ],
117
+ weight: 1.0,
118
+ },
119
+ {
120
+ category: "analyze",
121
+ keywords: [
122
+ "분석",
123
+ "조사",
124
+ "파악",
125
+ "analyze",
126
+ "investigate",
127
+ "examine",
128
+ "inspect",
129
+ ],
130
+ weight: 0.9,
131
+ },
132
+ {
133
+ category: "design",
134
+ keywords: [
135
+ "설계",
136
+ "아키텍처",
137
+ "디자인",
138
+ "구조",
139
+ "design",
140
+ "architect",
141
+ "structure",
142
+ ],
143
+ weight: 0.9,
144
+ },
145
+ {
146
+ category: "review",
147
+ keywords: ["리뷰", "검토", "코드리뷰", "review", "code review", "audit"],
148
+ weight: 1.0,
149
+ },
150
+ {
151
+ category: "document",
152
+ keywords: [
153
+ "문서",
154
+ "도큐먼트",
155
+ "문서화",
156
+ "document",
157
+ "docs",
158
+ "documentation",
159
+ "readme",
160
+ ],
161
+ weight: 0.9,
162
+ },
163
+ {
164
+ category: "research",
165
+ keywords: ["리서치", "연구", "탐색", "research", "explore", "study"],
166
+ weight: 0.8,
167
+ },
168
+ {
169
+ category: "quick-fix",
170
+ keywords: ["빠르게", "간단히", "급한", "quick fix", "hotfix", "quick"],
171
+ weight: 0.85,
172
+ },
173
+ {
174
+ category: "explain",
175
+ keywords: [
176
+ "설명",
177
+ "뭐야",
178
+ "알려",
179
+ "이해",
180
+ "explain",
181
+ "what is",
182
+ "how does",
183
+ "tell me",
184
+ "describe",
185
+ ],
186
+ weight: 1.0,
187
+ },
188
+ {
189
+ category: "test",
190
+ keywords: [
191
+ "테스트",
192
+ "테스팅",
193
+ "시험",
194
+ "검증",
195
+ "test",
196
+ "testing",
197
+ "spec",
198
+ "unit test",
199
+ ],
200
+ weight: 1.0,
201
+ },
87
202
  ];
88
203
 
89
204
  /**
@@ -93,8 +208,8 @@ const KEYWORD_PATTERNS = [
93
208
  * @returns {{ category: string, confidence: number }}
94
209
  */
95
210
  export function quickClassify(prompt) {
96
- if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
97
- return { category: 'implement', confidence: 0.1 };
211
+ if (!prompt || typeof prompt !== "string" || !prompt.trim()) {
212
+ return { category: "implement", confidence: 0.1 };
98
213
  }
99
214
 
100
215
  const lower = prompt.toLowerCase().trim();
@@ -118,11 +233,14 @@ export function quickClassify(prompt) {
118
233
  }
119
234
 
120
235
  if (!bestCategory) {
121
- return { category: 'implement', confidence: 0.3 };
236
+ return { category: "implement", confidence: 0.3 };
122
237
  }
123
238
 
124
239
  // 매칭 품질 기반 신뢰도 (0.5~0.95 범위) — matchCount 기준으로 정규화 (3개 매칭이면 최대)
125
- const confidence = Math.min(0.95, 0.5 + (Math.min(bestMatchCount, 3) / 3) * 0.45);
240
+ const confidence = Math.min(
241
+ 0.95,
242
+ 0.5 + (Math.min(bestMatchCount, 3) / 3) * 0.45,
243
+ );
126
244
  return { category: bestCategory, confidence };
127
245
  }
128
246
 
@@ -135,17 +253,22 @@ export function quickClassify(prompt) {
135
253
  * @returns {{ category: string, confidence: number, reasoning: string, routing: { agent: string, mcp: string|null, effort: string|null } }}
136
254
  */
137
255
  export function classifyIntent(prompt) {
138
- const hash = _promptHash(String(prompt ?? ''));
256
+ const hash = _promptHash(String(prompt ?? ""));
139
257
 
140
258
  // 캐시 확인
141
259
  const cached = _getCached(hash);
142
260
  if (cached) {
143
- const routing = INTENT_CATEGORIES[cached.category] || INTENT_CATEGORIES.implement;
261
+ const routing =
262
+ INTENT_CATEGORIES[cached.category] || INTENT_CATEGORIES.implement;
144
263
  return {
145
264
  category: cached.category,
146
265
  confidence: cached.confidence,
147
266
  reasoning: `cache-hit: ${cached.category} (${cached.confidence.toFixed(2)})`,
148
- routing: { agent: routing.agent, mcp: routing.mcp, effort: routing.effort },
267
+ routing: {
268
+ agent: routing.agent,
269
+ mcp: routing.mcp,
270
+ effort: routing.effort,
271
+ },
149
272
  };
150
273
  }
151
274
 
@@ -161,7 +284,7 @@ export function classifyIntent(prompt) {
161
284
  reasoning = `keyword-match: ${category} (${confidence.toFixed(2)})`;
162
285
  } else if (_isCodexAvailable()) {
163
286
  // Codex triage
164
- const codexResult = _tryCodexClassify(String(prompt ?? ''));
287
+ const codexResult = _tryCodexClassify(String(prompt ?? ""));
165
288
  if (codexResult && codexResult.confidence > 0.8) {
166
289
  category = codexResult.category;
167
290
  confidence = codexResult.confidence;
@@ -1,4 +1,10 @@
1
- import { accessSync, constants, existsSync, readFileSync, readdirSync } from "node:fs";
1
+ import {
2
+ accessSync,
3
+ constants,
4
+ existsSync,
5
+ readdirSync,
6
+ readFileSync,
7
+ } from "node:fs";
2
8
  import http from "node:http";
3
9
  import https from "node:https";
4
10
  import { basename, join, relative } from "node:path";
@@ -7,7 +13,10 @@ const NETWORK_TIMEOUT_MS = 3_000;
7
13
 
8
14
  function toIssuePath(cacheDir, filePath) {
9
15
  const relPath = relative(cacheDir, filePath);
10
- return (relPath && relPath.length > 0 ? relPath : basename(filePath)).replace(/\\/g, "/");
16
+ return (relPath && relPath.length > 0 ? relPath : basename(filePath)).replace(
17
+ /\\/g,
18
+ "/",
19
+ );
11
20
  }
12
21
 
13
22
  function collectCacheFiles(cacheDir) {
@@ -45,19 +54,28 @@ function probeUrl(url) {
45
54
 
46
55
  try {
47
56
  const parsed = new URL(url);
48
- const transport = parsed.protocol === "https:" ? https : parsed.protocol === "http:" ? http : null;
57
+ const transport =
58
+ parsed.protocol === "https:"
59
+ ? https
60
+ : parsed.protocol === "http:"
61
+ ? http
62
+ : null;
49
63
  if (!transport) {
50
64
  finish(false);
51
65
  return;
52
66
  }
53
67
 
54
- const request = transport.request(parsed, {
55
- method: "HEAD",
56
- headers: { "user-agent": "triflux-cache-guard" },
57
- }, (response) => {
58
- response.resume();
59
- finish(true);
60
- });
68
+ const request = transport.request(
69
+ parsed,
70
+ {
71
+ method: "HEAD",
72
+ headers: { "user-agent": "triflux-cache-guard" },
73
+ },
74
+ (response) => {
75
+ response.resume();
76
+ finish(true);
77
+ },
78
+ );
61
79
 
62
80
  request.setTimeout(NETWORK_TIMEOUT_MS, () => {
63
81
  request.destroy(new Error("timeout"));
@@ -88,23 +106,31 @@ export function validateRuntimeCachePaths(cacheDir) {
88
106
  } catch (error) {
89
107
  return {
90
108
  ok: false,
91
- issues: [{
92
- file: ".",
93
- error: error instanceof Error ? error.message : String(error),
94
- }],
109
+ issues: [
110
+ {
111
+ file: ".",
112
+ error: error instanceof Error ? error.message : String(error),
113
+ },
114
+ ],
95
115
  };
96
116
  }
97
117
  }
98
118
 
99
119
  export async function checkNetworkAvailability(urls) {
100
- const targets = [...new Set((Array.isArray(urls) ? urls : []).filter(Boolean))];
120
+ const targets = [
121
+ ...new Set((Array.isArray(urls) ? urls : []).filter(Boolean)),
122
+ ];
101
123
  if (targets.length === 0) {
102
124
  return { online: true, reachable: [], unreachable: [] };
103
125
  }
104
126
 
105
127
  const results = await Promise.all(targets.map((url) => probeUrl(url)));
106
- const reachable = results.filter((result) => result.ok).map((result) => result.url);
107
- const unreachable = results.filter((result) => !result.ok).map((result) => result.url);
128
+ const reachable = results
129
+ .filter((result) => result.ok)
130
+ .map((result) => result.url);
131
+ const unreachable = results
132
+ .filter((result) => !result.ok)
133
+ .map((result) => result.url);
108
134
 
109
135
  return {
110
136
  online: unreachable.length === 0,
@@ -2,29 +2,36 @@
2
2
  // Deep 스킬에서 3개 모델이 같은 context7 문서를 요청하면 1번만 호출, 나머지는 캐시 hit.
3
3
  // 파일 기반 영속 + 메모리 기반 핫캐시 이중 구조.
4
4
 
5
- import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync, statSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { createHash } from 'node:crypto';
8
- import { homedir } from 'node:os';
9
-
10
- const DEFAULT_CACHE_DIR = join(homedir(), '.triflux', 'mcp-cache');
5
+ import { createHash } from "node:crypto";
6
+ import {
7
+ existsSync,
8
+ mkdirSync,
9
+ readdirSync,
10
+ readFileSync,
11
+ unlinkSync,
12
+ writeFileSync,
13
+ } from "node:fs";
14
+ import { homedir } from "node:os";
15
+ import { join } from "node:path";
16
+
17
+ const DEFAULT_CACHE_DIR = join(homedir(), ".triflux", "mcp-cache");
11
18
  const DEFAULT_MAX_ENTRIES = 500;
12
19
 
13
20
  // 서버별 TTL (ms)
14
21
  const SERVER_TTL = Object.freeze({
15
- 'context7': 60 * 60 * 1000, // 1시간 — 라이브러리 문서는 자주 안 바뀜
16
- 'brave-search': 10 * 60 * 1000, // 10분 — 웹 검색
17
- 'exa': 10 * 60 * 1000, // 10분
18
- 'tavily': 10 * 60 * 1000, // 10분
19
- 'serena': 5 * 60 * 1000, // 5분 — 코드 분석 (코드 변경 가능)
20
- 'jira': 2 * 60 * 1000, // 2분 — 이슈 상태 자주 변경
21
- 'notion': 5 * 60 * 1000, // 5분
22
- '_default': 5 * 60 * 1000, // 기본 5분
22
+ context7: 60 * 60 * 1000, // 1시간 — 라이브러리 문서는 자주 안 바뀜
23
+ "brave-search": 10 * 60 * 1000, // 10분 — 웹 검색
24
+ exa: 10 * 60 * 1000, // 10분
25
+ tavily: 10 * 60 * 1000, // 10분
26
+ serena: 5 * 60 * 1000, // 5분 — 코드 분석 (코드 변경 가능)
27
+ jira: 2 * 60 * 1000, // 2분 — 이슈 상태 자주 변경
28
+ notion: 5 * 60 * 1000, // 5분
29
+ _default: 5 * 60 * 1000, // 기본 5분
23
30
  });
24
31
 
25
32
  function hashKey(server, method, params) {
26
33
  const payload = JSON.stringify({ server, method, params });
27
- return createHash('sha256').update(payload).digest('hex').slice(0, 16);
34
+ return createHash("sha256").update(payload).digest("hex").slice(0, 16);
28
35
  }
29
36
 
30
37
  /**
@@ -82,7 +89,7 @@ export function createMcpResponseCache(opts = {}) {
82
89
  const path = getCachePath(key);
83
90
  if (existsSync(path)) {
84
91
  try {
85
- const entry = JSON.parse(readFileSync(path, 'utf8'));
92
+ const entry = JSON.parse(readFileSync(path, "utf8"));
86
93
  if (Date.now() - entry.ts < ttl) {
87
94
  hits++;
88
95
  hot.set(key, entry);
@@ -90,8 +97,14 @@ export function createMcpResponseCache(opts = {}) {
90
97
  return { hit: true, data: entry.data };
91
98
  }
92
99
  // expired — 삭제
93
- try { unlinkSync(path); } catch { /* ignore */ }
94
- } catch { /* corrupt file */ }
100
+ try {
101
+ unlinkSync(path);
102
+ } catch {
103
+ /* ignore */
104
+ }
105
+ } catch {
106
+ /* corrupt file */
107
+ }
95
108
  }
96
109
 
97
110
  misses++;
@@ -115,8 +128,10 @@ export function createMcpResponseCache(opts = {}) {
115
128
 
116
129
  // 파일
117
130
  try {
118
- writeFileSync(getCachePath(key), JSON.stringify(entry), 'utf8');
119
- } catch { /* best-effort */ }
131
+ writeFileSync(getCachePath(key), JSON.stringify(entry), "utf8");
132
+ } catch {
133
+ /* best-effort */
134
+ }
120
135
  }
121
136
 
122
137
  function evictIfNeeded() {
@@ -134,21 +149,28 @@ export function createMcpResponseCache(opts = {}) {
134
149
  let pruned = 0;
135
150
  try {
136
151
  for (const name of readdirSync(cacheDir)) {
137
- if (!name.endsWith('.json')) continue;
152
+ if (!name.endsWith(".json")) continue;
138
153
  const path = join(cacheDir, name);
139
154
  try {
140
- const entry = JSON.parse(readFileSync(path, 'utf8'));
141
- const ttl = getTtl(entry.server || '_default');
155
+ const entry = JSON.parse(readFileSync(path, "utf8"));
156
+ const ttl = getTtl(entry.server || "_default");
142
157
  if (Date.now() - entry.ts >= ttl) {
143
158
  unlinkSync(path);
144
159
  pruned++;
145
160
  }
146
161
  } catch {
147
162
  // corrupt — 삭제
148
- try { unlinkSync(path); pruned++; } catch { /* ignore */ }
163
+ try {
164
+ unlinkSync(path);
165
+ pruned++;
166
+ } catch {
167
+ /* ignore */
168
+ }
149
169
  }
150
170
  }
151
- } catch { /* ignore */ }
171
+ } catch {
172
+ /* ignore */
173
+ }
152
174
  return pruned;
153
175
  }
154
176
 
@@ -159,8 +181,12 @@ export function createMcpResponseCache(opts = {}) {
159
181
  function stats() {
160
182
  let diskFiles = 0;
161
183
  try {
162
- diskFiles = readdirSync(cacheDir).filter((n) => n.endsWith('.json')).length;
163
- } catch { /* ignore */ }
184
+ diskFiles = readdirSync(cacheDir).filter((n) =>
185
+ n.endsWith(".json"),
186
+ ).length;
187
+ } catch {
188
+ /* ignore */
189
+ }
164
190
 
165
191
  const total = hits + misses;
166
192
  return {
@@ -168,7 +194,7 @@ export function createMcpResponseCache(opts = {}) {
168
194
  misses,
169
195
  hotSize: hot.size,
170
196
  diskFiles,
171
- hitRate: total > 0 ? `${((hits / total) * 100).toFixed(1)}%` : '0%',
197
+ hitRate: total > 0 ? `${((hits / total) * 100).toFixed(1)}%` : "0%",
172
198
  };
173
199
  }
174
200
 
@@ -177,11 +203,17 @@ export function createMcpResponseCache(opts = {}) {
177
203
  hot.clear();
178
204
  try {
179
205
  for (const name of readdirSync(cacheDir)) {
180
- if (name.endsWith('.json')) {
181
- try { unlinkSync(join(cacheDir, name)); } catch { /* ignore */ }
206
+ if (name.endsWith(".json")) {
207
+ try {
208
+ unlinkSync(join(cacheDir, name));
209
+ } catch {
210
+ /* ignore */
211
+ }
182
212
  }
183
213
  }
184
- } catch { /* ignore */ }
214
+ } catch {
215
+ /* ignore */
216
+ }
185
217
  hits = 0;
186
218
  misses = 0;
187
219
  }
@@ -192,7 +224,9 @@ export function createMcpResponseCache(opts = {}) {
192
224
  prune,
193
225
  stats,
194
226
  clear,
195
- get cacheDir() { return cacheDir; },
227
+ get cacheDir() {
228
+ return cacheDir;
229
+ },
196
230
  });
197
231
  }
198
232