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
@@ -5,20 +5,23 @@
5
5
  // - tfx-multi 활성 상태 시 headless dispatch 강제
6
6
  // - 프로젝트 컨텍스트 자동 첨부
7
7
 
8
- import { readFileSync, existsSync } from "node:fs";
9
- import { join } from "node:path";
8
+ import { existsSync, readFileSync } from "node:fs";
10
9
  import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
11
 
12
12
  const TFX_MULTI_STATE = join(tmpdir(), "tfx-multi-state.json");
13
13
  const EXPIRE_MS = 30 * 60 * 1000; // 30분
14
14
 
15
15
  // 서브에이전트 타입별 라우팅 힌트
16
16
  const AGENT_HINTS = {
17
- "general-purpose": "범용 에이전트. tfx 스킬이 활성이면 스킬 MD의 라우팅을 우선한다.",
17
+ "general-purpose":
18
+ "범용 에이전트. tfx 스킬이 활성이면 스킬 MD의 라우팅을 우선한다.",
18
19
  Explore: "탐색 전용. 파일 수정 불가. Glob/Grep/Read만 사용.",
19
20
  Plan: "설계 전용. 파일 수정 불가. 구현 계획 반환.",
20
- "oh-my-claudecode:executor": "OMC executor. triflux 프로젝트에서는 tfx-auto 라우팅을 우선.",
21
- "oh-my-claudecode:code-reviewer": "OMC 리뷰어. 교차 리뷰 CLAUDE.md 교차 검증 규칙 준수.",
21
+ "oh-my-claudecode:executor":
22
+ "OMC executor. triflux 프로젝트에서는 tfx-auto 라우팅을 우선.",
23
+ "oh-my-claudecode:code-reviewer":
24
+ "OMC 리뷰어. 교차 리뷰 시 CLAUDE.md 교차 검증 규칙 준수.",
22
25
  "oh-my-claudecode:architect": "OMC 아키텍트. READ-ONLY.",
23
26
  "oh-my-claudecode:debugger": "OMC 디버거. 근본 원인 분석 집중.",
24
27
  "oh-my-claudecode:test-engineer": "OMC 테스트. npm test 실행 후 결과 반환.",
@@ -51,7 +54,7 @@ function buildContext(agentType, prompt) {
51
54
  if (multiState) {
52
55
  parts.push(
53
56
  "[tfx-multi ACTIVE] headless dispatch 모드. " +
54
- "CLI 작업은 Bash(tfx-route.sh)를 통해 실행하세요."
57
+ "CLI 작업은 Bash(tfx-route.sh)를 통해 실행하세요.",
55
58
  );
56
59
  }
57
60
 
@@ -64,7 +67,7 @@ function buildContext(agentType, prompt) {
64
67
  // 3. 프로젝트 컨텍스트
65
68
  parts.push(
66
69
  "triflux 프로젝트: subagent_type 미지정 시 'general-purpose' 기본. " +
67
- "tfx-* 스킬 활성 시 스킬 MD 라우팅 우선."
70
+ "tfx-* 스킬 활성 시 스킬 MD 라우팅 우선.",
68
71
  );
69
72
 
70
73
  return parts.join("\n");
@@ -84,7 +87,8 @@ function main() {
84
87
  if (input.tool_name !== "Agent") process.exit(0);
85
88
 
86
89
  const toolInput = input.tool_input || {};
87
- const agentType = toolInput.subagent_type || toolInput.agent || "general-purpose";
90
+ const agentType =
91
+ toolInput.subagent_type || toolInput.agent || "general-purpose";
88
92
  const prompt = toolInput.prompt || "";
89
93
 
90
94
  const context = buildContext(agentType, prompt);
@@ -9,9 +9,9 @@
9
9
  // 2. 일정 수(REVIEW_THRESHOLD) 이상 미검증 파일이 쌓이면 nudge 메시지 주입
10
10
  // 3. git commit 전 미검증 파일 경고
11
11
 
12
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
13
- import { join, relative } from "node:path";
12
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
13
  import { tmpdir } from "node:os";
14
+ import { join, relative } from "node:path";
15
15
 
16
16
  const STATE_DIR = join(tmpdir(), "tfx-cross-review");
17
17
  const STATE_FILE = join(STATE_DIR, "pending-review.json");
@@ -20,9 +20,24 @@ const EXPIRE_MS = 60 * 60 * 1000; // 1시간 후 자동 만료
20
20
 
21
21
  // 코드 파일만 추적 (설정/문서/빌드 산출물 제외)
22
22
  const CODE_EXTENSIONS = new Set([
23
- ".js", ".mjs", ".cjs", ".ts", ".tsx", ".jsx",
24
- ".py", ".rs", ".go", ".java", ".c", ".cpp", ".h",
25
- ".vue", ".svelte", ".sh", ".bash", ".ps1",
23
+ ".js",
24
+ ".mjs",
25
+ ".cjs",
26
+ ".ts",
27
+ ".tsx",
28
+ ".jsx",
29
+ ".py",
30
+ ".rs",
31
+ ".go",
32
+ ".java",
33
+ ".c",
34
+ ".cpp",
35
+ ".h",
36
+ ".vue",
37
+ ".svelte",
38
+ ".sh",
39
+ ".bash",
40
+ ".ps1",
26
41
  ]);
27
42
 
28
43
  function isCodeFile(filePath) {
@@ -93,9 +108,7 @@ function main() {
93
108
  saveState(state);
94
109
 
95
110
  // 미검증 파일 수 체크
96
- const unreviewed = Object.entries(state.files).filter(
97
- ([, v]) => !v.reviewed
98
- );
111
+ const unreviewed = Object.entries(state.files).filter(([, v]) => !v.reviewed);
99
112
  const count = unreviewed.length;
100
113
 
101
114
  if (count >= REVIEW_THRESHOLD) {
@@ -4,7 +4,7 @@
4
4
  // 도구 실패 시 에러 패턴을 분석하여 해결 힌트를 additionalContext로 주입한다.
5
5
  // Claude가 동일 에러를 반복하지 않도록 구체적 가이드를 제공.
6
6
 
7
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
7
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
8
  import { join } from "node:path";
9
9
 
10
10
  // ── 에러 패턴 → 해결 힌트 매핑 ─────────────────────────────
@@ -130,7 +130,8 @@ function main() {
130
130
  ].join("\n");
131
131
 
132
132
  // ── reflexion 적응형 학습: safety-guard/headless-guard 차단을 패널티로 기록 ──
133
- const isSafetyBlock = /\[(?:safety-guard|headless-guard)\].*(?:BLOCKED|차단)/i.test(errorText);
133
+ const isSafetyBlock =
134
+ /\[(?:safety-guard|headless-guard)\].*(?:BLOCKED|차단)/i.test(errorText);
134
135
  if (isSafetyBlock) {
135
136
  try {
136
137
  const home = process.env.HOME || process.env.USERPROFILE || "";
@@ -142,12 +143,18 @@ function main() {
142
143
  ts: new Date().toISOString(),
143
144
  type: "guard_block",
144
145
  tool: input.tool_name || "Bash",
145
- error_pattern: errorText.match(/\[.*?\]\s*(.{0,120})/)?.[1] || errorText.slice(0, 120),
146
+ error_pattern:
147
+ errorText.match(/\[.*?\]\s*(.{0,120})/)?.[1] ||
148
+ errorText.slice(0, 120),
146
149
  command_preview: command.slice(0, 200),
147
- source: errorText.includes("safety-guard") ? "safety-guard" : "headless-guard",
150
+ source: errorText.includes("safety-guard")
151
+ ? "safety-guard"
152
+ : "headless-guard",
148
153
  };
149
154
  writeFileSync(penaltyFile, JSON.stringify(entry) + "\n", { flag: "a" });
150
- } catch { /* reflexion 기록 실패는 무시 — 힌트 출력에 영향 주지 않음 */ }
155
+ } catch {
156
+ /* reflexion 기록 실패는 무시 — 힌트 출력에 영향 주지 않음 */
157
+ }
151
158
  }
152
159
 
153
160
  const hints = findHints(errorText);
@@ -157,10 +164,15 @@ function main() {
157
164
  const toolName = input.tool_name || "Unknown";
158
165
  const parts = [];
159
166
  if (hints.length > 0) {
160
- parts.push(`[error-context] ${toolName} 실패 — 해결 힌트:\n` + hints.map((h) => ` → ${h}`).join("\n"));
167
+ parts.push(
168
+ `[error-context] ${toolName} 실패 — 해결 힌트:\n` +
169
+ hints.map((h) => ` → ${h}`).join("\n"),
170
+ );
161
171
  }
162
172
  if (isSafetyBlock) {
163
- parts.push("[reflexion] 이 패턴이 적응형 학습에 기록되었습니다. 다음 세션에서 동일 패턴 시 사전 차단됩니다.");
173
+ parts.push(
174
+ "[reflexion] 이 패턴이 적응형 학습에 기록되었습니다. 다음 세션에서 동일 패턴 시 사전 차단됩니다.",
175
+ );
164
176
  }
165
177
 
166
178
  if (parts.length === 0) process.exit(0);
@@ -1,31 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { existsSync, readFileSync } from 'node:fs';
4
- import { basename, join } from 'node:path';
5
- import { pathToFileURL } from 'node:url';
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { basename, join } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
6
 
7
- import { createAdaptiveEngine } from '../hub/adaptive.mjs';
7
+ import { createAdaptiveEngine } from "../hub/adaptive.mjs";
8
8
 
9
9
  let engine = null;
10
10
  let createEngine = createAdaptiveEngine;
11
11
 
12
12
  function readStdin() {
13
13
  try {
14
- return readFileSync(0, 'utf8');
14
+ return readFileSync(0, "utf8");
15
15
  } catch {
16
- return '';
16
+ return "";
17
17
  }
18
18
  }
19
19
 
20
20
  function inferProjectSlug(cwd = process.cwd()) {
21
- const packagePath = join(cwd, 'package.json');
21
+ const packagePath = join(cwd, "package.json");
22
22
  if (existsSync(packagePath)) {
23
23
  try {
24
- const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
25
- if (typeof pkg.name === 'string' && pkg.name.trim()) return pkg.name.trim();
24
+ const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
25
+ if (typeof pkg.name === "string" && pkg.name.trim())
26
+ return pkg.name.trim();
26
27
  } catch {}
27
28
  }
28
- return basename(cwd) || 'default';
29
+ return basename(cwd) || "default";
29
30
  }
30
31
 
31
32
  function getEngine() {
@@ -41,22 +42,22 @@ function getEngine() {
41
42
  function buildErrorContext(event = {}) {
42
43
  return {
43
44
  exitCode: event.exitCode,
44
- stderr: String(event.stderr || '').slice(0, 500),
45
+ stderr: String(event.stderr || "").slice(0, 500),
45
46
  tool: event.tool,
46
- command: String(event.command || '').slice(0, 200),
47
+ command: String(event.command || "").slice(0, 200),
47
48
  timestamp: new Date().toISOString(),
48
49
  };
49
50
  }
50
51
 
51
52
  export default function hookAdaptiveCollector(event = {}) {
52
53
  if (Number(event.exitCode) === 0) return null;
53
- if (!event.tool || event.tool === 'Read') return null;
54
+ if (!event.tool || event.tool === "Read") return null;
54
55
 
55
56
  const result = getEngine().handleError(buildErrorContext(event));
56
57
  if (result?.diagnosed) {
57
- console.error(`[adaptive] 에러 패턴 감지: ${result.rule?.id || 'unknown'}`);
58
+ console.error(`[adaptive] 에러 패턴 감지: ${result.rule?.id || "unknown"}`);
58
59
  if (result.promoted) {
59
- console.error(`[adaptive] 규칙 승격 → Tier ${result.rule?.tier ?? '?'}`);
60
+ console.error(`[adaptive] 규칙 승격 → Tier ${result.rule?.tier ?? "?"}`);
60
61
  }
61
62
  }
62
63
  return result;
@@ -80,7 +81,8 @@ function main() {
80
81
  } catch {}
81
82
  }
82
83
 
83
- const isEntrypoint = process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url;
84
+ const isEntrypoint =
85
+ process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url;
84
86
  if (isEntrypoint) {
85
87
  main();
86
88
  }
@@ -12,8 +12,8 @@
12
12
  //
13
13
  // Claude 대화에서 AskUserQuestion으로 UI를 제공하며 내부적으로 이 명령들을 호출합니다.
14
14
 
15
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
16
- import { join, dirname } from "node:path";
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
+ import { dirname, join } from "node:path";
17
17
  import { fileURLToPath } from "node:url";
18
18
  import { PLUGIN_ROOT } from "./lib/resolve-root.mjs";
19
19
 
@@ -27,7 +27,11 @@ const REGISTRY_PATH = join(HOME, ".claude", "cache", "hook-registry.json");
27
27
  function ensureUserRegistry() {
28
28
  if (!existsSync(REGISTRY_PATH) && existsSync(BUNDLED_REGISTRY_PATH)) {
29
29
  mkdirSync(dirname(REGISTRY_PATH), { recursive: true });
30
- writeFileSync(REGISTRY_PATH, readFileSync(BUNDLED_REGISTRY_PATH, "utf8"), "utf8");
30
+ writeFileSync(
31
+ REGISTRY_PATH,
32
+ readFileSync(BUNDLED_REGISTRY_PATH, "utf8"),
33
+ "utf8",
34
+ );
31
35
  }
32
36
  }
33
37
 
@@ -56,7 +60,11 @@ function scan() {
56
60
  ensureUserRegistry();
57
61
  const settings = loadJSON(SETTINGS_PATH);
58
62
  if (!settings?.hooks) {
59
- return { status: "no_hooks", message: "settings.json에 훅이 없습니다.", events: {} };
63
+ return {
64
+ status: "no_hooks",
65
+ message: "settings.json에 훅이 없습니다.",
66
+ events: {},
67
+ };
60
68
  }
61
69
 
62
70
  const registry = loadJSON(REGISTRY_PATH);
@@ -81,7 +89,7 @@ function scan() {
81
89
  // 레지스트리에서 매칭 찾기
82
90
  if (registry?.events?.[event]) {
83
91
  const match = registry.events[event].find(
84
- (r) => normalizeCmd(resolveVars(r.command)) === normalizeCmd(cmd)
92
+ (r) => normalizeCmd(resolveVars(r.command)) === normalizeCmd(cmd),
85
93
  );
86
94
  if (match) {
87
95
  hookInfo.registryMatch = { id: match.id, priority: match.priority };
@@ -100,7 +108,8 @@ function scan() {
100
108
  }
101
109
 
102
110
  function identifySource(cmd) {
103
- if (/triflux/i.test(cmd) || /\$\{?CLAUDE_PLUGIN_ROOT\}?/i.test(cmd)) return "triflux";
111
+ if (/triflux/i.test(cmd) || /\$\{?CLAUDE_PLUGIN_ROOT\}?/i.test(cmd))
112
+ return "triflux";
104
113
  if (/oh-my-claudecode|omc/i.test(cmd)) return "omc";
105
114
  if (/session-vault/i.test(cmd)) return "session-vault";
106
115
  if (/compact-helper/i.test(cmd)) return "compact-helper";
@@ -110,7 +119,12 @@ function identifySource(cmd) {
110
119
  }
111
120
 
112
121
  function normalizeCmd(cmd) {
113
- return cmd.replace(/["']/g, "").replace(/\\/g, "/").replace(/\s+/g, " ").trim().toLowerCase();
122
+ return cmd
123
+ .replace(/["']/g, "")
124
+ .replace(/\\/g, "/")
125
+ .replace(/\s+/g, " ")
126
+ .trim()
127
+ .toLowerCase();
114
128
  }
115
129
 
116
130
  function resolveVars(cmd) {
@@ -140,11 +154,21 @@ function diff() {
140
154
  const currentHooks = settings.hooks[event] || [];
141
155
  const registryHooks = registry.events[event] || [];
142
156
 
143
- const currentCount = currentHooks.reduce((n, m) => n + (m.hooks?.length || 0), 0);
144
- const registryCount = registryHooks.filter((h) => h.enabled !== false).length;
157
+ const currentCount = currentHooks.reduce(
158
+ (n, m) => n + (m.hooks?.length || 0),
159
+ 0,
160
+ );
161
+ const registryCount = registryHooks.filter(
162
+ (h) => h.enabled !== false,
163
+ ).length;
145
164
 
146
165
  if (currentCount === 1 && isOrchestrator(currentHooks)) {
147
- changes.push({ event, action: "already_orchestrated", currentCount, registryCount });
166
+ changes.push({
167
+ event,
168
+ action: "already_orchestrated",
169
+ currentCount,
170
+ registryCount,
171
+ });
148
172
  } else if (currentCount > 0 || registryCount > 0) {
149
173
  changes.push({
150
174
  event,
@@ -162,7 +186,10 @@ function diff() {
162
186
  function isOrchestrator(matchers) {
163
187
  if (!matchers || matchers.length !== 1) return false;
164
188
  const hooks = matchers[0]?.hooks || [];
165
- return hooks.length === 1 && (hooks[0]?.command || "").includes("hook-orchestrator");
189
+ return (
190
+ hooks.length === 1 &&
191
+ (hooks[0]?.command || "").includes("hook-orchestrator")
192
+ );
166
193
  }
167
194
 
168
195
  // ── apply: 오케스트레이터 적용 ──────────────────────────────
@@ -170,14 +197,22 @@ function isOrchestrator(matchers) {
170
197
  function apply() {
171
198
  ensureUserRegistry();
172
199
  const settings = loadJSON(SETTINGS_PATH);
173
- if (!settings) return { status: "error", message: "settings.json을 찾을 수 없습니다." };
200
+ if (!settings)
201
+ return { status: "error", message: "settings.json을 찾을 수 없습니다." };
174
202
 
175
203
  const registry = loadJSON(REGISTRY_PATH);
176
- if (!registry) return { status: "error", message: "hook-registry.json을 찾을 수 없습니다." };
204
+ if (!registry)
205
+ return {
206
+ status: "error",
207
+ message: "hook-registry.json을 찾을 수 없습니다.",
208
+ };
177
209
 
178
210
  // 백업
179
211
  if (settings.hooks && !existsSync(BACKUP_PATH)) {
180
- saveJSON(BACKUP_PATH, { hooks: settings.hooks, backedUpAt: new Date().toISOString() });
212
+ saveJSON(BACKUP_PATH, {
213
+ hooks: settings.hooks,
214
+ backedUpAt: new Date().toISOString(),
215
+ });
181
216
  }
182
217
 
183
218
  // 오케스트레이터 명령 생성
@@ -201,7 +236,8 @@ function apply() {
201
236
  if (enabledEntries.length > 0) {
202
237
  // 레지스트리에 있으면 → 오케스트레이터로 교체
203
238
  // 가장 큰 timeout을 기준으로 오케스트레이터 timeout 설정
204
- const maxTimeout = Math.max(...enabledEntries.map((h) => h.timeout || 10)) + 5;
239
+ const maxTimeout =
240
+ Math.max(...enabledEntries.map((h) => h.timeout || 10)) + 5;
205
241
 
206
242
  newHooks[event] = [
207
243
  {
@@ -238,7 +274,10 @@ function apply() {
238
274
 
239
275
  function restore() {
240
276
  if (!existsSync(BACKUP_PATH)) {
241
- return { status: "no_backup", message: "백업 파일이 없습니다. apply 전에는 복원할 수 없습니다." };
277
+ return {
278
+ status: "no_backup",
279
+ message: "백업 파일이 없습니다. apply 전에는 복원할 수 없습니다.",
280
+ };
242
281
  }
243
282
 
244
283
  const backup = loadJSON(BACKUP_PATH);
@@ -247,7 +286,8 @@ function restore() {
247
286
  }
248
287
 
249
288
  const settings = loadJSON(SETTINGS_PATH);
250
- if (!settings) return { status: "error", message: "settings.json을 찾을 수 없습니다." };
289
+ if (!settings)
290
+ return { status: "error", message: "settings.json을 찾을 수 없습니다." };
251
291
 
252
292
  settings.hooks = backup.hooks;
253
293
  saveJSON(SETTINGS_PATH, settings);
@@ -263,10 +303,12 @@ function restore() {
263
303
  function setPriority(hookId, priority) {
264
304
  ensureUserRegistry();
265
305
  const registry = loadJSON(REGISTRY_PATH);
266
- if (!registry) return { status: "error", message: "레지스트리를 찾을 수 없습니다." };
306
+ if (!registry)
307
+ return { status: "error", message: "레지스트리를 찾을 수 없습니다." };
267
308
 
268
309
  const numPriority = parseInt(priority, 10);
269
- if (isNaN(numPriority)) return { status: "error", message: "priority는 숫자여야 합니다." };
310
+ if (Number.isNaN(numPriority))
311
+ return { status: "error", message: "priority는 숫자여야 합니다." };
270
312
 
271
313
  let found = false;
272
314
  for (const hooks of Object.values(registry.events)) {
@@ -278,10 +320,17 @@ function setPriority(hookId, priority) {
278
320
  }
279
321
  }
280
322
 
281
- if (!found) return { status: "not_found", message: `훅 '${hookId}'를 찾을 수 없습니다.` };
323
+ if (!found)
324
+ return {
325
+ status: "not_found",
326
+ message: `훅 '${hookId}'를 찾을 수 없습니다.`,
327
+ };
282
328
 
283
329
  saveJSON(REGISTRY_PATH, registry);
284
- return { status: "ok", message: `${hookId}의 우선순위가 ${numPriority}로 변경되었습니다.` };
330
+ return {
331
+ status: "ok",
332
+ message: `${hookId}의 우선순위가 ${numPriority}로 변경되었습니다.`,
333
+ };
285
334
  }
286
335
 
287
336
  // ── toggle: 활성/비활성 토글 ────────────────────────────────
@@ -289,7 +338,8 @@ function setPriority(hookId, priority) {
289
338
  function toggle(hookId) {
290
339
  ensureUserRegistry();
291
340
  const registry = loadJSON(REGISTRY_PATH);
292
- if (!registry) return { status: "error", message: "레지스트리를 찾을 수 없습니다." };
341
+ if (!registry)
342
+ return { status: "error", message: "레지스트리를 찾을 수 없습니다." };
293
343
 
294
344
  let found = false;
295
345
  let newState = false;
@@ -303,10 +353,17 @@ function toggle(hookId) {
303
353
  }
304
354
  }
305
355
 
306
- if (!found) return { status: "not_found", message: `훅 '${hookId}'를 찾을 수 없습니다.` };
356
+ if (!found)
357
+ return {
358
+ status: "not_found",
359
+ message: `훅 '${hookId}'를 찾을 수 없습니다.`,
360
+ };
307
361
 
308
362
  saveJSON(REGISTRY_PATH, registry);
309
- return { status: "ok", message: `${hookId}: ${newState ? "활성화" : "비활성화"}` };
363
+ return {
364
+ status: "ok",
365
+ message: `${hookId}: ${newState ? "활성화" : "비활성화"}`,
366
+ };
310
367
  }
311
368
 
312
369
  // ── status: 현재 적용 상태 ──────────────────────────────────
@@ -318,7 +375,7 @@ function status() {
318
375
  let orchestrated = 0;
319
376
  let individual = 0;
320
377
 
321
- for (const [event, matchers] of Object.entries(settings.hooks)) {
378
+ for (const [_event, matchers] of Object.entries(settings.hooks)) {
322
379
  if (isOrchestrator(matchers)) {
323
380
  orchestrated++;
324
381
  } else {
@@ -333,9 +390,10 @@ function status() {
333
390
  orchestratedEvents: orchestrated,
334
391
  individualEvents: individual,
335
392
  hasBackup,
336
- message: orchestrated > 0
337
- ? `오케스트레이터 적용 중: ${orchestrated}개 이벤트 통합, ${individual}개 개별 유지`
338
- : `오케스트레이터 미적용. ${individual}개 이벤트가 개별 훅으로 실행 중`,
393
+ message:
394
+ orchestrated > 0
395
+ ? `오케스트레이터 적용 중: ${orchestrated}개 이벤트 통합, ${individual}개 개별 유지`
396
+ : `오케스트레이터 미적용. ${individual}개 이벤트가 개별 훅으로 실행 중`,
339
397
  };
340
398
  }
341
399
 
@@ -354,10 +412,13 @@ const commands = {
354
412
  };
355
413
 
356
414
  if (!command || !commands[command]) {
357
- console.log(JSON.stringify({
358
- error: "사용법: node hook-manager.mjs <scan|diff|apply|restore|set-priority|toggle|status>",
359
- commands: Object.keys(commands),
360
- }));
415
+ console.log(
416
+ JSON.stringify({
417
+ error:
418
+ "사용법: node hook-manager.mjs <scan|diff|apply|restore|set-priority|toggle|status>",
419
+ commands: Object.keys(commands),
420
+ }),
421
+ );
361
422
  process.exit(1);
362
423
  }
363
424