triflux 10.19.0 → 10.20.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.
@@ -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": "Tri-CLI orchestrator for Claude Code. Routes tasks across Claude + Codex + Gemini with consensus intelligence, natural language routing, 42 skills, and cross-model review.",
12
+ "version": "10.20.0",
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.20.0"
34
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "triflux",
3
+ "version": "10.20.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
+ }
@@ -0,0 +1,44 @@
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
+ "policy_notes": {
10
+ "transport": "Server transport accepts \"hub-url\" for the existing triflux Hub URL flow or \"http\" for direct Streamable HTTP MCP endpoints. Direct stdio registration via command/args is intentionally unsupported.",
11
+ "headers": "Optional headers are allowed only for HTTP-compatible transports. Each header value must be a descriptor: {\"value\":\"literal\"} for non-secret static values, {\"env\":\"ENV_VAR_NAME\"} for secrets resolved at sync/runtime, or {\"env\":\"ENV_VAR_NAME\",\"prefix\":\"Bearer \"} for common authorization formats.",
12
+ "secret_safety": "Resolved secret values must not be written back to this registry file. Missing env vars warn during sync and do not emit empty secret headers.",
13
+ "sync_denylist": "Array of client:server strings skipped by proactive registry sync, for example gemini:tfx-hub."
14
+ },
15
+ "servers": {
16
+ "tfx-hub": {
17
+ "transport": "hub-url",
18
+ "url": "http://127.0.0.1:27888/mcp",
19
+ "safe": true,
20
+ "targets": ["claude", "gemini", "codex"],
21
+ "description": "triflux Hub MCP 서버"
22
+ },
23
+ "context7": {
24
+ "transport": "http",
25
+ "url": "https://mcp.context7.com/mcp",
26
+ "safe": true,
27
+ "targets": ["claude", "gemini", "codex"],
28
+ "description": "Upstash Context7 — 라이브러리 문서/코드 컨텍스트 (HTTP MCP, API key 불필요)"
29
+ }
30
+ },
31
+ "policies": {
32
+ "stdio_action": "replace-with-hub",
33
+ "unknown_server_action": "warn",
34
+ "sync_denylist": [],
35
+ "watched_paths": [
36
+ "~/.gemini/settings.json",
37
+ "~/.codex/config.toml",
38
+ "~/.claude/settings.json",
39
+ "~/.claude/settings.local.json",
40
+ ".claude/mcp.json",
41
+ ".mcp.json"
42
+ ]
43
+ }
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "10.19.0",
3
+ "version": "10.20.0",
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,29 +13,81 @@
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",
26
22
  "!skills/tfx-workspace",
23
+ "!**/failure-reports",
24
+ "scripts",
27
25
  "hooks",
28
26
  "hud",
29
- "scripts",
30
- "hub",
31
27
  "mesh",
32
- "references",
33
- "!references/codex-snapshots",
34
- "!references/gemini-snapshots",
35
- "CLAUDE.md",
28
+ ".claude-plugin",
36
29
  "README.md",
30
+ "README.ko.md",
37
31
  "LICENSE"
38
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
+ "snapshot:codex": "node scripts/snapshot-codex-state.mjs",
46
+ "snapshot:gemini": "node scripts/snapshot-gemini-state.mjs",
47
+ "snapshot:all": "npm run snapshot:codex && npm run snapshot:gemini",
48
+ "lint": "biome check bin config hooks hub hud mesh scripts tests .claude-plugin .github package.json package-lock.json biome.json",
49
+ "lint:fix": "biome check --write bin config hooks hub hud mesh scripts tests .claude-plugin .github package.json package-lock.json biome.json",
50
+ "health": "npm test && npm run lint",
51
+ "test": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
52
+ "test:guard-codex-config": "node scripts/check-codex-config-stable.mjs npm test",
53
+ "test:unit": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/unit/**/*.test.mjs",
54
+ "test:integration": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/integration/**/*.test.mjs",
55
+ "test:route-smoke": "node scripts/test-lock.mjs --test scripts/test-tfx-route-no-claude-native.mjs",
56
+ "test:contract": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/contract/**/*.test.mjs",
57
+ "test:coverage": "node --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=60 --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\"",
58
+ "gen:skill-docs": "node scripts/gen-skill-docs.mjs",
59
+ "gen:skill-manifest": "node scripts/gen-skill-manifest.mjs",
60
+ "release:check-sync": "node scripts/release/check-sync.mjs",
61
+ "release:check-sync:fix": "node scripts/release/check-sync.mjs --fix",
62
+ "release:check-mirror": "node scripts/release/check-packages-mirror.mjs",
63
+ "release:check-mirror:fix": "node scripts/release/check-packages-mirror.mjs --fix",
64
+ "release:bump": "node scripts/release/bump-version.mjs",
65
+ "release:prepare": "node scripts/release/prepare.mjs",
66
+ "release:publish": "node scripts/release/publish.mjs",
67
+ "release:verify": "node scripts/release/verify.mjs"
68
+ },
69
+ "engines": {
70
+ "node": ">=18.0.0"
71
+ },
72
+ "repository": {
73
+ "type": "git",
74
+ "url": "git+https://github.com/tellang/triflux.git"
75
+ },
76
+ "homepage": "https://github.com/tellang/triflux#readme",
77
+ "author": "tellang",
78
+ "license": "MIT",
79
+ "dependencies": {
80
+ "@modelcontextprotocol/sdk": "^1.29.0",
81
+ "better-sqlite3": "^12.6.2",
82
+ "pino": "^10.3.1",
83
+ "pino-pretty": "^13.1.3",
84
+ "systray2": "^2.1.4",
85
+ "zod": "^4.0.0"
86
+ },
87
+ "devDependencies": {
88
+ "@biomejs/biome": "^2.0.0",
89
+ "knip": "^6.3.0"
90
+ },
39
91
  "keywords": [
40
92
  "claude-code",
41
93
  "plugin",
@@ -46,13 +98,5 @@
46
98
  "multi-model",
47
99
  "triflux",
48
100
  "tfx"
49
- ],
50
- "author": "tellang",
51
- "license": "MIT",
52
- "homepage": "https://github.com/tellang/triflux#readme",
53
- "repository": {
54
- "type": "git",
55
- "url": "git+https://github.com/tellang/triflux.git",
56
- "directory": "packages/triflux"
57
- }
101
+ ]
58
102
  }
@@ -3,7 +3,7 @@
3
3
  // mcp-gateway-ensure.mjs — SessionStart 훅에서 supergateway MCP 서비스 보장
4
4
  // hub-ensure.mjs 패턴을 따름. 가볍게 헬스체크만 수행하고 필요시 기동.
5
5
 
6
- import { execSync } from "node:child_process";
6
+ import { execSync, spawn } from "node:child_process";
7
7
  import { existsSync } from "node:fs";
8
8
  import { tmpdir } from "node:os";
9
9
  import { dirname, join } from "node:path";
@@ -36,10 +36,19 @@ function startGateway() {
36
36
  if (!existsSync(scriptPath)) return false;
37
37
 
38
38
  try {
39
- execSync(
40
- `powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath '${process.execPath}' -ArgumentList '${scriptPath.replaceAll("'", "''")}'"`,
41
- { stdio: "ignore", timeout: 10000 },
42
- );
39
+ if (process.platform === "win32") {
40
+ execSync(
41
+ `powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath '${process.execPath}' -ArgumentList '${scriptPath.replaceAll("'", "''")}'"`,
42
+ { stdio: "ignore", timeout: 10000 },
43
+ );
44
+ } else {
45
+ // POSIX: detached node child + stdio:'ignore' so SessionStart 훅이 block 되지 않음
46
+ const child = spawn(process.execPath, [scriptPath], {
47
+ detached: true,
48
+ stdio: "ignore",
49
+ });
50
+ child.unref();
51
+ }
43
52
  return true;
44
53
  } catch {
45
54
  return false;
@@ -51,18 +51,34 @@ function runScript(scriptPath, ...args) {
51
51
  }
52
52
 
53
53
  function countSupergateways() {
54
+ if (process.platform === "win32") {
55
+ try {
56
+ // Write the query as a PS1 file to avoid shell quoting issues
57
+ const ps1 = [
58
+ `$procs = Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='cmd.exe'"`,
59
+ `$hits = $procs | Where-Object { $_.CommandLine -match 'supergateway' }`,
60
+ `Write-Output $hits.Count`,
61
+ ].join("\n");
62
+ const out = execSync(
63
+ `powershell -NoProfile -Command "${ps1.replace(/\n/g, "; ")}"`,
64
+ { encoding: "utf8", timeout: 10000, stdio: ["pipe", "pipe", "ignore"] },
65
+ );
66
+ return parseInt(out.trim(), 10) || 0;
67
+ } catch {
68
+ return 0;
69
+ }
70
+ }
71
+
72
+ // POSIX: pgrep -f → 줄별 PID. macOS BSD pgrep 은 `-c` 미지원이라
73
+ // PID 목록을 받아 줄 수로 카운트한다. 0 match 시 exit 1.
54
74
  try {
55
- // Write the query as a PS1 file to avoid shell quoting issues
56
- const ps1 = [
57
- `$procs = Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='cmd.exe'"`,
58
- `$hits = $procs | Where-Object { $_.CommandLine -match 'supergateway' }`,
59
- `Write-Output $hits.Count`,
60
- ].join("\n");
61
- const out = execSync(
62
- `powershell -NoProfile -Command "${ps1.replace(/\n/g, "; ")}"`,
63
- { encoding: "utf8", timeout: 10000, stdio: ["pipe", "pipe", "ignore"] },
64
- );
65
- return parseInt(out.trim(), 10) || 0;
75
+ const out = execSync("pgrep -f supergateway", {
76
+ encoding: "utf8",
77
+ stdio: ["ignore", "pipe", "ignore"],
78
+ timeout: 5000,
79
+ });
80
+ const trimmed = out.trim();
81
+ return trimmed ? trimmed.split("\n").filter(Boolean).length : 0;
66
82
  } catch {
67
83
  return 0;
68
84
  }
@@ -4,7 +4,7 @@
4
4
  // node mcp-gateway-start.mjs --stop # 중지
5
5
  // node mcp-gateway-start.mjs --status # 상태 확인
6
6
 
7
- import { execSync } from "node:child_process";
7
+ import { execSync, spawn } from "node:child_process";
8
8
  import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
9
9
  import { createConnection } from "node:net";
10
10
  import { tmpdir } from "node:os";
@@ -104,16 +104,41 @@ function sleep(ms) {
104
104
  // ── 시작 ──
105
105
 
106
106
  function spawnGateway(srv) {
107
- // 임시 .cmd 파일로 quoting 문제 회피
108
- const cmdContent = `@echo off\nnpx -y supergateway --stdio "${srv.cmd}" --port ${srv.port} --outputTransport sse --healthEndpoint /healthz --cors "http://localhost"`;
109
- const cmdFile = join(tmpdir(), `tfx-sg-${srv.name}.cmd`);
110
- writeFileSync(cmdFile, cmdContent);
111
-
112
- // PowerShell Start-Process: Windows Job Object에서 벗어나 부모 종료 후 생존
113
- execSync(
114
- `powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath cmd.exe -ArgumentList '/c','${cmdFile.replaceAll("'", "''")}'"`,
115
- { stdio: "ignore", timeout: 10000 },
116
- );
107
+ if (process.platform === "win32") {
108
+ // 임시 .cmd 파일로 quoting 문제 회피
109
+ const cmdContent = `@echo off\nnpx -y supergateway --stdio "${srv.cmd}" --port ${srv.port} --outputTransport sse --healthEndpoint /healthz --cors "http://localhost"`;
110
+ const cmdFile = join(tmpdir(), `tfx-sg-${srv.name}.cmd`);
111
+ writeFileSync(cmdFile, cmdContent);
112
+
113
+ // PowerShell Start-Process: Windows Job Object에서 벗어나 부모 종료 후 생존
114
+ execSync(
115
+ `powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath cmd.exe -ArgumentList '/c','${cmdFile.replaceAll("'", "''")}'"`,
116
+ { stdio: "ignore", timeout: 10000 },
117
+ );
118
+ return;
119
+ }
120
+
121
+ // POSIX (macOS/Linux): detached child + stdio:'ignore' so parent can exit
122
+ // 부모 process 가 죽어도 daemon 이 살아남도록 detached + unref.
123
+ const args = [
124
+ "-y",
125
+ "supergateway",
126
+ "--stdio",
127
+ srv.cmd,
128
+ "--port",
129
+ String(srv.port),
130
+ "--outputTransport",
131
+ "sse",
132
+ "--healthEndpoint",
133
+ "/healthz",
134
+ "--cors",
135
+ "http://localhost",
136
+ ];
137
+ const child = spawn("npx", args, {
138
+ detached: true,
139
+ stdio: "ignore",
140
+ });
141
+ child.unref();
117
142
  }
118
143
 
119
144
  function ensureFirewallRule() {
@@ -221,30 +246,57 @@ async function startAll() {
221
246
  // ── 중지 ──
222
247
 
223
248
  function stopAll() {
224
- // supergateway + 하위 MCP 프로세스를 포트 기반으로 찾아 종료
225
- try {
226
- // temp .ps1 파일로 bash/cmd 쿼팅 충돌 회피
227
- const psFile = join(tmpdir(), "tfx-sg-stop.ps1");
228
- writeFileSync(
229
- psFile,
230
- [
231
- `Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='cmd.exe'" |`,
232
- ` Where-Object { $_.CommandLine -match 'supergateway' } |`,
233
- ` ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null; Write-Output "[STOP] PID $($_.ProcessId)" }`,
234
- ].join("\n"),
235
- );
236
- const output = execSync(
237
- `powershell -NoProfile -ExecutionPolicy Bypass -File "${psFile}"`,
238
- {
249
+ // supergateway + 하위 MCP 프로세스를 cmdline 기반으로 찾아 종료
250
+ if (process.platform === "win32") {
251
+ try {
252
+ // temp .ps1 파일로 bash/cmd 쿼팅 충돌 회피
253
+ const psFile = join(tmpdir(), "tfx-sg-stop.ps1");
254
+ writeFileSync(
255
+ psFile,
256
+ [
257
+ `Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='cmd.exe'" |`,
258
+ ` Where-Object { $_.CommandLine -match 'supergateway' } |`,
259
+ ` ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null; Write-Output "[STOP] PID $($_.ProcessId)" }`,
260
+ ].join("\n"),
261
+ );
262
+ const output = execSync(
263
+ `powershell -NoProfile -ExecutionPolicy Bypass -File "${psFile}"`,
264
+ {
265
+ encoding: "utf8",
266
+ timeout: 10000,
267
+ stdio: ["pipe", "pipe", "ignore"],
268
+ },
269
+ );
270
+ if (output.trim()) console.log(output.trim());
271
+ else console.log("[gateway] No supergateway processes found");
272
+ } catch {
273
+ console.log("[gateway] No supergateway processes found");
274
+ }
275
+ } else {
276
+ // POSIX: pgrep -f 로 cmdline match → kill -TERM. pgrep 은 0 hit 시 exit 1.
277
+ try {
278
+ const out = execSync("pgrep -f supergateway", {
239
279
  encoding: "utf8",
240
- timeout: 10000,
241
- stdio: ["pipe", "pipe", "ignore"],
242
- },
243
- );
244
- if (output.trim()) console.log(output.trim());
245
- else console.log("[gateway] No supergateway processes found");
246
- } catch {
247
- console.log("[gateway] No supergateway processes found");
280
+ stdio: ["ignore", "pipe", "ignore"],
281
+ timeout: 5000,
282
+ }).trim();
283
+ const pids = out ? out.split("\n").filter(Boolean) : [];
284
+ if (pids.length === 0) {
285
+ console.log("[gateway] No supergateway processes found");
286
+ } else {
287
+ for (const pid of pids) {
288
+ try {
289
+ execSync(`kill -TERM ${pid}`, { stdio: "ignore" });
290
+ console.log(`[STOP] PID ${pid}`);
291
+ } catch {
292
+ /* 이미 죽었거나 권한 부족 — 다음 PID 시도 */
293
+ }
294
+ }
295
+ }
296
+ } catch {
297
+ // pgrep no match → exit 1
298
+ console.log("[gateway] No supergateway processes found");
299
+ }
248
300
  }
249
301
 
250
302
  if (existsSync(PID_FILE)) {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  internal: true
3
3
  name: tfx-research
4
- description: "웹 검색/리서치가 필요할 때 사용한다. '검색해줘', '찾아봐', '최신 정보', '이거 뭐야', '심층 조사', '자세히 알아봐', 'deep research', '전면 리서치', '자율 리서치', '조사해', 'research and plan' 같은 요청에 반드시 사용. 기본값은 3-CLI 멀티소스(Exa+Brave+Tavily) 합의 딥 리서치. 빠른 Gemini Google Search 는 --quick. 자율 쿼리생성+보고서 모드는 --auto."
4
+ description: "웹 검색/리서치가 필요할 때 사용한다. '검색해줘', '찾아봐', '최신 정보', '이거 뭐야', '심층 조사', '자세히 알아봐', 'deep research', '전면 리서치', '자율 리서치', '조사해', 'research and plan' 같은 요청에 반드시 사용. 추가로 'X 있나?', 'X 쓸 수 있나?', 'X 풀려있어?', 'X 어떻게 쓰는지', 'X 가능한가?', '방법 있나?', 'X 살아있나?' 같은 도구·기능 존재/사용법/상태 의문문에도 사용. 기본값은 3-CLI 멀티소스(Exa+Brave+Tavily) 합의 딥 리서치. 빠른 Gemini Google Search 는 --quick. 자율 쿼리생성+보고서 모드는 --auto."
5
5
  triggers:
6
6
  - tfx-research
7
7
  - 리서치