triflux 10.9.1 → 10.9.3

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 (91) hide show
  1. package/.claude-plugin/marketplace.json +34 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/README.ko.md +14 -24
  4. package/README.md +7 -12
  5. package/bin/triflux.mjs +2 -3
  6. package/config/mcp-registry.json +29 -0
  7. package/hooks/hook-registry.json +2 -2
  8. package/hooks/session-start-fast.mjs +21 -10
  9. package/hub/pipe.mjs +2 -0
  10. package/hub/server.mjs +19 -0
  11. package/hub/state.mjs +2 -1
  12. package/hud/hud-qos-status.mjs +1 -3
  13. package/hud/renderers.mjs +5 -9
  14. package/package.json +56 -21
  15. package/scripts/hub-ensure.mjs +13 -25
  16. package/scripts/tfx-route.sh +1 -1
  17. package/skills/tfx-auto/SKILL.md +26 -22
  18. package/skills/tfx-hub/SKILL.md +273 -85
  19. package/skills/tfx-hub/skill.json +2 -2
  20. package/tui/codex-profile.mjs +457 -0
  21. package/tui/core.mjs +266 -0
  22. package/tui/doctor.mjs +375 -0
  23. package/tui/gemini-profile.mjs +299 -0
  24. package/tui/monitor-data.mjs +152 -0
  25. package/tui/monitor.mjs +339 -0
  26. package/tui/setup.mjs +598 -0
  27. package/CLAUDE.md +0 -212
  28. package/references/hosts.json +0 -46
  29. package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
  30. package/skills/tfx-workspace/evals/evals.json +0 -79
  31. package/skills/tfx-workspace/iteration-1/benchmark.json +0 -524
  32. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
  33. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -25
  34. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
  35. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
  36. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -25
  37. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
  38. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
  39. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
  40. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -25
  41. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
  42. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
  43. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -25
  44. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
  45. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
  46. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
  47. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -20
  48. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
  49. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
  50. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -20
  51. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
  52. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
  53. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
  54. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -30
  55. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
  56. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
  57. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -30
  58. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
  59. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
  60. package/skills/tfx-workspace/iteration-1/review.html +0 -1325
  61. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
  62. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -30
  63. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
  64. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
  65. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -30
  66. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
  67. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
  68. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
  69. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -30
  70. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
  71. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
  72. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -30
  73. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
  74. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
  75. package/skills/tfx-workspace/iteration-2/benchmark.json +0 -144
  76. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
  77. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -35
  78. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
  79. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
  80. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -35
  81. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
  82. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
  83. package/skills/tfx-workspace/iteration-2/review.html +0 -1325
  84. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
  85. package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +0 -77
  86. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
  87. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
  88. package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +0 -82
  89. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
  90. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
  91. 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": "10.9.3",
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": "10.3.2"
34
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "triflux",
3
+ "version": "10.3.4",
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/README.ko.md CHANGED
@@ -37,27 +37,22 @@
37
37
 
38
38
  ## 빠른 시작
39
39
 
40
- ### 1. 설치
40
+ **Claude Code** (권장):
41
41
 
42
- **Claude Code 플러그인** (권장):
43
-
44
- ```bash
45
- claude plugin add triflux
42
+ ```
43
+ /plugin marketplace add tellang/triflux
44
+ /plugin install triflux@tellang
46
45
  ```
47
46
 
48
- **npm 글로벌**:
47
+ **npm**:
49
48
 
50
49
  ```bash
51
50
  npm install -g triflux
52
51
  ```
53
52
 
54
- ### 2. 설정
55
-
56
- ```bash
57
- tfx setup
58
- ```
53
+ `tfx setup`으로 환경을 설정하세요.
59
54
 
60
- ### 3. 사용법
55
+ ### 사용법
61
56
 
62
57
  ```bash
63
58
  # Light — 단일 모델로 빠르게 실행
@@ -292,27 +287,22 @@ graph TD
292
287
 
293
288
  ## 빠른 시작
294
289
 
295
- ### 1. 설치
290
+ **Claude Code** (권장):
296
291
 
297
- **Claude Code 플러그인** (권장):
298
-
299
- ```bash
300
- claude plugin add triflux
292
+ ```
293
+ /plugin marketplace add tellang/triflux
294
+ /plugin install triflux@tellang
301
295
  ```
302
296
 
303
- **npm 글로벌**:
297
+ **npm**:
304
298
 
305
299
  ```bash
306
300
  npm install -g triflux
307
301
  ```
308
302
 
309
- ### 2. 설정
310
-
311
- ```bash
312
- tfx setup
313
- ```
303
+ `tfx setup`으로 환경을 설정하세요.
314
304
 
315
- ### 3. 사용법
305
+ ### 사용법
316
306
 
317
307
  ```bash
318
308
  # Light — 단일 모델로 빠르게 실행
package/README.md CHANGED
@@ -61,27 +61,22 @@ You don't need to memorize commands. Say what you want in natural language — t
61
61
 
62
62
  ## Quick Start
63
63
 
64
- ### 1. Install
64
+ **Claude Code** (recommended):
65
65
 
66
- **Claude Code Plugin** (recommended):
67
-
68
- ```bash
69
- claude plugin add triflux
66
+ ```
67
+ /plugin marketplace add tellang/triflux
68
+ /plugin install triflux@tellang
70
69
  ```
71
70
 
72
- **npm global**:
71
+ **npm**:
73
72
 
74
73
  ```bash
75
74
  npm install -g triflux
76
75
  ```
77
76
 
78
- ### 2. Setup
79
-
80
- ```bash
81
- tfx setup
82
- ```
77
+ Then run `tfx setup` to configure your environment.
83
78
 
84
- ### 3. Use
79
+ ### Use
85
80
 
86
81
  ```bash
87
82
  # 3-party consensus — three models argue, only consensus survives
package/bin/triflux.mjs CHANGED
@@ -1047,14 +1047,13 @@ function cmdSetup(options = {}) {
1047
1047
  }
1048
1048
  {
1049
1049
  const claudeGuide = ensureGlobalClaudeRoutingSection(CLAUDE_DIR);
1050
- if (claudeGuide.skipped)
1050
+ if (claudeGuide.skipped && claudeGuide.reason !== "global_sync_disabled")
1051
1051
  warn(`CLAUDE.md 라우팅 섹션 확인 실패: ${claudeGuide.reason}`);
1052
1052
  else if (
1053
1053
  claudeGuide.action === "created" ||
1054
1054
  claudeGuide.action === "updated"
1055
1055
  )
1056
1056
  ok("CLAUDE.md: 전역 triflux 라우팅 요약 갱신");
1057
- else ok("CLAUDE.md: 전역 triflux 라우팅 요약 유지");
1058
1057
  }
1059
1058
 
1060
1059
  // 스킬 동기화 (~/.claude/skills/{name}/SKILL.md)
@@ -1847,7 +1846,7 @@ async function cmdDoctor(options = {}) {
1847
1846
  }
1848
1847
  {
1849
1848
  const claudeGuide = ensureGlobalClaudeRoutingSection(CLAUDE_DIR);
1850
- if (claudeGuide.skipped)
1849
+ if (claudeGuide.skipped && claudeGuide.reason !== "global_sync_disabled")
1851
1850
  warn(`CLAUDE.md 라우팅 섹션 확인 실패: ${claudeGuide.reason}`);
1852
1851
  else if (
1853
1852
  claudeGuide.action === "created" ||
@@ -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
+ }
@@ -167,8 +167,8 @@
167
167
  "priority": 2,
168
168
  "enabled": true,
169
169
  "timeout": 8,
170
- "blocking": false,
171
- "description": "tfx-hub 서비스 헬스체크 및 시작"
170
+ "blocking": true,
171
+ "description": "tfx-hub 서비스 헬스체크 및 시작 (BLOCKING — 세션 시작 전 Hub 준비 보장)"
172
172
  },
173
173
  {
174
174
  "id": "tfx-preflight-cache",
@@ -4,8 +4,8 @@
4
4
  // 6개 훅을 1개 node 프로세스 안에서 실행하여 콜드스타트 7회 → 1회로 줄인다.
5
5
  //
6
6
  // 분류:
7
- // BLOCKING (직렬, stdout 반환 전 완료): setup.runCritical, mcp-safety-guard.run
8
- // DEFERRED (병렬, 실패해도 안 죽음): hub-ensure.run, mcp-gateway-ensure.run, setup.runDeferred
7
+ // BLOCKING (직렬, stdout 반환 전 완료): setup.runCritical, mcp-safety-guard.run, hub-ensure.run
8
+ // DEFERRED (병렬, 실패해도 안 죽음): mcp-gateway-ensure.run, setup.runDeferred
9
9
  // BACKGROUND (fire-and-forget): preflight-cache.run
10
10
  //
11
11
  // external source 훅 (session-vault 등)은 여전히 execFile로 실행된다.
@@ -55,6 +55,24 @@ async function runBlocking(stdinData) {
55
55
  log.error({ hook: "mcp-safety-guard", err: String(err.message || err) }, "hook.failed");
56
56
  }
57
57
 
58
+ // 3. hub-ensure — Hub 필수 인프라, BLOCKING으로 실행
59
+ try {
60
+ const t0 = performance.now();
61
+ const hubMod = await importMod(join(SCRIPTS, "hub-ensure.mjs"));
62
+ const result = await hubMod.run(stdinData);
63
+ const dur = performance.now() - t0;
64
+ timings.push({ hook: "hub-ensure", dur_ms: Math.round(dur) });
65
+ if (result?.stdout) output.stdout += result.stdout + "\n";
66
+ if (result?.stderr) output.stderr += result.stderr + "\n";
67
+ if (result?.code !== 0) {
68
+ log.warn({ hook: "hub-ensure", dur_ms: Math.round(dur), code: result?.code }, "hook.warn");
69
+ } else {
70
+ log.info({ hook: "hub-ensure", dur_ms: Math.round(dur) }, "hook.completed");
71
+ }
72
+ } catch (err) {
73
+ log.error({ hook: "hub-ensure", err: String(err.message || err) }, "hook.failed");
74
+ }
75
+
58
76
  return { ...output, timings };
59
77
  }
60
78
 
@@ -65,13 +83,6 @@ async function runBlocking(stdinData) {
65
83
  */
66
84
  function runDeferred(stdinData) {
67
85
  const tasks = [
68
- {
69
- name: "hub-ensure",
70
- fn: async () => {
71
- const mod = await importMod(join(SCRIPTS, "hub-ensure.mjs"));
72
- return mod.run(stdinData);
73
- },
74
- },
75
86
  {
76
87
  name: "mcp-gateway-ensure",
77
88
  fn: async () => {
@@ -134,7 +145,7 @@ export async function execute(stdinData, externalHooks = []) {
134
145
  runBackground(stdinData);
135
146
 
136
147
  const totalDur = performance.now() - totalStart;
137
- log.info({ total_ms: Math.round(totalDur), blocking_count: 2, deferred_count: 3, bg_count: 1 }, "session-start.done");
148
+ log.info({ total_ms: Math.round(totalDur), blocking_count: 3, deferred_count: 2, bg_count: 1 }, "session-start.done");
138
149
 
139
150
  return {
140
151
  stdout: blocking.stdout,
package/hub/pipe.mjs CHANGED
@@ -125,6 +125,7 @@ export function createPipeServer({
125
125
  heartbeatTtlMs = DEFAULT_HEARTBEAT_TTL_MS,
126
126
  delegatorService = null,
127
127
  hitlManager = null,
128
+ onActivity = null,
128
129
  } = {}) {
129
130
  if (!router) {
130
131
  throw new Error("router is required");
@@ -164,6 +165,7 @@ export function createPipeServer({
164
165
 
165
166
  function touchClient(client) {
166
167
  client.lastHeartbeatMs = Date.now();
168
+ if (onActivity) onActivity();
167
169
  }
168
170
 
169
171
  function resolveAgentId(client, payload) {
package/hub/server.mjs CHANGED
@@ -580,6 +580,7 @@ export async function startHub({
580
580
  sessionId,
581
581
  delegatorService,
582
582
  hitlManager: hitl,
583
+ onActivity: markRequestActivity,
583
584
  });
584
585
  const assignCallbacks = createAssignCallbackServer({ store, sessionId });
585
586
  const tools = createTools(store, router, hitl, pipe);
@@ -1660,6 +1661,23 @@ if (selfRun) {
1660
1661
  const port = parseInt(process.env.TFX_HUB_PORT || "27888", 10);
1661
1662
  const dbPath = process.env.TFX_HUB_DB || undefined;
1662
1663
 
1664
+ const cleanupPidFile = () => {
1665
+ try {
1666
+ unlinkSync(PID_FILE);
1667
+ } catch {}
1668
+ };
1669
+
1670
+ process.on("unhandledRejection", (err) => {
1671
+ hubLog.fatal({ err }, "hub.unhandledRejection");
1672
+ cleanupPidFile();
1673
+ process.exit(1);
1674
+ });
1675
+ process.on("uncaughtException", (err) => {
1676
+ hubLog.fatal({ err }, "hub.uncaughtException");
1677
+ cleanupPidFile();
1678
+ process.exit(1);
1679
+ });
1680
+
1663
1681
  startHub({ port, dbPath })
1664
1682
  .then((info) => {
1665
1683
  const shutdown = async (signal) => {
@@ -1678,6 +1696,7 @@ if (selfRun) {
1678
1696
  })
1679
1697
  .catch((error) => {
1680
1698
  hubLog.fatal({ err: error }, "hub.start_failed");
1699
+ cleanupPidFile();
1681
1700
  process.exit(1);
1682
1701
  });
1683
1702
  }
package/hub/state.mjs CHANGED
@@ -256,8 +256,9 @@ export async function acquireLock(options = {}) {
256
256
  const raw = readFileSync(lockPath, "utf8");
257
257
  const data = parseJson(raw, {});
258
258
  const stats = statSync(lockPath);
259
+ const STALE_LOCK_AGE_MS = 60_000;
259
260
  const staleByPid = !isPidAlive(data?.pid);
260
- const staleByAge = Date.now() - stats.mtimeMs > timeoutMs;
261
+ const staleByAge = Date.now() - stats.mtimeMs > STALE_LOCK_AGE_MS;
261
262
  if (staleByPid || staleByAge) {
262
263
  try {
263
264
  unlinkSync(lockPath);
@@ -138,9 +138,7 @@ async function main() {
138
138
  // 실측 데이터 추출
139
139
  const stdin = await stdinPromise;
140
140
  const contextView = buildContextUsageView(stdin, contextSnapshot);
141
- const claudeUsage = claudeUsageSnapshot.isStale
142
- ? { ...(claudeUsageSnapshot.data || {}), stale: true }
143
- : claudeUsageSnapshot.data;
141
+ const claudeUsage = claudeUsageSnapshot.data;
144
142
  const codexEmail = getCodexEmail();
145
143
  const geminiEmail = getGeminiEmail();
146
144
  const codexBuckets = codexSnapshot.buckets;
package/hud/renderers.mjs CHANGED
@@ -263,8 +263,6 @@ export function getMicroLine(
263
263
  combinedSvPct,
264
264
  ) {
265
265
  const ctxView = contextView || buildContextUsageView({}, null);
266
- const staleMarker = claudeUsage?.stale ? ` ${dim("[stale]")}` : "";
267
-
268
266
  // Claude 5h/1w
269
267
  const cF =
270
268
  claudeUsage?.fiveHourPercent != null
@@ -319,7 +317,7 @@ export function getMicroLine(
319
317
  `${bold(codexWhite("x"))}${dim(":")}${xVal} ` +
320
318
  `${bold(geminiBlue("g"))}${dim(":")}${gVal} ` +
321
319
  `${dim("sv:")}${sv} ` +
322
- `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}${staleMarker}`;
320
+ `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`;
323
321
  return truncateAnsi(line, cols);
324
322
  }
325
323
 
@@ -334,8 +332,6 @@ export function getClaudeRows(
334
332
  ) {
335
333
  const ctxView = contextView || buildContextUsageView({}, null);
336
334
  const prefix = `${bold(claudeOrange("c"))}:`;
337
- const staleMarker = claudeUsage?.stale ? ` ${dim("[stale]")}` : "";
338
-
339
335
  // 절약 퍼센트
340
336
  const svStr = formatSvPct(combinedSvPct || 0);
341
337
  const svSuffix = `${dim("sv:")}${svStr}`;
@@ -388,18 +384,18 @@ export function getClaudeRows(
388
384
  hasData && weeklyPercent != null
389
385
  ? colorByProvider(weeklyPercent, `${weeklyPercent}%`, claudeOrange)
390
386
  : dim("--");
391
- const quotaSection = `${fShort}${dim("/")}${wShort}${staleMarker}`;
387
+ const quotaSection = `${fShort}${dim("/")}${wShort}`;
392
388
  return [{ prefix, left: quotaSection, right: "" }];
393
389
  }
394
390
 
395
391
  if (currentTier === "minimal") {
396
- const quotaSection = `${dim("5h:")}${fStr} ${dim("1w:")}${wStr}${staleMarker}`;
392
+ const quotaSection = `${dim("5h:")}${fStr} ${dim("1w:")}${wStr}`;
397
393
  const right = `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`;
398
394
  return [{ prefix, left: quotaSection, right }];
399
395
  }
400
396
 
401
397
  if (currentTier === "compact") {
402
- const quotaSection = `${dim("5h:")}${fStr} ${dim(fTime)} ${dim("1w:")}${wStr} ${dim(wTime)}${staleMarker}`;
398
+ const quotaSection = `${dim("5h:")}${fStr} ${dim(fTime)} ${dim("1w:")}${wStr} ${dim(wTime)}`;
403
399
  const warning = ctxView.warningTag
404
400
  ? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
405
401
  : "";
@@ -408,7 +404,7 @@ export function getClaudeRows(
408
404
  }
409
405
 
410
406
  // full tier (>= 120 cols)
411
- const quotaSection = `${dim("5h:")}${fBar}${fStr} ${dim(fTime)} ${dim("1w:")}${wBar}${wStr} ${dim(wTime)}${staleMarker}`;
407
+ const quotaSection = `${dim("5h:")}${fBar}${fStr} ${dim(fTime)} ${dim("1w:")}${wBar}${wStr} ${dim(wTime)}`;
412
408
  const warning = ctxView.warningTag
413
409
  ? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
414
410
  : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "10.9.1",
3
+ "version": "10.9.3",
4
4
  "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,26 +13,69 @@
13
13
  "tfx-doctor-tui": "bin/tfx-doctor-tui.mjs",
14
14
  "tfx-setup-tui": "bin/tfx-setup-tui.mjs"
15
15
  },
16
- "engines": {
17
- "node": ">=18.0.0"
18
- },
19
- "dependencies": {
20
- "@triflux/core": "10.0.1",
21
- "@triflux/remote": "^10.0.0-alpha.1"
22
- },
23
16
  "files": [
24
17
  "bin",
18
+ "tui",
19
+ "hub",
20
+ "config",
25
21
  "skills",
22
+ "!skills/tfx-workspace",
23
+ "!**/failure-reports",
24
+ "scripts",
26
25
  "hooks",
27
26
  "hud",
28
- "scripts",
29
- "hub",
30
27
  "mesh",
31
- "references",
32
- "CLAUDE.md",
28
+ ".claude-plugin",
33
29
  "README.md",
30
+ "README.ko.md",
34
31
  "LICENSE"
35
32
  ],
33
+ "workspaces": [
34
+ "packages/core",
35
+ "packages/remote",
36
+ "packages/triflux"
37
+ ],
38
+ "scripts": {
39
+ "pack": "node scripts/pack.mjs all",
40
+ "pack:core": "node scripts/pack.mjs core",
41
+ "pack:remote": "node scripts/pack.mjs remote",
42
+ "setup": "node scripts/setup.mjs",
43
+ "preinstall": "node scripts/preinstall.mjs",
44
+ "postinstall": "node scripts/setup.mjs",
45
+ "lint": "biome check .",
46
+ "lint:fix": "biome check --fix .",
47
+ "health": "npm test && npm run lint",
48
+ "test": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
49
+ "test:unit": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/unit/**/*.test.mjs",
50
+ "test:integration": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/integration/**/*.test.mjs",
51
+ "test:route-smoke": "node scripts/test-lock.mjs --test scripts/test-tfx-route-no-claude-native.mjs",
52
+ "test:contract": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/contract/**/*.test.mjs",
53
+ "test:coverage": "node --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=60 --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\"",
54
+ "gen:skill-docs": "node scripts/gen-skill-docs.mjs",
55
+ "gen:skill-manifest": "node scripts/gen-skill-manifest.mjs"
56
+ },
57
+ "engines": {
58
+ "node": ">=18.0.0"
59
+ },
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git+https://github.com/tellang/triflux.git"
63
+ },
64
+ "homepage": "https://github.com/tellang/triflux#readme",
65
+ "author": "tellang",
66
+ "license": "MIT",
67
+ "dependencies": {
68
+ "@modelcontextprotocol/sdk": "^1.27.1",
69
+ "better-sqlite3": "^12.6.2",
70
+ "pino": "^10.3.1",
71
+ "pino-pretty": "^13.1.3",
72
+ "systray2": "^2.1.4",
73
+ "zod": "^4.0.0"
74
+ },
75
+ "devDependencies": {
76
+ "@biomejs/biome": "^2.0.0",
77
+ "knip": "^6.3.0"
78
+ },
36
79
  "keywords": [
37
80
  "claude-code",
38
81
  "plugin",
@@ -43,13 +86,5 @@
43
86
  "multi-model",
44
87
  "triflux",
45
88
  "tfx"
46
- ],
47
- "author": "tellang",
48
- "license": "MIT",
49
- "homepage": "https://github.com/tellang/triflux#readme",
50
- "repository": {
51
- "type": "git",
52
- "url": "git+https://github.com/tellang/triflux.git",
53
- "directory": "packages/triflux"
54
- }
89
+ ]
55
90
  }
@@ -52,12 +52,12 @@ function resolveHubTarget() {
52
52
 
53
53
  async function isHubHealthy(host, port) {
54
54
  try {
55
- const res = await fetch(`${buildHubBaseUrl(host, port)}/status`, {
55
+ const res = await fetch(`${buildHubBaseUrl(host, port)}/health`, {
56
56
  signal: AbortSignal.timeout(1000),
57
57
  });
58
58
  if (!res.ok) return false;
59
59
  const data = await res.json();
60
- return data?.hub?.state === "healthy";
60
+ return data?.ok === true;
61
61
  } catch {
62
62
  return false;
63
63
  }
@@ -69,25 +69,13 @@ function startHubDetached(port) {
69
69
 
70
70
  try {
71
71
  const env = { ...process.env, TFX_HUB_PORT: String(port) };
72
- if (process.platform === "win32") {
73
- const child = spawn(
74
- "cmd.exe",
75
- ["/c", "start", "/b", "", process.execPath, serverPath],
76
- {
77
- env,
78
- stdio: "ignore",
79
- windowsHide: true,
80
- },
81
- );
82
- child.unref();
83
- } else {
84
- const child = spawn(process.execPath, [serverPath], {
85
- env,
86
- detached: true,
87
- stdio: "ignore",
88
- });
89
- child.unref();
90
- }
72
+ const child = spawn(process.execPath, [serverPath], {
73
+ env,
74
+ detached: true,
75
+ stdio: "ignore",
76
+ windowsHide: true,
77
+ });
78
+ child.unref();
91
79
  return true;
92
80
  } catch {
93
81
  return false;
@@ -114,13 +102,13 @@ export async function run(stdinData) {
114
102
 
115
103
  const started = startHubDetached(port);
116
104
  if (!started) {
117
- return { code: 0, stdout: "", stderr: "[hub-ensure] hub 시작 실패" };
105
+ return { code: 1, stdout: "", stderr: "[hub-ensure] hub 시작 실패" };
118
106
  }
119
107
 
120
- const ready = await waitForHubReady(host, port, 3000);
108
+ const ready = await waitForHubReady(host, port, 5000);
121
109
  return {
122
- code: 0,
123
- stdout: ready ? "hub: ok" : "hub: starting",
110
+ code: ready ? 0 : 2,
111
+ stdout: ready ? "hub: ok" : "hub: starting (timeout)",
124
112
  stderr: "",
125
113
  };
126
114
  }
@@ -920,7 +920,7 @@ route_agent() {
920
920
  TFX_CLI_MODE="${TFX_CLI_MODE:-auto}"
921
921
  TFX_NO_CLAUDE_NATIVE="${TFX_NO_CLAUDE_NATIVE:-0}"
922
922
  TFX_VERIFIER_OVERRIDE="${TFX_VERIFIER_OVERRIDE:-auto}"
923
- TFX_CODEX_TRANSPORT="${TFX_CODEX_TRANSPORT:-exec}"
923
+ TFX_CODEX_TRANSPORT="${TFX_CODEX_TRANSPORT:-auto}"
924
924
  # Preflight 캐시 일괄 로드 — CLI/Hub 가용성 + Codex 요금제를 환경변수로 내보냄
925
925
  # 하위 프로세스(스킬 포함)가 TFX_CODEX_OK, TFX_GEMINI_OK, TFX_HUB_OK로 즉시 참조 가능
926
926
  if [[ -z "${TFX_PREFLIGHT_LOADED:-}" ]]; then