triflux 10.4.0 → 10.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/.claude-plugin/marketplace.json +34 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/bin/tfx-doctor-tui.mjs +1 -1
  4. package/bin/tfx-doctor.mjs +6 -1
  5. package/bin/tfx-profile.mjs +1 -1
  6. package/bin/tfx-setup-tui.mjs +1 -1
  7. package/bin/tfx-setup.mjs +6 -1
  8. package/bin/triflux.mjs +2396 -1140
  9. package/config/mcp-registry.json +29 -0
  10. package/hooks/agent-route-guard.mjs +12 -8
  11. package/hooks/cross-review-tracker.mjs +21 -8
  12. package/hooks/error-context.mjs +19 -7
  13. package/hooks/hook-adaptive-collector.mjs +18 -16
  14. package/hooks/hook-manager.mjs +93 -32
  15. package/hooks/hook-orchestrator.mjs +108 -24
  16. package/hooks/hook-registry.json +267 -256
  17. package/hooks/keyword-rules.json +6 -10
  18. package/hooks/lib/resolve-root.mjs +1 -1
  19. package/hooks/mcp-config-watcher.mjs +6 -2
  20. package/hooks/pipeline-stop.mjs +3 -6
  21. package/hooks/safety-guard.mjs +99 -28
  22. package/hooks/session-start-fast.mjs +143 -0
  23. package/hooks/subagent-verifier.mjs +5 -4
  24. package/hub/account-broker.mjs +256 -60
  25. package/hub/adaptive-diagnostic.mjs +75 -48
  26. package/hub/adaptive-inject.mjs +96 -58
  27. package/hub/adaptive-memory.mjs +156 -42
  28. package/hub/adaptive.mjs +60 -31
  29. package/hub/assign-callbacks.mjs +163 -126
  30. package/hub/bridge.mjs +0 -1
  31. package/hub/cli-adapter-base.mjs +200 -48
  32. package/hub/codex-adapter.mjs +76 -96
  33. package/hub/codex-compat.mjs +3 -3
  34. package/hub/codex-preflight.mjs +63 -37
  35. package/hub/delegator/contracts.mjs +19 -23
  36. package/hub/delegator/index.mjs +14 -14
  37. package/hub/delegator/service.mjs +88 -64
  38. package/hub/delegator/tool-definitions.mjs +35 -35
  39. package/hub/fullcycle.mjs +33 -17
  40. package/hub/gemini-adapter.mjs +69 -94
  41. package/hub/hitl.mjs +202 -143
  42. package/hub/intent.mjs +161 -38
  43. package/hub/lib/cache-guard.mjs +43 -17
  44. package/hub/lib/mcp-response-cache.mjs +66 -32
  45. package/hub/lib/memory-store.mjs +285 -111
  46. package/hub/lib/path-utils.mjs +35 -37
  47. package/hub/lib/process-utils.mjs +106 -37
  48. package/hub/lib/spawn-trace.mjs +527 -0
  49. package/hub/lib/ssh-command.mjs +34 -4
  50. package/hub/lib/ssh-retry.mjs +5 -1
  51. package/hub/lib/uuidv7.mjs +4 -3
  52. package/hub/memory-doctor.mjs +266 -106
  53. package/hub/middleware/request-logger.mjs +61 -34
  54. package/hub/paths.mjs +9 -9
  55. package/hub/pipeline/gates/confidence.mjs +34 -15
  56. package/hub/pipeline/gates/consensus.mjs +27 -15
  57. package/hub/pipeline/gates/index.mjs +7 -3
  58. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  59. package/hub/pipeline/index.mjs +77 -42
  60. package/hub/pipeline/state.mjs +10 -10
  61. package/hub/pipeline/transitions.mjs +40 -23
  62. package/hub/platform.mjs +57 -48
  63. package/hub/promote-penalties.mjs +25 -7
  64. package/hub/quality/deslop.mjs +70 -49
  65. package/hub/research.mjs +32 -25
  66. package/hub/router.mjs +924 -791
  67. package/hub/routing/complexity.mjs +132 -29
  68. package/hub/routing/index.mjs +17 -12
  69. package/hub/routing/q-learning.mjs +76 -28
  70. package/hub/server.mjs +4 -4
  71. package/hub/session-fingerprint.mjs +127 -61
  72. package/hub/state.mjs +84 -43
  73. package/hub/store-adapter.mjs +59 -26
  74. package/hub/store.mjs +356 -153
  75. package/hub/team/agent-map.json +22 -7
  76. package/hub/team/ansi.mjs +186 -122
  77. package/hub/team/backend.mjs +28 -10
  78. package/hub/team/cli/commands/attach.mjs +57 -37
  79. package/hub/team/cli/commands/control.mjs +29 -8
  80. package/hub/team/cli/commands/debug.mjs +95 -74
  81. package/hub/team/cli/commands/focus.mjs +80 -53
  82. package/hub/team/cli/commands/interrupt.mjs +18 -6
  83. package/hub/team/cli/commands/kill.mjs +16 -5
  84. package/hub/team/cli/commands/list.mjs +31 -24
  85. package/hub/team/cli/commands/send.mjs +19 -6
  86. package/hub/team/cli/commands/start/index.mjs +154 -31
  87. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  88. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  89. package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
  90. package/hub/team/cli/commands/start/start-mux.mjs +122 -73
  91. package/hub/team/cli/commands/start/start-wt.mjs +86 -69
  92. package/hub/team/cli/commands/status.mjs +43 -14
  93. package/hub/team/cli/commands/stop.mjs +11 -4
  94. package/hub/team/cli/commands/task.mjs +8 -3
  95. package/hub/team/cli/commands/tasks.mjs +13 -13
  96. package/hub/team/cli/index.mjs +2 -2
  97. package/hub/team/cli/manifest.mjs +38 -8
  98. package/hub/team/cli/render.mjs +52 -30
  99. package/hub/team/cli/services/attach-fallback.mjs +74 -54
  100. package/hub/team/cli/services/hub-client.mjs +42 -14
  101. package/hub/team/cli/services/member-selector.mjs +37 -30
  102. package/hub/team/cli/services/native-control.mjs +143 -116
  103. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  104. package/hub/team/cli/services/state-store.mjs +25 -8
  105. package/hub/team/cli/services/task-model.mjs +40 -30
  106. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  107. package/hub/team/conductor.mjs +8 -4
  108. package/hub/team/dashboard-anchor.mjs +4 -5
  109. package/hub/team/dashboard-layout.mjs +3 -1
  110. package/hub/team/dashboard-open.mjs +41 -21
  111. package/hub/team/dashboard.mjs +76 -28
  112. package/hub/team/event-log.mjs +18 -10
  113. package/hub/team/handoff.mjs +31 -15
  114. package/hub/team/headless.mjs +2 -1
  115. package/hub/team/health-probe.mjs +69 -54
  116. package/hub/team/launcher-template.mjs +16 -13
  117. package/hub/team/native-supervisor.mjs +65 -21
  118. package/hub/team/native.mjs +74 -35
  119. package/hub/team/nativeProxy.mjs +184 -113
  120. package/hub/team/notify.mjs +120 -77
  121. package/hub/team/orchestrator.mjs +166 -161
  122. package/hub/team/pane.mjs +12 -7
  123. package/hub/team/process-cleanup.mjs +25 -16
  124. package/hub/team/psmux.mjs +491 -201
  125. package/hub/team/remote-probe.mjs +68 -52
  126. package/hub/team/remote-session.mjs +117 -59
  127. package/hub/team/remote-watcher.mjs +61 -33
  128. package/hub/team/routing.mjs +51 -25
  129. package/hub/team/runtime-strategy.mjs +3 -1
  130. package/hub/team/session.mjs +675 -611
  131. package/hub/team/shared.mjs +13 -13
  132. package/hub/team/staleState.mjs +72 -30
  133. package/hub/team/swarm-locks.mjs +15 -13
  134. package/hub/team/swarm-planner.mjs +32 -21
  135. package/hub/team/swarm-reconciler.mjs +48 -23
  136. package/hub/team/tui-lite.mjs +266 -68
  137. package/hub/team/tui-remote-adapter.mjs +14 -10
  138. package/hub/team/tui-viewer.mjs +99 -43
  139. package/hub/team/tui.mjs +708 -271
  140. package/hub/team/worktree-lifecycle.mjs +152 -58
  141. package/hub/team/wt-manager.mjs +24 -14
  142. package/hub/token-mode.mjs +71 -71
  143. package/hub/tray.mjs +411 -368
  144. package/hub/workers/claude-worker.mjs +162 -118
  145. package/hub/workers/codex-mcp.mjs +558 -507
  146. package/hub/workers/delegator-mcp.mjs +507 -333
  147. package/hub/workers/factory.mjs +21 -21
  148. package/hub/workers/gemini-worker.mjs +115 -84
  149. package/hub/workers/interface.mjs +6 -1
  150. package/hub/workers/worker-utils.mjs +21 -14
  151. package/hud/colors.mjs +27 -9
  152. package/hud/constants.mjs +162 -26
  153. package/hud/context-monitor.mjs +82 -41
  154. package/hud/hud-qos-status.mjs +129 -49
  155. package/hud/mission-board.mjs +6 -3
  156. package/hud/providers/claude.mjs +226 -115
  157. package/hud/providers/codex.mjs +62 -22
  158. package/hud/providers/gemini.mjs +168 -56
  159. package/hud/renderers.mjs +384 -119
  160. package/hud/terminal.mjs +101 -31
  161. package/hud/utils.mjs +78 -38
  162. package/mesh/index.mjs +11 -5
  163. package/mesh/mesh-budget.mjs +18 -9
  164. package/mesh/mesh-heartbeat.mjs +1 -1
  165. package/mesh/mesh-queue.mjs +3 -5
  166. package/mesh/mesh-router.mjs +5 -4
  167. package/package.json +56 -21
  168. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  169. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  170. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  171. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  172. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  173. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  174. package/scripts/__tests__/skill-template.test.mjs +98 -50
  175. package/scripts/__tests__/smoke.test.mjs +1 -1
  176. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  177. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  178. package/scripts/cache-doctor.mjs +11 -4
  179. package/scripts/cache-warmup.mjs +96 -37
  180. package/scripts/claudemd-sync.mjs +27 -17
  181. package/scripts/codex-gateway-preflight.mjs +52 -37
  182. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +139 -120
  193. package/scripts/keyword-detector.mjs +293 -272
  194. package/scripts/keyword-rules-expander.mjs +538 -521
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +355 -118
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +669 -554
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +61 -57
  229. package/scripts/tfx-batch-stats.mjs +117 -96
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/idle-notif-cooldown.json +1 -1
  237. package/skills/.omc/state/last-tool-error.json +1 -1
  238. package/skills/.omc/state/subagent-tracking.json +1 -1
  239. package/skills/_templates/base.md +1 -6
  240. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  241. package/skills/shared/telemetry-segment.md +6 -0
  242. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  243. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  244. package/skills/tfx-analysis/skill.json +1 -6
  245. package/skills/tfx-auto/SKILL.md +1 -0
  246. package/skills/{tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md → tfx-auto-codex/SKILL.md.tmpl} +33 -4
  247. package/skills/tfx-auto-codex/skill.json +1 -3
  248. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  249. package/skills/tfx-autopilot/skill.json +1 -5
  250. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  251. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  252. package/skills/tfx-autoroute/skill.json +1 -7
  253. package/skills/tfx-codex/SKILL.md +1 -0
  254. package/skills/tfx-codex/skill.json +1 -3
  255. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  256. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  257. package/skills/tfx-codex-swarm/skill.json +1 -4
  258. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  265. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  266. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  267. package/skills/tfx-debate/skill.json +1 -7
  268. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  269. package/skills/tfx-deep-analysis/skill.json +1 -5
  270. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  271. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  272. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  273. package/skills/tfx-deep-qa/skill.json +1 -6
  274. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  275. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  276. package/skills/tfx-doctor/SKILL.md +21 -0
  277. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  278. package/skills/tfx-doctor/skill.json +1 -3
  279. package/skills/tfx-find/SKILL.md +1 -0
  280. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  281. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  282. package/skills/tfx-fullcycle/skill.json +1 -6
  283. package/skills/{tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md → tfx-gemini/SKILL.md.tmpl} +16 -7
  284. package/skills/tfx-gemini/skill.json +1 -3
  285. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  286. package/skills/tfx-hooks/skill.json +1 -3
  287. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  288. package/skills/tfx-hub/skill.json +1 -3
  289. package/skills/tfx-index/SKILL.md +1 -0
  290. package/skills/tfx-index/skill.json +1 -6
  291. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  292. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  293. package/skills/tfx-multi/skill.json +1 -3
  294. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  295. package/skills/tfx-panel/skill.json +1 -7
  296. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  297. package/skills/tfx-persist/skill.json +1 -7
  298. package/skills/tfx-plan/SKILL.md +1 -0
  299. package/skills/tfx-plan/skill.json +1 -6
  300. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  301. package/skills/tfx-profile/skill.json +1 -3
  302. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  303. package/skills/tfx-prune/skill.json +1 -7
  304. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  305. package/skills/tfx-psmux-rules/skill.json +1 -4
  306. package/skills/tfx-qa/SKILL.md +1 -0
  307. package/skills/tfx-qa/skill.json +1 -6
  308. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  309. package/skills/tfx-ralph/skill.json +1 -4
  310. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  311. package/skills/tfx-remote-setup/skill.json +1 -3
  312. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  313. package/skills/tfx-remote-spawn/references/hosts.json +16 -16
  314. package/skills/tfx-remote-spawn/skill.json +1 -4
  315. package/skills/tfx-research/SKILL.md +1 -0
  316. package/skills/tfx-review/SKILL.md +1 -0
  317. package/skills/tfx-review/skill.json +1 -6
  318. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  319. package/skills/tfx-setup/skill.json +1 -3
  320. package/skills/tfx-swarm/SKILL.md +22 -0
  321. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  322. package/tui/codex-profile.mjs +457 -0
  323. package/tui/core.mjs +266 -0
  324. package/tui/doctor.mjs +375 -0
  325. package/tui/gemini-profile.mjs +299 -0
  326. package/tui/monitor-data.mjs +152 -0
  327. package/tui/monitor.mjs +339 -0
  328. package/tui/setup.mjs +533 -0
  329. package/CLAUDE.md +0 -193
  330. package/references/hosts.json +0 -33
  331. package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
  332. package/skills/tfx-workspace/evals/evals.json +0 -79
  333. package/skills/tfx-workspace/iteration-1/benchmark.json +0 -162
  334. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
  335. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -9
  336. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
  337. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
  338. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -9
  339. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
  340. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
  341. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
  342. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -9
  343. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
  344. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
  345. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -9
  346. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
  347. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
  348. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
  349. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -8
  350. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
  351. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
  352. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -8
  353. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
  354. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
  355. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
  356. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -10
  357. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
  358. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
  359. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -10
  360. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
  361. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
  362. package/skills/tfx-workspace/iteration-1/review.html +0 -1325
  363. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
  364. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -10
  365. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
  366. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
  367. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -10
  368. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
  369. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
  370. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
  371. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -10
  372. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
  373. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
  374. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -10
  375. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
  376. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
  377. package/skills/tfx-workspace/iteration-2/benchmark.json +0 -62
  378. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
  379. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -11
  380. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
  381. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
  382. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -11
  383. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
  384. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
  385. package/skills/tfx-workspace/iteration-2/review.html +0 -1325
  386. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
  387. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
  388. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
  389. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
  390. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
  391. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
@@ -12,15 +12,15 @@
12
12
  // after: [mcp_servers.context7]
13
13
  // url = "http://127.0.0.1:8100/sse"
14
14
 
15
- import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from 'node:fs';
16
- import { join, dirname } from 'node:path';
17
- import { homedir } from 'node:os';
18
- import { fileURLToPath } from 'node:url';
15
+ import { copyFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
16
+ import { homedir } from "node:os";
17
+ import { dirname, join } from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
19
 
20
- import { SERVERS } from './mcp-gateway-start.mjs';
20
+ import { SERVERS } from "./mcp-gateway-start.mjs";
21
21
 
22
- const CODEX_CONFIG = join(homedir(), '.codex', 'config.toml');
23
- const BACKUP_SUFFIX = '.pre-gateway.bak';
22
+ const CODEX_CONFIG = join(homedir(), ".codex", "config.toml");
23
+ const BACKUP_SUFFIX = ".pre-gateway.bak";
24
24
 
25
25
  // gateway 서버 → SSE URL 매핑
26
26
  const GATEWAY_MAP = new Map(
@@ -28,9 +28,13 @@ const GATEWAY_MAP = new Map(
28
28
  );
29
29
 
30
30
  // stdio 정의를 보존해야 하는 MCP 서버 (gateway 대상 아님)
31
- const KEEP_STDIO = new Set([
32
- 'omx_state', 'omx_memory', 'omx_code_intel', 'omx_trace', 'omx_team_run',
33
- 'tfx-hub',
31
+ const _KEEP_STDIO = new Set([
32
+ "omx_state",
33
+ "omx_memory",
34
+ "omx_code_intel",
35
+ "omx_trace",
36
+ "omx_team_run",
37
+ "tfx-hub",
34
38
  ]);
35
39
 
36
40
  function parseTomlMcpServers(content) {
@@ -43,13 +47,21 @@ function parseTomlMcpServers(content) {
43
47
  const startIdx = match.index + match[0].length;
44
48
 
45
49
  // 다음 [section] 또는 파일 끝까지가 이 서버의 범위
46
- const nextSection = content.indexOf('\n[', startIdx);
47
- const block = content.slice(startIdx, nextSection === -1 ? undefined : nextSection).trim();
50
+ const nextSection = content.indexOf("\n[", startIdx);
51
+ const block = content
52
+ .slice(startIdx, nextSection === -1 ? undefined : nextSection)
53
+ .trim();
48
54
 
49
55
  const hasUrl = /^url\s*=/m.test(block);
50
56
  const hasCommand = /^command\s*=/m.test(block);
51
57
 
52
- servers.set(name, { block, hasUrl, hasCommand, startIdx, headerIdx: match.index });
58
+ servers.set(name, {
59
+ block,
60
+ hasUrl,
61
+ hasCommand,
62
+ startIdx,
63
+ headerIdx: match.index,
64
+ });
53
65
  }
54
66
 
55
67
  return servers;
@@ -59,7 +71,7 @@ function buildSseEntry(name, url) {
59
71
  return `[mcp_servers.${name}]\nurl = "${url}"\n`;
60
72
  }
61
73
 
62
- function buildStdioEntry(name, block) {
74
+ function _buildStdioEntry(name, block) {
63
75
  return `[mcp_servers.${name}]\n${block}\n`;
64
76
  }
65
77
 
@@ -67,11 +79,11 @@ function buildStdioEntry(name, block) {
67
79
 
68
80
  export function enableGateway() {
69
81
  if (!existsSync(CODEX_CONFIG)) {
70
- console.log('[SKIP] ~/.codex/config.toml not found');
82
+ console.log("[SKIP] ~/.codex/config.toml not found");
71
83
  return { changed: 0, skipped: 0 };
72
84
  }
73
85
 
74
- const original = readFileSync(CODEX_CONFIG, 'utf8');
86
+ const original = readFileSync(CODEX_CONFIG, "utf8");
75
87
  const servers = parseTomlMcpServers(original);
76
88
 
77
89
  // 백업 (최초 1회만)
@@ -113,18 +125,22 @@ export function enableGateway() {
113
125
  }
114
126
 
115
127
  // preflight MCP 서버 등록 — Codex 시작 시 gateway 자동 기동 보장
116
- const preflightName = 'tfx-gateway-preflight';
128
+ const preflightName = "tfx-gateway-preflight";
117
129
  if (!servers.has(preflightName)) {
118
- const resolvedPath = join(dirname(fileURLToPath(import.meta.url)), 'codex-gateway-preflight.mjs')
119
- .replace(/\\/g, '\\\\');
130
+ const resolvedPath = join(
131
+ dirname(fileURLToPath(import.meta.url)),
132
+ "codex-gateway-preflight.mjs",
133
+ ).replace(/\\/g, "\\\\");
120
134
  content += `\n[mcp_servers.${preflightName}]\ncommand = "node"\nargs = ["${resolvedPath}"]\nenabled = true\nstartup_timeout_sec = 10\n`;
121
135
  changed++;
122
136
  console.log(`[ADD] ${preflightName} — Codex 시작 시 gateway 자동 기동`);
123
137
  }
124
138
 
125
139
  if (changed > 0) {
126
- writeFileSync(CODEX_CONFIG, content, 'utf8');
127
- console.log(`\n[DONE] ${changed} servers converted, ${skipped} already SSE`);
140
+ writeFileSync(CODEX_CONFIG, content, "utf8");
141
+ console.log(
142
+ `\n[DONE] ${changed} servers converted, ${skipped} already SSE`,
143
+ );
128
144
  } else {
129
145
  console.log(`\n[DONE] No changes needed (${skipped} already SSE)`);
130
146
  }
@@ -137,12 +153,12 @@ export function enableGateway() {
137
153
  export function disableGateway() {
138
154
  const backupPath = CODEX_CONFIG + BACKUP_SUFFIX;
139
155
  if (!existsSync(backupPath)) {
140
- console.log('[SKIP] No backup found — nothing to restore');
156
+ console.log("[SKIP] No backup found — nothing to restore");
141
157
  return false;
142
158
  }
143
159
 
144
160
  copyFileSync(backupPath, CODEX_CONFIG);
145
- console.log('[RESTORE] config.toml restored from pre-gateway backup');
161
+ console.log("[RESTORE] config.toml restored from pre-gateway backup");
146
162
  return true;
147
163
  }
148
164
 
@@ -153,18 +169,18 @@ export function getStatus() {
153
169
  return { exists: false, servers: [] };
154
170
  }
155
171
 
156
- const content = readFileSync(CODEX_CONFIG, 'utf8');
172
+ const content = readFileSync(CODEX_CONFIG, "utf8");
157
173
  const servers = parseTomlMcpServers(content);
158
174
  const result = [];
159
175
 
160
176
  for (const [name, url] of GATEWAY_MAP) {
161
177
  const srv = servers.get(name);
162
178
  if (!srv) {
163
- result.push({ name, mode: 'missing', url });
179
+ result.push({ name, mode: "missing", url });
164
180
  } else if (srv.hasUrl) {
165
- result.push({ name, mode: 'sse', url });
181
+ result.push({ name, mode: "sse", url });
166
182
  } else {
167
- result.push({ name, mode: 'stdio', url });
183
+ result.push({ name, mode: "stdio", url });
168
184
  }
169
185
  }
170
186
 
@@ -175,25 +191,29 @@ export function getStatus() {
175
191
 
176
192
  const arg = process.argv[2];
177
193
 
178
- if (arg === '--enable') {
194
+ if (arg === "--enable") {
179
195
  enableGateway();
180
- } else if (arg === '--disable') {
196
+ } else if (arg === "--disable") {
181
197
  disableGateway();
182
- } else if (arg === '--status') {
198
+ } else if (arg === "--status") {
183
199
  const { exists, servers } = getStatus();
184
200
  if (!exists) {
185
- console.log('config.toml not found');
201
+ console.log("config.toml not found");
186
202
  process.exit(1);
187
203
  }
188
- console.log('\nCodex MCP Gateway Status:');
189
- console.log(''.repeat(50));
204
+ console.log("\nCodex MCP Gateway Status:");
205
+ console.log("".repeat(50));
190
206
  for (const s of servers) {
191
- const icon = s.mode === 'sse' ? '' : s.mode === 'stdio' ? '⚠️' : '';
192
- console.log(`${icon} ${s.name.padEnd(15)} ${s.mode.padEnd(8)} ${s.mode === 'stdio' ? '← zombie risk' : ''}`);
207
+ const icon = s.mode === "sse" ? "" : s.mode === "stdio" ? "⚠️" : "";
208
+ console.log(
209
+ `${icon} ${s.name.padEnd(15)} ${s.mode.padEnd(8)} ${s.mode === "stdio" ? "← zombie risk" : ""}`,
210
+ );
193
211
  }
194
212
  } else {
195
- console.log('Usage: codex-mcp-gateway-sync.mjs [--enable|--disable|--status]');
196
- console.log(' --enable Convert stdio MCP servers to SSE gateway URLs');
197
- console.log(' --disable Restore original stdio config from backup');
198
- console.log(' --status Show current MCP connection mode per server');
213
+ console.log(
214
+ "Usage: codex-mcp-gateway-sync.mjs [--enable|--disable|--status]",
215
+ );
216
+ console.log(" --enable Convert stdio MCP servers to SSE gateway URLs");
217
+ console.log(" --disable Restore original stdio config from backup");
218
+ console.log(" --status Show current MCP connection mode per server");
199
219
  }
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env node
2
+ // scripts/config-audit.mjs — 설정 정적 보안/성능 감사
3
+ //
4
+ // triflux doctor --audit 또는 단독 실행.
5
+ // settings.json, CLAUDE.md, MCP, 훅 설정을 스캔하여 위험/성능 이슈 탐지.
6
+ //
7
+ // 출력: JSON (--json) 또는 마크다운 테이블
8
+
9
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import { join, basename } from "node:path";
12
+
13
+ const HOME = homedir();
14
+ const CLAUDE_DIR = join(HOME, ".claude");
15
+ const JSON_OUTPUT = process.argv.includes("--json");
16
+
17
+ // ── 결과 수집 ──
18
+ const findings = [];
19
+
20
+ function addFinding(category, severity, message, detail = "") {
21
+ findings.push({ category, severity, message, detail });
22
+ }
23
+
24
+ // ── 1. settings.json 감사 ──
25
+ function auditSettings() {
26
+ const settingsPath = join(CLAUDE_DIR, "settings.json");
27
+ if (!existsSync(settingsPath)) {
28
+ addFinding("settings", "info", "settings.json 없음", settingsPath);
29
+ return;
30
+ }
31
+
32
+ let settings;
33
+ try {
34
+ settings = JSON.parse(readFileSync(settingsPath, "utf8"));
35
+ } catch {
36
+ addFinding("settings", "warn", "settings.json 파싱 실패", settingsPath);
37
+ return;
38
+ }
39
+
40
+ // 권한 모드
41
+ const mode = settings.permissions?.defaultMode;
42
+ if (mode === "bypassPermissions" || mode === "acceptEdits") {
43
+ addFinding("settings", "warn", `defaultMode: "${mode}" — 도구 승인 없이 실행됨`, "보안 리뷰 시 주의");
44
+ }
45
+
46
+ // 환경 변수 내 시크릿 패턴
47
+ const env = settings.env || {};
48
+ const SECRET_PATTERNS = [
49
+ /(?:api[_-]?key|secret|token|password|credential)s?\s*[:=]/i,
50
+ /(?:sk-|ghp_|gho_|github_pat_|xoxb-|xoxp-)/i,
51
+ /ANTHROPIC_API_KEY/i,
52
+ /OPENAI_API_KEY/i,
53
+ ];
54
+ for (const [key, value] of Object.entries(env)) {
55
+ for (const pattern of SECRET_PATTERNS) {
56
+ if (pattern.test(key) || pattern.test(String(value))) {
57
+ addFinding("settings", "critical", `env에 시크릿 패턴 감지: ${key}`, "환경변수로 분리 권장");
58
+ break;
59
+ }
60
+ }
61
+ }
62
+
63
+ // 훅 timeout 검사
64
+ const hooks = settings.hooks || {};
65
+ let totalHooks = 0;
66
+ let longTimeouts = 0;
67
+ for (const [event, matchers] of Object.entries(hooks)) {
68
+ for (const matcher of Array.isArray(matchers) ? matchers : []) {
69
+ for (const hook of matcher.hooks || []) {
70
+ totalHooks++;
71
+ if (hook.timeout && hook.timeout > 15) {
72
+ longTimeouts++;
73
+ addFinding("hooks", "warn", `${event} 훅 timeout ${hook.timeout}s (>15s)`, hook.command?.slice(0, 80));
74
+ }
75
+ }
76
+ }
77
+ }
78
+ if (totalHooks > 10) {
79
+ addFinding("hooks", "info", `settings.json에 훅 ${totalHooks}개 등록`, "SessionStart 성능에 영향 가능");
80
+ }
81
+ }
82
+
83
+ // ── 2. CLAUDE.md 시크릿 스캔 ──
84
+ function auditClaudeMd() {
85
+ const candidates = [
86
+ join(process.cwd(), "CLAUDE.md"),
87
+ join(CLAUDE_DIR, "CLAUDE.md"),
88
+ ];
89
+
90
+ for (const mdPath of candidates) {
91
+ if (!existsSync(mdPath)) continue;
92
+ let content;
93
+ try {
94
+ content = readFileSync(mdPath, "utf8");
95
+ } catch { continue; }
96
+
97
+ const SECRET_LINE_PATTERNS = [
98
+ /(?:api[_-]?key|secret|password)\s*[:=]\s*["']?\S{8,}/i,
99
+ /(?:^|[\s"'=:])(?:sk-|ghp_|gho_|github_pat_|xoxb-|xoxp-)[A-Za-z0-9_-]{10,}/,
100
+ /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/,
101
+ ];
102
+
103
+ const lines = content.split("\n");
104
+ for (let i = 0; i < lines.length; i++) {
105
+ for (const pattern of SECRET_LINE_PATTERNS) {
106
+ if (pattern.test(lines[i])) {
107
+ addFinding("claude-md", "critical", `CLAUDE.md:${i + 1} 시크릿 패턴 감지`, basename(mdPath));
108
+ break;
109
+ }
110
+ }
111
+ }
112
+
113
+ // 길이 경고
114
+ const tokenEstimate = Math.ceil(Buffer.byteLength(content, "utf8") / 4);
115
+ if (tokenEstimate > 3000) {
116
+ addFinding("claude-md", "warn", `CLAUDE.md ~${tokenEstimate} 토큰 (>3000)`, `${basename(mdPath)} — 컨텍스트 로트 위험`);
117
+ }
118
+ }
119
+ }
120
+
121
+ // ── 3. MCP 설정 감사 ──
122
+ function auditMcp() {
123
+ const mcpPaths = [
124
+ join(CLAUDE_DIR, "mcp_servers.json"),
125
+ join(CLAUDE_DIR, ".mcp.json"),
126
+ join(process.cwd(), ".mcp.json"),
127
+ ];
128
+
129
+ let totalServers = 0;
130
+ let stdioCount = 0;
131
+
132
+ for (const mcpPath of mcpPaths) {
133
+ if (!existsSync(mcpPath)) continue;
134
+ let config;
135
+ try {
136
+ config = JSON.parse(readFileSync(mcpPath, "utf8"));
137
+ } catch { continue; }
138
+
139
+ const servers = config.mcpServers || config.servers || config;
140
+ for (const [name, def] of Object.entries(servers)) {
141
+ if (!def || typeof def !== "object") continue;
142
+ totalServers++;
143
+
144
+ if (def.type === "stdio" || def.command) {
145
+ stdioCount++;
146
+ }
147
+
148
+ // 위험 패턴: 쉘 실행, eval, 알 수 없는 npx 패키지
149
+ const cmd = String(def.command || "");
150
+ if (/\beval\b|\bexec\b.*sh\b|\bcurl\b.*\|\s*(?:bash|sh)\b/i.test(cmd)) {
151
+ addFinding("mcp", "critical", `MCP "${name}": 위험한 명령 패턴`, cmd.slice(0, 100));
152
+ }
153
+ }
154
+ }
155
+
156
+ if (totalServers > 10) {
157
+ addFinding("mcp", "warn", `MCP 서버 ${totalServers}개 등록 (>10)`, "컨텍스트 윈도우 압박 가능");
158
+ }
159
+ if (stdioCount > 5) {
160
+ addFinding("mcp", "info", `stdio MCP ${stdioCount}개 — 프로세스 spawn 부하`, "불필요한 서버 비활성화 권장");
161
+ }
162
+ }
163
+
164
+ // ── 4. 훅 레지스트리 감사 ──
165
+ function auditHookRegistry() {
166
+ const registryPath = join(process.cwd(), "hooks", "hook-registry.json");
167
+ if (!existsSync(registryPath)) return;
168
+
169
+ let registry;
170
+ try {
171
+ registry = JSON.parse(readFileSync(registryPath, "utf8"));
172
+ } catch { return; }
173
+
174
+ const events = registry.events || {};
175
+ let blockingCount = 0;
176
+ let externalCount = 0;
177
+
178
+ for (const [event, hooks] of Object.entries(events)) {
179
+ for (const hook of hooks) {
180
+ if (hook.blocking) blockingCount++;
181
+ if (hook.source !== "triflux" && hook.source !== "omc") externalCount++;
182
+
183
+ // 외부 훅의 명령에 위험 패턴
184
+ if (hook.source !== "triflux") {
185
+ const cmd = String(hook.command || "");
186
+ if (/\brm\b|\bdel\b|\bformat\b|\bgit\s+push\b/i.test(cmd)) {
187
+ addFinding("hooks", "warn", `외부 훅 "${hook.id}": 위험 명령 패턴`, cmd.slice(0, 80));
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ if (blockingCount > 5) {
194
+ addFinding("hooks", "info", `blocking 훅 ${blockingCount}개 — 도구 실행 지연 가능`, "불필요한 blocking 해제 검토");
195
+ }
196
+ if (externalCount > 0) {
197
+ addFinding("hooks", "info", `외부 훅 ${externalCount}개 등록`, "출처 확인 권장");
198
+ }
199
+ }
200
+
201
+ // ── 실행 ──
202
+ auditSettings();
203
+ auditClaudeMd();
204
+ auditMcp();
205
+ auditHookRegistry();
206
+
207
+ // ── 출력 ──
208
+ const summary = {
209
+ total: findings.length,
210
+ critical: findings.filter(f => f.severity === "critical").length,
211
+ warn: findings.filter(f => f.severity === "warn").length,
212
+ info: findings.filter(f => f.severity === "info").length,
213
+ };
214
+
215
+ if (JSON_OUTPUT) {
216
+ console.log(JSON.stringify({ summary, findings }, null, 2));
217
+ } else {
218
+ const SEVERITY_ICON = { critical: "🔴", warn: "🟡", info: "🔵" };
219
+ console.log("\n ⬡ triflux config audit\n");
220
+ if (findings.length === 0) {
221
+ console.log(" ✅ 이슈 없음\n");
222
+ } else {
223
+ console.log(` | 심각도 | 카테고리 | 이슈 | 상세 |`);
224
+ console.log(` |--------|----------|------|------|`);
225
+ for (const f of findings) {
226
+ console.log(` | ${SEVERITY_ICON[f.severity] || "⚪"} ${f.severity} | ${f.category} | ${f.message} | ${f.detail} |`);
227
+ }
228
+ console.log(`\n 합계: critical=${summary.critical} warn=${summary.warn} info=${summary.info}\n`);
229
+ }
230
+ }
231
+
232
+ process.exit(summary.critical > 0 ? 1 : 0);
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Converts SKILL.md files to SKILL.md.tmpl format.
4
+ * - Replaces skill name in title with {{SKILL_NAME}}
5
+ * - Removes expanded base block (ARGUMENTS + Telemetry)
6
+ * - Inserts {{> base}} after the title line
7
+ */
8
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs";
9
+ import { join } from "path";
10
+
11
+ const SKILLS_DIR = join(import.meta.dirname, "..", "skills");
12
+
13
+ const BASE_BLOCK_RE =
14
+ /\n> \*\*ARGUMENTS 처리\*\*.*?\n> 워크플로우의 첫 단계.*?기존 절차대로.*?\n\n> \*\*Telemetry\*\*\n>\n> - Skill:.*?\n> - Description:.*?\n> - Session:.*?\n> - Errors:.*?\n/s;
15
+
16
+ let converted = 0;
17
+ let skipped = 0;
18
+
19
+ for (const name of readdirSync(SKILLS_DIR)) {
20
+ const dir = join(SKILLS_DIR, name);
21
+ const skillMd = join(dir, "SKILL.md");
22
+ const tmplMd = join(dir, "SKILL.md.tmpl");
23
+
24
+ if (name.startsWith("_")) continue;
25
+ if (!existsSync(skillMd)) continue;
26
+ if (existsSync(tmplMd)) {
27
+ skipped++;
28
+ continue;
29
+ }
30
+
31
+ let content = readFileSync(skillMd, "utf8");
32
+
33
+ // Replace skill name in title: # tfx-foo — Title -> # {{SKILL_NAME}} — Title
34
+ content = content.replace(
35
+ new RegExp(`^(# )${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}( —)`, "m"),
36
+ "$1{{SKILL_NAME}}$2",
37
+ );
38
+
39
+ // Remove expanded base block if present
40
+ if (BASE_BLOCK_RE.test(content)) {
41
+ content = content.replace(BASE_BLOCK_RE, "\n\n{{> base}}\n\n");
42
+ } else {
43
+ // Insert {{> base}} after the title line
44
+ content = content.replace(/^(# .+\n)/, "$1\n{{> base}}\n");
45
+ }
46
+
47
+ writeFileSync(tmplMd, content);
48
+ converted++;
49
+ console.log(`✅ ${name}`);
50
+ }
51
+
52
+ console.log(
53
+ `\nConverted: ${converted}, Skipped (already has .tmpl): ${skipped}`,
54
+ );
@@ -2,17 +2,17 @@
2
2
 
3
3
  import { existsSync, readFileSync, unlinkSync } from "node:fs";
4
4
  import { join } from "node:path";
5
- import { nudge, deny } from "./lib/hook-utils.mjs";
6
5
  import {
7
- readStdin,
8
- parseJson,
6
+ expectedReviewer,
9
7
  nowSec,
8
+ parseJson,
9
+ readStdin,
10
10
  resolveBaseDir,
11
- shouldTrackPath,
12
- expectedReviewer,
13
11
  SESSION_TTL_SEC,
14
12
  STATE_REL_PATH,
13
+ shouldTrackPath,
15
14
  } from "./lib/cross-review-utils.mjs";
15
+ import { deny, nudge } from "./lib/hook-utils.mjs";
16
16
 
17
17
  function loadState(statePath) {
18
18
  if (!existsSync(statePath)) return null;
@@ -84,17 +84,37 @@ async function main() {
84
84
 
85
85
  // tracker가 설정한 self_approved 플래그 명시적 체크
86
86
  if (meta.self_approved === true) {
87
- selfApproved.push({ path, author, reviewer: meta.reviewer || author, expectedReviewer: requiredReviewer });
87
+ selfApproved.push({
88
+ path,
89
+ author,
90
+ reviewer: meta.reviewer || author,
91
+ expectedReviewer: requiredReviewer,
92
+ });
88
93
  continue;
89
94
  }
90
95
 
91
96
  if (reviewed && reviewer && reviewer === author) {
92
- selfApproved.push({ path, author, reviewer, expectedReviewer: requiredReviewer });
97
+ selfApproved.push({
98
+ path,
99
+ author,
100
+ reviewer,
101
+ expectedReviewer: requiredReviewer,
102
+ });
93
103
  continue;
94
104
  }
95
105
 
96
- if (reviewed && requiredReviewer && reviewer && reviewer !== requiredReviewer) {
97
- selfApproved.push({ path, author, reviewer, expectedReviewer: requiredReviewer });
106
+ if (
107
+ reviewed &&
108
+ requiredReviewer &&
109
+ reviewer &&
110
+ reviewer !== requiredReviewer
111
+ ) {
112
+ selfApproved.push({
113
+ path,
114
+ author,
115
+ reviewer,
116
+ expectedReviewer: requiredReviewer,
117
+ });
98
118
  continue;
99
119
  }
100
120
 
@@ -105,18 +125,21 @@ async function main() {
105
125
 
106
126
  if (selfApproved.length > 0) {
107
127
  const lines = selfApproved
108
- .map((item) => ` * ${item.path} (author=${item.author}, reviewer=${item.reviewer}, required=${item.expectedReviewer || "n/a"})`)
128
+ .map(
129
+ (item) =>
130
+ ` * ${item.path} (author=${item.author}, reviewer=${item.reviewer}, required=${item.expectedReviewer || "n/a"})`,
131
+ )
109
132
  .join("\n");
110
133
  deny(
111
134
  `[cross-review] self-approve 차단: 동일/비허용 reviewer가 감지되었습니다.\n${lines}\n` +
112
- "규칙: author=claude -> reviewer=codex, author=codex -> reviewer=claude",
135
+ "규칙: author=claude -> reviewer=codex, author=codex -> reviewer=claude",
113
136
  );
114
137
  }
115
138
 
116
139
  if (pending.length > 0) {
117
140
  nudge(
118
141
  `[cross-review] git commit 전에 교차 검증이 필요합니다.\n${summarizePending(pending)}\n` +
119
- "규칙: author=claude -> reviewer=codex, author=codex -> reviewer=claude",
142
+ "규칙: author=claude -> reviewer=codex, author=codex -> reviewer=claude",
120
143
  );
121
144
  }
122
145
 
@@ -1,16 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import {
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from "node:fs";
4
10
  import { dirname, isAbsolute, join, relative } from "node:path";
5
11
  import {
6
- readStdin,
7
- parseJson,
12
+ expectedReviewer,
8
13
  nowSec,
14
+ parseJson,
15
+ readStdin,
9
16
  resolveBaseDir,
10
- shouldTrackPath,
11
- expectedReviewer,
12
17
  SESSION_TTL_SEC,
13
18
  STATE_REL_PATH,
19
+ shouldTrackPath,
14
20
  } from "./lib/cross-review-utils.mjs";
15
21
 
16
22
  function resolveStatePath(baseDir) {
@@ -39,7 +45,8 @@ function loadState(statePath) {
39
45
  }
40
46
 
41
47
  return {
42
- files: parsed?.files && typeof parsed.files === "object" ? parsed.files : {},
48
+ files:
49
+ parsed?.files && typeof parsed.files === "object" ? parsed.files : {},
43
50
  session_start: sessionStart,
44
51
  };
45
52
  } catch {
@@ -69,7 +76,8 @@ function normalizePath(filePath, baseDir) {
69
76
 
70
77
  function extractFilePath(toolInput) {
71
78
  if (!toolInput || typeof toolInput !== "object") return "";
72
- const candidate = toolInput.file_path ?? toolInput.path ?? toolInput.filePath ?? "";
79
+ const candidate =
80
+ toolInput.file_path ?? toolInput.path ?? toolInput.filePath ?? "";
73
81
  return typeof candidate === "string" ? candidate : "";
74
82
  }
75
83
 
@@ -81,7 +89,12 @@ function extractCandidatePaths(payload, baseDir) {
81
89
  const trimmed = value.trim();
82
90
  if (!trimmed || /\s/.test(trimmed)) return false;
83
91
  if (trimmed.length > 260) return false;
84
- if (!trimmed.includes(".") && !trimmed.includes("/") && !trimmed.includes("\\")) return false;
92
+ if (
93
+ !trimmed.includes(".") &&
94
+ !trimmed.includes("/") &&
95
+ !trimmed.includes("\\")
96
+ )
97
+ return false;
85
98
  return /^[./\\A-Za-z0-9_-]/.test(trimmed);
86
99
  };
87
100