triflux 10.0.6 → 10.1.1

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 (180) hide show
  1. package/.claude-plugin/marketplace.json +34 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/LICENSE +21 -21
  4. package/bin/triflux.mjs +30 -20
  5. package/config/mcp-registry.json +29 -0
  6. package/hooks/error-context.mjs +35 -7
  7. package/hooks/hook-orchestrator.mjs +14 -0
  8. package/hooks/hook-registry.json +10 -0
  9. package/hooks/safety-guard.mjs +70 -1
  10. package/hub/adaptive-inject.mjs +1 -1
  11. package/hub/assign-callbacks.mjs +120 -120
  12. package/hub/bridge.mjs +1 -1
  13. package/hub/codex-adapter.mjs +1 -1
  14. package/hub/codex-compat.mjs +1 -0
  15. package/hub/delegator/index.mjs +14 -14
  16. package/hub/delegator/tool-definitions.mjs +35 -35
  17. package/hub/fullcycle.mjs +1 -0
  18. package/hub/hitl.mjs +143 -143
  19. package/hub/intent.mjs +4 -1
  20. package/hub/lib/memory-store.mjs +22 -0
  21. package/hub/promote-penalties.mjs +120 -0
  22. package/hub/public/dashboard.html +7 -7
  23. package/hub/quality/deslop.mjs +1 -1
  24. package/hub/reflexion.mjs +40 -56
  25. package/hub/research.mjs +1 -0
  26. package/hub/router.mjs +791 -791
  27. package/hub/server.mjs +23 -0
  28. package/hub/session-fingerprint.mjs +3 -3
  29. package/hub/store-adapter.mjs +51 -20
  30. package/hub/store.mjs +8 -8
  31. package/hub/team/cli/commands/attach.mjs +37 -37
  32. package/hub/team/cli/commands/debug.mjs +74 -74
  33. package/hub/team/cli/commands/focus.mjs +53 -53
  34. package/hub/team/cli/commands/list.mjs +24 -24
  35. package/hub/team/cli/commands/start/start-headless.mjs +5 -3
  36. package/hub/team/cli/commands/start/start-in-process.mjs +40 -40
  37. package/hub/team/cli/commands/start/start-mux.mjs +73 -73
  38. package/hub/team/cli/commands/start/start-wt.mjs +69 -69
  39. package/hub/team/cli/commands/tasks.mjs +13 -13
  40. package/hub/team/cli/render.mjs +30 -30
  41. package/hub/team/cli/services/attach-fallback.mjs +54 -54
  42. package/hub/team/cli/services/hub-client.mjs +1 -1
  43. package/hub/team/cli/services/member-selector.mjs +30 -30
  44. package/hub/team/cli/services/native-control.mjs +116 -116
  45. package/hub/team/cli/services/task-model.mjs +30 -30
  46. package/hub/team/conductor.mjs +671 -671
  47. package/hub/team/dashboard-open.mjs +1 -1
  48. package/hub/team/headless.mjs +1156 -1156
  49. package/hub/team/notify.mjs +1 -1
  50. package/hub/team/orchestrator.mjs +161 -161
  51. package/hub/team/psmux.mjs +67 -5
  52. package/hub/team/remote-probe.mjs +1 -1
  53. package/hub/team/remote-watcher.mjs +1 -1
  54. package/hub/team/session.mjs +611 -611
  55. package/hub/team/shared.mjs +13 -13
  56. package/hub/team/swarm-hypervisor.mjs +3 -4
  57. package/hub/team/tui-remote-adapter.mjs +3 -3
  58. package/hub/team/tui-viewer.mjs +0 -1
  59. package/hub/team/tui.mjs +12 -15
  60. package/hub/team/worktree-lifecycle.mjs +1 -1
  61. package/hub/token-mode.mjs +4 -1
  62. package/hub/tray.mjs +368 -368
  63. package/hub/workers/codex-mcp.mjs +507 -507
  64. package/hub/workers/delegator-mcp.mjs +1 -2
  65. package/hub/workers/factory.mjs +21 -21
  66. package/hud/providers/codex.mjs +24 -2
  67. package/package.json +55 -22
  68. package/scripts/__tests__/skill-template.test.mjs +0 -1
  69. package/scripts/claudemd-sync.mjs +1 -1
  70. package/scripts/cli-route.sh +3 -3
  71. package/scripts/completions/tfx.bash +47 -47
  72. package/scripts/completions/tfx.fish +44 -44
  73. package/scripts/completions/tfx.zsh +83 -83
  74. package/scripts/cross-review-gate.mjs +1 -1
  75. package/scripts/headless-guard.mjs +44 -16
  76. package/scripts/hub-ensure.mjs +120 -120
  77. package/scripts/keyword-detector.mjs +272 -272
  78. package/scripts/keyword-rules-expander.mjs +521 -521
  79. package/scripts/lib/mcp-filter.mjs +2 -2
  80. package/scripts/lib/mcp-guard-engine.mjs +1 -1
  81. package/scripts/lib/mcp-server-catalog.mjs +118 -118
  82. package/scripts/lib/psmux-info.mjs +2 -2
  83. package/scripts/lib/skill-template.mjs +4 -4
  84. package/scripts/mcp-check.mjs +1 -1
  85. package/scripts/mcp-gateway-config.mjs +1 -1
  86. package/scripts/mcp-gateway-ensure.mjs +1 -1
  87. package/scripts/mcp-gateway-start.mjs +1 -1
  88. package/scripts/notion-read.mjs +553 -553
  89. package/scripts/remote-spawn.mjs +1 -1
  90. package/scripts/session-spawn-helper.mjs +1 -3
  91. package/scripts/setup.mjs +1 -1
  92. package/scripts/test-lock.mjs +1 -1
  93. package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
  94. package/scripts/tfx-batch-stats.mjs +96 -96
  95. package/scripts/tfx-route-post.mjs +6 -6
  96. package/scripts/tfx-route.sh +6 -0
  97. package/tui/codex-profile.mjs +402 -0
  98. package/tui/core.mjs +236 -0
  99. package/tui/doctor.mjs +328 -0
  100. package/tui/gemini-profile.mjs +254 -0
  101. package/tui/monitor-data.mjs +148 -0
  102. package/tui/monitor.mjs +295 -0
  103. package/tui/setup.mjs +442 -0
  104. package/CLAUDE.md +0 -171
  105. package/mesh/index.mjs +0 -63
  106. package/mesh/mesh-budget.mjs +0 -128
  107. package/mesh/mesh-heartbeat.mjs +0 -100
  108. package/mesh/mesh-protocol.mjs +0 -96
  109. package/mesh/mesh-queue.mjs +0 -165
  110. package/mesh/mesh-registry.mjs +0 -78
  111. package/mesh/mesh-router.mjs +0 -76
  112. package/references/hosts.json +0 -33
  113. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +0 -1
  114. package/skills/.omc/state/idle-notif-cooldown.json +0 -3
  115. package/skills/.omc/state/last-tool-error.json +0 -7
  116. package/skills/.omc/state/subagent-tracking.json +0 -7
  117. package/skills/tfx-remote-spawn/references/hosts.json +0 -16
  118. package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
  119. package/skills/tfx-workspace/evals/evals.json +0 -79
  120. package/skills/tfx-workspace/iteration-1/benchmark.json +0 -162
  121. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
  122. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -9
  123. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
  124. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
  125. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -9
  126. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
  127. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
  128. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
  129. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -9
  130. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
  131. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
  132. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -9
  133. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
  134. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
  135. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
  136. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -8
  137. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
  138. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
  139. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -8
  140. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
  141. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
  142. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
  143. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -10
  144. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
  145. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
  146. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -10
  147. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
  148. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
  149. package/skills/tfx-workspace/iteration-1/review.html +0 -1325
  150. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
  151. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -10
  152. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
  153. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
  154. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -10
  155. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
  156. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
  157. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
  158. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -10
  159. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
  160. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
  161. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -10
  162. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
  163. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
  164. package/skills/tfx-workspace/iteration-2/benchmark.json +0 -62
  165. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
  166. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -11
  167. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
  168. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
  169. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -11
  170. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
  171. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
  172. package/skills/tfx-workspace/iteration-2/review.html +0 -1325
  173. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
  174. package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +0 -77
  175. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
  176. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
  177. package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +0 -82
  178. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
  179. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
  180. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
3
+ "name": "triflux",
4
+ "description": "CLI-first multi-model orchestrator — Codex/Gemini/Claude routing with DAG execution, auto-triage, and cost optimization",
5
+ "owner": {
6
+ "name": "tellang"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "triflux",
11
+ "description": "CLI-first multi-model orchestrator for Claude Code. Routes tasks to Codex, Gemini, and Claude CLIs with automatic triage, DAG-based parallel execution, headless psmux sessions, and cost-optimized routing. Includes 41 skills, HUD status bar, hook orchestrator, and shell-based CLI routing.",
12
+ "version": "9.7.14",
13
+ "author": {
14
+ "name": "tellang"
15
+ },
16
+ "source": {
17
+ "source": "npm",
18
+ "package": "triflux"
19
+ },
20
+ "category": "productivity",
21
+ "homepage": "https://github.com/tellang/triflux",
22
+ "tags": [
23
+ "multi-model",
24
+ "codex",
25
+ "gemini",
26
+ "cli-routing",
27
+ "orchestration",
28
+ "cost-optimization",
29
+ "dag-execution"
30
+ ]
31
+ }
32
+ ],
33
+ "version": "9.7.14"
34
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "triflux",
3
+ "version": "10.0.0",
4
+ "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
5
+ "author": {
6
+ "name": "tellang"
7
+ },
8
+ "repository": "https://github.com/tellang/triflux",
9
+ "homepage": "https://github.com/tellang/triflux",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "claude-code",
13
+ "plugin",
14
+ "codex",
15
+ "gemini",
16
+ "cli-routing",
17
+ "orchestration",
18
+ "multi-model"
19
+ ],
20
+ "skills": "./skills/",
21
+ "hooks": "./hooks/hooks.json"
22
+ }
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 tellang
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 tellang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/bin/triflux.mjs CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  import {
28
28
  SYNC_MAP, SKILL_ALIASES, REQUIRED_CODEX_PROFILES, LEGACY_CODEX_MODELS,
29
29
  syncAliasedSkillDir, hasProfileSection, replaceProfileSection,
30
- ensureCodexProfiles, getVersion, cleanupStaleSkills, DEPRECATED_SKILLS,
30
+ ensureCodexProfiles, getVersion, cleanupStaleSkills,
31
31
  extractManagedHookFilename, getManagedRegistryHooks, ensureHooksInSettings,
32
32
  ensureCodexHubServerConfig,
33
33
  } from "../scripts/setup.mjs";
@@ -65,14 +65,14 @@ const GREEN_BRIGHT = "\x1b[38;5;82m";
65
65
  const RED_BRIGHT = "\x1b[38;5;196m";
66
66
 
67
67
  // ── 브랜드 요소 ──
68
- const BRAND = `${AMBER}${BOLD}triflux${RESET}`;
68
+ const _BRAND = `${AMBER}${BOLD}triflux${RESET}`;
69
69
  const VER = `${DIM}v${PKG.version}${RESET}`;
70
70
  const LINE = `${GRAY}${"─".repeat(48)}${RESET}`;
71
- const DOT = `${GRAY}·${RESET}`;
71
+ const _DOT = `${GRAY}·${RESET}`;
72
72
  const STALE_TEAM_MAX_AGE_SEC = 3600;
73
73
  const ANSI_PATTERN = /\x1B\[[0-?]*[ -/]*[@-~]/g;
74
74
 
75
- const EXIT_SUCCESS = 0;
75
+ const _EXIT_SUCCESS = 0;
76
76
  const EXIT_ERROR = 1;
77
77
  const EXIT_ARG_ERROR = 2;
78
78
  const EXIT_CLI_MISSING = 3;
@@ -719,12 +719,11 @@ function previewMcpRegistrationActions(mcpUrl) {
719
719
  function previewClaudeRoutingAction() {
720
720
  const globalClaudePath = join(CLAUDE_DIR, "CLAUDE.md");
721
721
  const projectClaudePath = join(PKG_ROOT, "CLAUDE.md");
722
- const projectContent = existsSync(projectClaudePath)
723
- ? readFileSync(projectClaudePath, "utf8")
724
- : "";
725
- const projectSection = extractMarkdownSection(projectContent, TFX_SECTION_HEADING);
726
722
 
727
- if (!projectSection) {
723
+ let _routingTable;
724
+ try {
725
+ _routingTable = getLatestRoutingTable();
726
+ } catch {
728
727
  return {
729
728
  type: "claude-guidance",
730
729
  path: globalClaudePath,
@@ -734,18 +733,23 @@ function previewClaudeRoutingAction() {
734
733
  };
735
734
  }
736
735
 
737
- const globalContent = existsSync(globalClaudePath)
738
- ? readFileSync(globalClaudePath, "utf8")
739
- : "";
740
- const preview = ensureTfxSection(globalContent, { scope: "global", projectSection });
736
+ if (!existsSync(globalClaudePath)) {
737
+ return {
738
+ type: "claude-guidance",
739
+ path: globalClaudePath,
740
+ source: projectClaudePath,
741
+ change: "create",
742
+ };
743
+ }
744
+
745
+ const globalContent = readFileSync(globalClaudePath, "utf8");
746
+ const hasRouting = globalContent.includes("<routing>") || globalContent.includes("## triflux CLI 라우팅");
741
747
 
742
748
  return {
743
749
  type: "claude-guidance",
744
750
  path: globalClaudePath,
745
751
  source: projectClaudePath,
746
- change: preview.changed ? (existsSync(globalClaudePath) ? "update" : "create") : "noop",
747
- heading: TFX_SECTION_HEADING,
748
- summary: TFX_GLOBAL_SUMMARY_SECTION,
752
+ change: hasRouting ? "noop" : "create",
749
753
  };
750
754
  }
751
755
 
@@ -2073,7 +2077,7 @@ async function cmdDoctor(options = {}) {
2073
2077
  // 14. Stale Teams (Claude teams/ + tasks/ 자동 감지)
2074
2078
  section("Stale Teams");
2075
2079
  const teamsDir = join(CLAUDE_DIR, "teams");
2076
- const tasksDir = join(CLAUDE_DIR, "tasks");
2080
+ const _tasksDir = join(CLAUDE_DIR, "tasks");
2077
2081
  if (existsSync(teamsDir)) {
2078
2082
  try {
2079
2083
  const teamDirs = readdirSync(teamsDir).filter(d => {
@@ -2130,8 +2134,8 @@ async function cmdDoctor(options = {}) {
2130
2134
  // 프로세스 명령줄에서 세션 ID 매칭 (tmux 없는 in-process 팀 지원)
2131
2135
  if (!hasActiveMember && teamConfig.leadSessionId) {
2132
2136
  try {
2133
- const sessionToken = teamConfig.leadSessionId.toLowerCase();
2134
- const safeToken = teamConfig.leadSessionId.slice(0, 8).replace(/[^a-zA-Z0-9\-]/g, '');
2137
+ const _sessionToken = teamConfig.leadSessionId.toLowerCase();
2138
+ const safeToken = teamConfig.leadSessionId.slice(0, 8).replace(/[^a-zA-Z0-9-]/g, '');
2135
2139
  // Claude Code 프로세스에서 세션 ID 검색
2136
2140
  if (process.platform === "win32") {
2137
2141
  const psOut = execSync(
@@ -3723,7 +3727,7 @@ async function cmdHub(args = [], options = {}) {
3723
3727
  process.kill(info.pid, "SIGTERM");
3724
3728
  try { unlinkSync(HUB_PID_FILE); } catch {}
3725
3729
  console.log(`\n ${GREEN_BRIGHT}✓${RESET} hub 종료됨 (PID ${info.pid})\n`);
3726
- } catch (e) {
3730
+ } catch (_e) {
3727
3731
  try { unlinkSync(HUB_PID_FILE); } catch {}
3728
3732
  console.log(`\n ${DIM}hub 프로세스 없음 — PID 파일 정리됨${RESET}\n`);
3729
3733
  }
@@ -3884,6 +3888,12 @@ async function main() {
3884
3888
  case "hub":
3885
3889
  await cmdHub(cmdArgs, { json: JSON_OUTPUT && (cmdArgs[0] || "status") === "status" });
3886
3890
  return;
3891
+ case "monitor": {
3892
+ const { createMonitor } = await import("../tui/monitor.mjs");
3893
+ const mon = createMonitor();
3894
+ await mon.start();
3895
+ break;
3896
+ }
3887
3897
  case "tray": {
3888
3898
  const trayUrl = new URL("../hub/tray.mjs", import.meta.url);
3889
3899
  const trayPath = fileURLToPath(trayUrl);
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "mcp-registry-schema",
3
+ "version": 1,
4
+ "description": "MCP 서버 중앙 레지스트리 — 진실의 원천",
5
+ "defaults": {
6
+ "transport": "hub-url",
7
+ "hub_base": "http://127.0.0.1:27888"
8
+ },
9
+ "servers": {
10
+ "tfx-hub": {
11
+ "transport": "hub-url",
12
+ "url": "http://127.0.0.1:27888/mcp",
13
+ "safe": true,
14
+ "targets": ["claude", "gemini", "codex"],
15
+ "description": "triflux Hub MCP 서버"
16
+ }
17
+ },
18
+ "policies": {
19
+ "stdio_action": "replace-with-hub",
20
+ "unknown_server_action": "warn",
21
+ "watched_paths": [
22
+ "~/.gemini/settings.json",
23
+ "~/.codex/config.toml",
24
+ "~/.claude/settings.json",
25
+ "~/.claude/settings.local.json",
26
+ ".mcp.json"
27
+ ]
28
+ }
29
+ }
@@ -4,7 +4,8 @@
4
4
  // 도구 실패 시 에러 패턴을 분석하여 해결 힌트를 additionalContext로 주입한다.
5
5
  // Claude가 동일 에러를 반복하지 않도록 구체적 가이드를 제공.
6
6
 
7
- import { readFileSync } from "node:fs";
7
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
8
+ import { join } from "node:path";
8
9
 
9
10
  // ── 에러 패턴 → 해결 힌트 매핑 ─────────────────────────────
10
11
  const ERROR_HINTS = [
@@ -128,16 +129,43 @@ function main() {
128
129
  JSON.stringify(input.tool_result || ""),
129
130
  ].join("\n");
130
131
 
132
+ // ── reflexion 적응형 학습: safety-guard/headless-guard 차단을 패널티로 기록 ──
133
+ const isSafetyBlock = /\[(?:safety-guard|headless-guard)\].*(?:BLOCKED|차단)/i.test(errorText);
134
+ if (isSafetyBlock) {
135
+ try {
136
+ const home = process.env.HOME || process.env.USERPROFILE || "";
137
+ const penaltyDir = join(home, ".triflux", "reflexion");
138
+ mkdirSync(penaltyDir, { recursive: true });
139
+ const penaltyFile = join(penaltyDir, "pending-penalties.jsonl");
140
+ const command = input.tool_input?.command || "";
141
+ const entry = {
142
+ ts: new Date().toISOString(),
143
+ type: "guard_block",
144
+ tool: input.tool_name || "Bash",
145
+ error_pattern: errorText.match(/\[.*?\]\s*(.{0,120})/)?.[1] || errorText.slice(0, 120),
146
+ command_preview: command.slice(0, 200),
147
+ source: errorText.includes("safety-guard") ? "safety-guard" : "headless-guard",
148
+ };
149
+ writeFileSync(penaltyFile, JSON.stringify(entry) + "\n", { flag: "a" });
150
+ } catch { /* reflexion 기록 실패는 무시 — 힌트 출력에 영향 주지 않음 */ }
151
+ }
152
+
131
153
  const hints = findHints(errorText);
132
- if (hints.length === 0) process.exit(0);
154
+ // safety-guard 차단에는 guard가 이미 구체적 안내를 제공하므로 추가 힌트 불필요
155
+ if (hints.length === 0 && !isSafetyBlock) process.exit(0);
133
156
 
134
157
  const toolName = input.tool_name || "Unknown";
135
- const output = {
136
- systemMessage:
137
- `[error-context] ${toolName} 실패 — 해결 힌트:\n` +
138
- hints.map((h) => ` → ${h}`).join("\n"),
139
- };
158
+ const parts = [];
159
+ if (hints.length > 0) {
160
+ parts.push(`[error-context] ${toolName} 실패 — 해결 힌트:\n` + hints.map((h) => ` → ${h}`).join("\n"));
161
+ }
162
+ if (isSafetyBlock) {
163
+ parts.push("[reflexion] 이 패턴이 적응형 학습에 기록되었습니다. 다음 세션에서 동일 패턴 시 사전 차단됩니다.");
164
+ }
165
+
166
+ if (parts.length === 0) process.exit(0);
140
167
 
168
+ const output = { systemMessage: parts.join("\n") };
141
169
  process.stdout.write(JSON.stringify(output));
142
170
  }
143
171
 
@@ -340,6 +340,20 @@ async function main() {
340
340
  }
341
341
  }
342
342
 
343
+ // ── PostToolUse:Skill 완료 시 라우팅 가중치 기록 ──
344
+ if (eventName === "PostToolUse" && toolName === "Skill" && !blocked) {
345
+ try {
346
+ const input = JSON.parse(stdinRaw);
347
+ const skillName = input.tool_input?.skill || "";
348
+ if (skillName && skillName.startsWith("tfx-")) {
349
+ const mode = skillName.replace(/^tfx-/, "");
350
+ const gitRoot = process.env.GIT_WORK_TREE || process.cwd();
351
+ const slug = gitRoot.split(/[\\/]/).pop() || "unknown";
352
+ recordRouteOutcome(slug, mode, "completion");
353
+ }
354
+ } catch { /* 가중치 기록 실패 무시 */ }
355
+ }
356
+
343
357
  // 결과 출력
344
358
  if (blocked) {
345
359
  process.exit(2);
@@ -63,6 +63,16 @@
63
63
  "timeout": 2,
64
64
  "blocking": false,
65
65
  "description": "tfx-multi 상태 추적 게이트"
66
+ },
67
+ {
68
+ "id": "tfx-write-edit-passthrough",
69
+ "source": "triflux",
70
+ "matcher": "Edit|Write",
71
+ "command": "exit 0",
72
+ "priority": 999,
73
+ "enabled": true,
74
+ "blocking": false,
75
+ "description": "Edit/Write passthrough"
66
76
  }
67
77
  ],
68
78
  "PostToolUse": [
@@ -8,7 +8,8 @@
8
8
  // BLOCK (exit 2) — 복구 불가능한 파괴적 명령
9
9
  // WARN (allow + context) — 주의가 필요한 명령
10
10
 
11
- import { readFileSync } from "node:fs";
11
+ import { readFileSync, existsSync } from "node:fs";
12
+ import { join } from "node:path";
12
13
 
13
14
  // ── 차단 규칙 ──────────────────────────────────────────────
14
15
  const BLOCK_RULES = [
@@ -38,6 +39,21 @@ const WT_DIRECT_PATTERNS = [
38
39
  const WT_DIRECT_BLOCK_MESSAGE =
39
40
  "[safety-guard] wt.exe 직접 호출 차단됨. → hub/team/wt-manager.mjs의 createTab() / splitPane() / applySplitLayout()을 사용하세요.";
40
41
 
42
+ // ── SSH+PowerShell bash 문법 차단 ────────────────────────────
43
+ // 원격 기본 셸이 PowerShell인 호스트에 bash redirect/glob을 보내면 오동작
44
+ const BASH_SYNTAX_IN_SSH = [
45
+ /2>\/dev\/null/, // 2>/dev/null → PowerShell에서 Out-File C:\dev\null
46
+ />\s*\/dev\/null/, // >/dev/null
47
+ /&>\s*\/dev\/null/, // &>/dev/null
48
+ /\$\(/, // $(cmd) → PowerShell에서 다른 의미
49
+ /\bsource\s+/, // source → PowerShell에 없음
50
+ /\bexport\s+\w+=/, // export VAR= → PowerShell에 없음
51
+ ];
52
+
53
+ const SSH_POWERSHELL_HINT =
54
+ "원격 셸이 PowerShell입니다. bash 문법 직접 전달 금지. scp + pwsh -File 패턴 사용. " +
55
+ "2>/dev/null → 2>$null, $() → $(), export → $env:, source → . (dot-source)";
56
+
41
57
  // ── 경고 규칙 ──────────────────────────────────────────────
42
58
  const WARN_RULES = [
43
59
  { pattern: /\bgit\s+push\b(?!.*--force)/i, warn: "git push 감지. 원격 저장소에 반영됩니다." },
@@ -50,6 +66,20 @@ const WARN_RULES = [
50
66
  { pattern: /\bcurl\s.*\|\s*(bash|sh)\b/i, warn: "curl | sh 감지. 원격 스크립트 실행 주의." },
51
67
  ];
52
68
 
69
+ // ── reflexion 적응형 패널티 로드 ──────────────────────────────
70
+ function loadReflexionPenalties() {
71
+ try {
72
+ const home = process.env.HOME || process.env.USERPROFILE || "";
73
+ const penaltyFile = join(home, ".triflux", "reflexion", "pending-penalties.jsonl");
74
+ if (!existsSync(penaltyFile)) return [];
75
+ return readFileSync(penaltyFile, "utf8")
76
+ .split("\n")
77
+ .filter(Boolean)
78
+ .map(line => { try { return JSON.parse(line); } catch { return null; } })
79
+ .filter(Boolean);
80
+ } catch { return []; }
81
+ }
82
+
53
83
  function readStdin() {
54
84
  try {
55
85
  return readFileSync(0, "utf8");
@@ -127,6 +157,45 @@ function main() {
127
157
  blockCommand(WT_DIRECT_BLOCK_MESSAGE, command);
128
158
  }
129
159
 
160
+ // 0.1. reflexion 적응형 패널티 — 이전 세션에서 차단된 패턴 사전 경고
161
+ const penalties = loadReflexionPenalties();
162
+ if (penalties.length > 0) {
163
+ for (const penalty of penalties) {
164
+ if (penalty.error_pattern && new RegExp(penalty.error_pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").slice(0, 80), "i").test(command)) {
165
+ const output = {
166
+ hookSpecificOutput: {
167
+ hookEventName: "PreToolUse",
168
+ permissionDecision: "allow",
169
+ additionalContext:
170
+ `[reflexion] 이전 세션에서 차단된 패턴과 유사합니다 (${penalty.source}, ${penalty.ts?.slice(0, 10)}). ` +
171
+ `이전 차단 사유: ${penalty.error_pattern?.slice(0, 100)}`,
172
+ },
173
+ };
174
+ process.stdout.write(JSON.stringify(output));
175
+ process.exit(0);
176
+ }
177
+ }
178
+ }
179
+
180
+ // 0.5. SSH → PowerShell 호스트에 bash 문법 전달 차단
181
+ // 세그먼트 시작 위치에서 ssh 명령인 경우만 (문자열/코드 안의 ssh는 무시)
182
+ if (hasSegmentInvocation(command, [/^\s*ssh\s+/i])) {
183
+ // 세그먼트 분리 후 ssh로 시작하는 세그먼트의 payload만 검사
184
+ const segments = command.split(/\s*(?:&&|;|\|\||\|)\s*/);
185
+ for (const seg of segments) {
186
+ const sshMatch = seg.trim().match(/^ssh\s+\S+\s+(.*)/s);
187
+ if (!sshMatch) continue;
188
+ const sshPayload = sshMatch[1];
189
+ const bashSyntax = BASH_SYNTAX_IN_SSH.find(p => p.test(sshPayload));
190
+ if (bashSyntax) {
191
+ blockCommand(
192
+ `[safety-guard] SSH 명령에 bash 전용 문법 감지: ${bashSyntax}. ${SSH_POWERSHELL_HINT}`,
193
+ command,
194
+ );
195
+ }
196
+ }
197
+ }
198
+
130
199
  // 1. BLOCK 체크 — exit 2로 차단
131
200
  for (const rule of BLOCK_RULES) {
132
201
  if (rule.skipIfGit && !isPsmuxInvocation(command)) continue;
@@ -183,4 +183,4 @@ export function createAdaptiveInjector(opts = {}) {
183
183
  });
184
184
  }
185
185
 
186
- export default createAdaptiveInjector;
186
+ export default createAdaptiveInjector;