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
@@ -10,14 +10,18 @@
10
10
  * 각 요청에 correlationId를 할당하고, 응답 완료 시 구조화 로그를 남긴다.
11
11
  * health/status 체크는 로깅을 건너뛴다.
12
12
  */
13
- import { withRequestContext, getCorrelationId } from '../../scripts/lib/context.mjs';
14
- import { createModuleLogger } from '../../scripts/lib/logger.mjs';
15
- import { createContextMonitor } from '../../hud/context-monitor.mjs';
16
13
 
17
- const log = createModuleLogger('hub');
14
+ import { createContextMonitor } from "../../hud/context-monitor.mjs";
15
+ import {
16
+ getCorrelationId,
17
+ withRequestContext,
18
+ } from "../../scripts/lib/context.mjs";
19
+ import { createModuleLogger } from "../../scripts/lib/logger.mjs";
20
+
21
+ const log = createModuleLogger("hub");
18
22
  const contextMonitor = createContextMonitor();
19
23
 
20
- const SKIP_PATHS = new Set(['/health', '/healthz', '/status', '/ready']);
24
+ const SKIP_PATHS = new Set(["/health", "/healthz", "/status", "/ready"]);
21
25
  const MAX_CAPTURE_BYTES = 256 * 1024;
22
26
 
23
27
  /**
@@ -35,8 +39,8 @@ export function wrapRequestHandler(handler) {
35
39
  }
36
40
 
37
41
  const correlationId =
38
- req.headers['x-correlation-id'] ||
39
- req.headers['x-request-id'] ||
42
+ req.headers["x-correlation-id"] ||
43
+ req.headers["x-request-id"] ||
40
44
  undefined; // withRequestContext will generate one
41
45
 
42
46
  withRequestContext(
@@ -51,9 +55,11 @@ export function wrapRequestHandler(handler) {
51
55
  let reqBytes = 0;
52
56
  let reqOverflow = false;
53
57
 
54
- req.on('data', (chunk) => {
58
+ req.on("data", (chunk) => {
55
59
  if (reqOverflow) return;
56
- const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
60
+ const buf = Buffer.isBuffer(chunk)
61
+ ? chunk
62
+ : Buffer.from(String(chunk));
57
63
  reqBytes += buf.length;
58
64
  if (reqBytes > MAX_CAPTURE_BYTES) {
59
65
  reqOverflow = true;
@@ -72,7 +78,9 @@ export function wrapRequestHandler(handler) {
72
78
 
73
79
  function captureResponseChunk(chunk) {
74
80
  if (resOverflow || chunk == null) return;
75
- const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
81
+ const buf = Buffer.isBuffer(chunk)
82
+ ? chunk
83
+ : Buffer.from(String(chunk));
76
84
  resBytes += buf.length;
77
85
  if (resBytes > MAX_CAPTURE_BYTES) {
78
86
  resOverflow = true;
@@ -94,28 +102,38 @@ export function wrapRequestHandler(handler) {
94
102
 
95
103
  // 응답 헤더에 상관 ID 포함
96
104
  const cid = getCorrelationId();
97
- if (cid) res.setHeader('X-Correlation-ID', cid);
105
+ if (cid) res.setHeader("X-Correlation-ID", cid);
98
106
 
99
107
  // 응답 완료 시 로깅
100
- res.on('finish', () => {
101
- const duration = Number(process.hrtime.bigint() - startTime) / 1_000_000;
102
- const level = res.statusCode >= 500 ? 'error'
103
- : res.statusCode >= 400 ? 'warn'
104
- : 'info';
105
- const reqBodyText = reqOverflow ? '' : Buffer.concat(reqChunks).toString('utf8');
106
- const resBodyText = resOverflow ? '' : Buffer.concat(resChunks).toString('utf8');
108
+ res.on("finish", () => {
109
+ const duration =
110
+ Number(process.hrtime.bigint() - startTime) / 1_000_000;
111
+ const level =
112
+ res.statusCode >= 500
113
+ ? "error"
114
+ : res.statusCode >= 400
115
+ ? "warn"
116
+ : "info";
117
+ const reqBodyText = reqOverflow
118
+ ? ""
119
+ : Buffer.concat(reqChunks).toString("utf8");
120
+ const resBodyText = resOverflow
121
+ ? ""
122
+ : Buffer.concat(resChunks).toString("utf8");
107
123
  const tokenSummary = contextMonitor.record({
108
124
  requestBody: reqBodyText,
109
- requestBytes: reqBytes || Number(req.headers['content-length'] || 0),
125
+ requestBytes:
126
+ reqBytes || Number(req.headers["content-length"] || 0),
110
127
  responseBody: resBodyText,
111
- responseBytes: resBytes || Number(res.getHeader('content-length') || 0),
128
+ responseBytes:
129
+ resBytes || Number(res.getHeader("content-length") || 0),
112
130
  });
113
131
 
114
132
  log[level](
115
133
  {
116
134
  status: res.statusCode,
117
135
  duration: Math.round(duration * 100) / 100,
118
- contentLength: res.getHeader('content-length') || 0,
136
+ contentLength: res.getHeader("content-length") || 0,
119
137
  tokenUsage: {
120
138
  request: tokenSummary.requestTokens,
121
139
  response: tokenSummary.responseTokens,
@@ -125,23 +143,32 @@ export function wrapRequestHandler(handler) {
125
143
  overheadMs: tokenSummary.overheadMs,
126
144
  },
127
145
  },
128
- 'http.response',
146
+ "http.response",
129
147
  );
130
148
 
131
- if (tokenSummary.warningLevel === 'critical') {
149
+ if (tokenSummary.warningLevel === "critical") {
132
150
  log.error(
133
- { context: tokenSummary.display, message: tokenSummary.warningMessage },
134
- 'context.critical',
151
+ {
152
+ context: tokenSummary.display,
153
+ message: tokenSummary.warningMessage,
154
+ },
155
+ "context.critical",
135
156
  );
136
- } else if (tokenSummary.warningLevel === 'warn') {
157
+ } else if (tokenSummary.warningLevel === "warn") {
137
158
  log.warn(
138
- { context: tokenSummary.display, message: tokenSummary.warningMessage },
139
- 'context.warn',
159
+ {
160
+ context: tokenSummary.display,
161
+ message: tokenSummary.warningMessage,
162
+ },
163
+ "context.warn",
140
164
  );
141
- } else if (tokenSummary.warningLevel === 'info') {
165
+ } else if (tokenSummary.warningLevel === "info") {
142
166
  log.info(
143
- { context: tokenSummary.display, message: tokenSummary.warningMessage },
144
- 'context.info',
167
+ {
168
+ context: tokenSummary.display,
169
+ message: tokenSummary.warningMessage,
170
+ },
171
+ "context.info",
145
172
  );
146
173
  }
147
174
  });
@@ -152,10 +179,10 @@ export function wrapRequestHandler(handler) {
152
179
  };
153
180
  }
154
181
 
155
- function getRequestPath(url = '/') {
182
+ function getRequestPath(url = "/") {
156
183
  try {
157
- return new URL(url, 'http://127.0.0.1').pathname;
184
+ return new URL(url, "http://127.0.0.1").pathname;
158
185
  } catch {
159
- return String(url).replace(/\?.*/, '') || '/';
186
+ return String(url).replace(/\?.*/, "") || "/";
160
187
  }
161
188
  }
package/hub/paths.mjs CHANGED
@@ -1,15 +1,15 @@
1
1
  // hub/paths.mjs — triflux 워킹 디렉토리 경로 상수
2
2
 
3
- import { mkdirSync } from 'node:fs';
4
- import { join } from 'node:path';
3
+ import { mkdirSync } from "node:fs";
4
+ import { join } from "node:path";
5
5
 
6
- export const TFX_WORK_DIR = '.tfx';
7
- export const TFX_PLANS_DIR = join(TFX_WORK_DIR, 'plans');
8
- export const TFX_REPORTS_DIR = join(TFX_WORK_DIR, 'reports');
9
- export const TFX_HANDOFFS_DIR = join(TFX_WORK_DIR, 'handoffs');
10
- export const TFX_LOGS_DIR = join(TFX_WORK_DIR, 'logs');
11
- export const TFX_STATE_DIR = join(TFX_WORK_DIR, 'state');
12
- export const TFX_FULLCYCLE_DIR = join(TFX_WORK_DIR, 'fullcycle');
6
+ export const TFX_WORK_DIR = ".tfx";
7
+ export const TFX_PLANS_DIR = join(TFX_WORK_DIR, "plans");
8
+ export const TFX_REPORTS_DIR = join(TFX_WORK_DIR, "reports");
9
+ export const TFX_HANDOFFS_DIR = join(TFX_WORK_DIR, "handoffs");
10
+ export const TFX_LOGS_DIR = join(TFX_WORK_DIR, "logs");
11
+ export const TFX_STATE_DIR = join(TFX_WORK_DIR, "state");
12
+ export const TFX_FULLCYCLE_DIR = join(TFX_WORK_DIR, "fullcycle");
13
13
 
14
14
  /**
15
15
  * triflux 워킹 디렉토리 구조를 보장한다.
@@ -4,11 +4,11 @@
4
4
  // 5단계 확신도 검증: >=90% proceed / 70-89% alternative / <70% abort
5
5
 
6
6
  export const CRITERIA = [
7
- { id: 'no_duplicate', label: '중복 구현 없는지?', weight: 0.25 },
8
- { id: 'architecture', label: '아키텍처 준수?', weight: 0.25 },
9
- { id: 'docs_verified', label: '공식 문서 확인?', weight: 0.20 },
10
- { id: 'oss_reference', label: 'OSS 레퍼런스?', weight: 0.15 },
11
- { id: 'root_cause', label: '근본 원인 파악?', weight: 0.15 },
7
+ { id: "no_duplicate", label: "중복 구현 없는지?", weight: 0.25 },
8
+ { id: "architecture", label: "아키텍처 준수?", weight: 0.25 },
9
+ { id: "docs_verified", label: "공식 문서 확인?", weight: 0.2 },
10
+ { id: "oss_reference", label: "OSS 레퍼런스?", weight: 0.15 },
11
+ { id: "root_cause", label: "근본 원인 파악?", weight: 0.15 },
12
12
  ];
13
13
 
14
14
  /**
@@ -22,18 +22,31 @@ export function runConfidenceCheck(planArtifact, context = {}) {
22
22
  if (!planArtifact) {
23
23
  return {
24
24
  score: 0,
25
- breakdown: CRITERIA.map(c => ({ id: c.id, label: c.label, weight: c.weight, score: 0, passed: false })),
26
- decision: 'abort',
27
- reasoning: 'planArtifact가 제공되지 않았습니다.',
25
+ breakdown: CRITERIA.map((c) => ({
26
+ id: c.id,
27
+ label: c.label,
28
+ weight: c.weight,
29
+ score: 0,
30
+ passed: false,
31
+ })),
32
+ decision: "abort",
33
+ reasoning: "planArtifact가 제공되지 않았습니다.",
28
34
  };
29
35
  }
30
36
 
31
37
  const checks = context.checks || {};
32
38
 
33
- const breakdown = CRITERIA.map(c => {
39
+ const breakdown = CRITERIA.map((c) => {
34
40
  const raw = checks[c.id];
35
- const score = typeof raw === 'number' ? Math.max(0, Math.min(1, raw)) : (raw ? 1 : 0);
36
- return { id: c.id, label: c.label, weight: c.weight, score, passed: score >= 0.7 };
41
+ const score =
42
+ typeof raw === "number" ? Math.max(0, Math.min(1, raw)) : raw ? 1 : 0;
43
+ return {
44
+ id: c.id,
45
+ label: c.label,
46
+ weight: c.weight,
47
+ score,
48
+ passed: score >= 0.7,
49
+ };
37
50
  });
38
51
 
39
52
  const totalScore = Math.round(
@@ -42,15 +55,21 @@ export function runConfidenceCheck(planArtifact, context = {}) {
42
55
 
43
56
  let decision, reasoning;
44
57
  if (totalScore >= 90) {
45
- decision = 'proceed';
58
+ decision = "proceed";
46
59
  reasoning = `확신도 ${totalScore}%: 모든 기준 충족. 실행 진행.`;
47
60
  } else if (totalScore >= 70) {
48
- decision = 'alternative';
61
+ decision = "alternative";
49
62
  reasoning = `확신도 ${totalScore}%: 일부 기준 미달. 대안 검토 필요.`;
50
63
  } else {
51
- decision = 'abort';
64
+ decision = "abort";
52
65
  reasoning = `확신도 ${totalScore}%: 기준 미달. 실행 중단.`;
53
66
  }
54
67
 
55
- return { score: totalScore, breakdown, decision, reasoning, needsReview: decision === 'alternative' };
68
+ return {
69
+ score: totalScore,
70
+ breakdown,
71
+ decision,
72
+ reasoning,
73
+ needsReview: decision === "alternative",
74
+ };
56
75
  }
@@ -14,8 +14,9 @@ export const STAGE_THRESHOLDS = {
14
14
 
15
15
  /** 환경변수 기반 기본 임계값 (기본 75) */
16
16
  function getDefaultThreshold() {
17
- const env = typeof process !== 'undefined' && process.env?.TRIFLUX_CONSENSUS_THRESHOLD;
18
- if (env != null && env !== '') {
17
+ const env =
18
+ typeof process !== "undefined" && process.env?.TRIFLUX_CONSENSUS_THRESHOLD;
19
+ if (env != null && env !== "") {
19
20
  const parsed = Number(env);
20
21
  if (!Number.isNaN(parsed) && parsed >= 0 && parsed <= 100) return parsed;
21
22
  }
@@ -30,14 +31,19 @@ function getDefaultThreshold() {
30
31
  * @param {string} [mode] - 실행 모드 ('supervised' | 기타)
31
32
  * @returns {'proceed' | 'proceed_warn' | 'retry' | 'escalate' | 'abort'}
32
33
  */
33
- export function evaluateQualityBranch(successRate, retryCount, maxRetries, mode) {
34
- if (successRate >= 90) return 'proceed';
35
- if (successRate >= 75) return 'proceed_warn';
34
+ export function evaluateQualityBranch(
35
+ successRate,
36
+ retryCount,
37
+ maxRetries,
38
+ mode,
39
+ ) {
40
+ if (successRate >= 90) return "proceed";
41
+ if (successRate >= 75) return "proceed_warn";
36
42
 
37
43
  // <75%: 재시도 가능 여부에 따라 분기
38
- if (retryCount < maxRetries) return 'retry';
39
- if (mode === 'supervised') return 'escalate';
40
- return 'abort';
44
+ if (retryCount < maxRetries) return "retry";
45
+ if (mode === "supervised") return "escalate";
46
+ return "abort";
41
47
  }
42
48
 
43
49
  /**
@@ -56,8 +62,8 @@ export function evaluateConsensus(results, options = {}) {
56
62
  return {
57
63
  successRate: 0,
58
64
  threshold: options.threshold ?? getDefaultThreshold(),
59
- decision: 'abort',
60
- reasoning: '평가 대상 결과가 없습니다.',
65
+ decision: "abort",
66
+ reasoning: "평가 대상 결과가 없습니다.",
61
67
  results: [],
62
68
  };
63
69
  }
@@ -67,14 +73,20 @@ export function evaluateConsensus(results, options = {}) {
67
73
  const mode = options.mode;
68
74
 
69
75
  // 임계값 결정: 직접 지정 > stage별 > 환경변수 > 기본 75
70
- const threshold = options.threshold
71
- ?? (options.stage && STAGE_THRESHOLDS[options.stage])
72
- ?? getDefaultThreshold();
76
+ const threshold =
77
+ options.threshold ??
78
+ (options.stage && STAGE_THRESHOLDS[options.stage]) ??
79
+ getDefaultThreshold();
73
80
 
74
- const successCount = results.filter(r => r.success).length;
81
+ const successCount = results.filter((r) => r.success).length;
75
82
  const successRate = Math.round((successCount / results.length) * 100);
76
83
 
77
- const decision = evaluateQualityBranch(successRate, retryCount, maxRetries, mode);
84
+ const decision = evaluateQualityBranch(
85
+ successRate,
86
+ retryCount,
87
+ maxRetries,
88
+ mode,
89
+ );
78
90
 
79
91
  const reasoningMap = {
80
92
  proceed: `합의율 ${successRate}% (>= 90%): 전원 합의. 진행.`,
@@ -1,5 +1,9 @@
1
1
  // hub/pipeline/gates/index.mjs — Quality Gates re-export
2
2
 
3
- export { CRITERIA, runConfidenceCheck } from './confidence.mjs';
4
- export { RED_FLAGS, QUESTIONS, runSelfCheck } from './selfcheck.mjs';
5
- export { STAGE_THRESHOLDS, evaluateQualityBranch, evaluateConsensus } from './consensus.mjs';
3
+ export { CRITERIA, runConfidenceCheck } from "./confidence.mjs";
4
+ export {
5
+ evaluateConsensus,
6
+ evaluateQualityBranch,
7
+ STAGE_THRESHOLDS,
8
+ } from "./consensus.mjs";
9
+ export { QUESTIONS, RED_FLAGS, runSelfCheck } from "./selfcheck.mjs";
@@ -4,20 +4,56 @@
4
4
  // 4대 필수 질문 + 7대 할루시네이션 Red Flag 탐지
5
5
 
6
6
  export const RED_FLAGS = [
7
- { id: 'test_pass_no_output', pattern: /테스트\s*(?:가\s*)?통과/, label: '"테스트 통과" (출력 없이)' },
8
- { id: 'everything_works', pattern: /모든\s*게?\s*작동/, label: '"모든게 작동" (증거 없이)' },
9
- { id: 'no_changes_with_diff', pattern: /변경\s*(?:사항\s*)?없/, label: '"변경 없음" (diff 있는데)' },
10
- { id: 'backward_compatible', pattern: /호환성\s*(?:이\s*)?유지/, label: '"호환성 유지" (검증 없이)' },
11
- { id: 'performance_improved', pattern: /성능\s*(?:이\s*)?개선/, label: '"성능 개선" (벤치마크 없이)' },
12
- { id: 'security_enhanced', pattern: /보안\s*(?:이\s*)?강화/, label: '"보안 강화" (증거 없이)' },
13
- { id: 'error_handling_done', pattern: /에러\s*처리\s*(?:가\s*)?완료/, label: '"에러 처리 완료" (catch 블록만)' },
7
+ {
8
+ id: "test_pass_no_output",
9
+ pattern: /테스트\s*(?:가\s*)?통과/,
10
+ label: '"테스트 통과" (출력 없이)',
11
+ },
12
+ {
13
+ id: "everything_works",
14
+ pattern: /모든\s*게?\s*작동/,
15
+ label: '"모든게 작동" (증거 없이)',
16
+ },
17
+ {
18
+ id: "no_changes_with_diff",
19
+ pattern: /변경\s*(?:사항\s*)?없/,
20
+ label: '"변경 없음" (diff 있는데)',
21
+ },
22
+ {
23
+ id: "backward_compatible",
24
+ pattern: /호환성\s*(?:이\s*)?유지/,
25
+ label: '"호환성 유지" (검증 없이)',
26
+ },
27
+ {
28
+ id: "performance_improved",
29
+ pattern: /성능\s*(?:이\s*)?개선/,
30
+ label: '"성능 개선" (벤치마크 없이)',
31
+ },
32
+ {
33
+ id: "security_enhanced",
34
+ pattern: /보안\s*(?:이\s*)?강화/,
35
+ label: '"보안 강화" (증거 없이)',
36
+ },
37
+ {
38
+ id: "error_handling_done",
39
+ pattern: /에러\s*처리\s*(?:가\s*)?완료/,
40
+ label: '"에러 처리 완료" (catch 블록만)',
41
+ },
14
42
  ];
15
43
 
16
44
  export const QUESTIONS = [
17
- { id: 'tests_passing', label: '모든 테스트 통과?', evidenceKey: 'testOutput' },
18
- { id: 'requirements_met', label: '모든 요구사항 충족?', evidenceKey: 'requirementChecklist' },
19
- { id: 'no_assumptions', label: '검증 없는 가정?', evidenceKey: 'references' },
20
- { id: 'evidence_provided', label: '증거 있는가?', evidenceKey: 'artifacts' },
45
+ {
46
+ id: "tests_passing",
47
+ label: "모든 테스트 통과?",
48
+ evidenceKey: "testOutput",
49
+ },
50
+ {
51
+ id: "requirements_met",
52
+ label: "모든 요구사항 충족?",
53
+ evidenceKey: "requirementChecklist",
54
+ },
55
+ { id: "no_assumptions", label: "검증 없는 가정?", evidenceKey: "references" },
56
+ { id: "evidence_provided", label: "증거 있는가?", evidenceKey: "artifacts" },
21
57
  ];
22
58
 
23
59
  /**
@@ -34,10 +70,10 @@ function detectRedFlags(text, context = {}) {
34
70
  if (!rf.pattern.test(text)) continue;
35
71
 
36
72
  // "변경 없음"은 실제 diff가 있을 때만 Red Flag
37
- if (rf.id === 'no_changes_with_diff' && !context.hasDiff) continue;
73
+ if (rf.id === "no_changes_with_diff" && !context.hasDiff) continue;
38
74
 
39
75
  // "테스트 통과"는 testOutput 증거가 없을 때만 Red Flag
40
- if (rf.id === 'test_pass_no_output' && evidence.testOutput) continue;
76
+ if (rf.id === "test_pass_no_output" && evidence.testOutput) continue;
41
77
 
42
78
  // 기타 Red Flag는 해당 id의 반증이 있으면 스킵
43
79
  if (evidence[rf.id]) continue;
@@ -58,24 +94,26 @@ function detectRedFlags(text, context = {}) {
58
94
  * @returns {{ passed: boolean, score: number, flags: Array, checklist: Array }}
59
95
  */
60
96
  export function runSelfCheck(execResult, verifyResult, requirements = {}) {
61
- const normalize = (v) => typeof v === 'string' ? v : (v != null ? JSON.stringify(v) : '');
62
- const text = [normalize(execResult), normalize(verifyResult)].join('\n');
97
+ const normalize = (v) =>
98
+ typeof v === "string" ? v : v != null ? JSON.stringify(v) : "";
99
+ const text = [normalize(execResult), normalize(verifyResult)].join("\n");
63
100
 
64
101
  const flags = detectRedFlags(text, requirements);
65
102
 
66
103
  const evidence = requirements.evidence || {};
67
- const checklist = QUESTIONS.map(q => {
104
+ const checklist = QUESTIONS.map((q) => {
68
105
  const ev = evidence[q.evidenceKey];
69
- const passed = ev != null && (typeof ev === 'string' ? ev.trim().length > 0 : true);
106
+ const passed =
107
+ ev != null && (typeof ev === "string" ? ev.trim().length > 0 : true);
70
108
  return { id: q.id, label: q.label, passed, evidence: ev || null };
71
109
  });
72
110
 
73
- const allQuestionsPassed = checklist.every(q => q.passed);
111
+ const allQuestionsPassed = checklist.every((q) => q.passed);
74
112
  const passed = flags.length === 0 && allQuestionsPassed;
75
113
 
76
114
  // 점수: 기본 100, Red Flag당 -15, 실패 질문당 -20
77
115
  const flagPenalty = flags.length * 15;
78
- const questionPenalty = checklist.filter(q => !q.passed).length * 20;
116
+ const questionPenalty = checklist.filter((q) => !q.passed).length * 20;
79
117
  const score = Math.max(0, 100 - flagPenalty - questionPenalty);
80
118
 
81
119
  return { passed, score, flags, checklist };