triflux 2.3.0 → 2.4.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.
package/bin/triflux.mjs CHANGED
@@ -1,6 +1,6 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  // triflux CLI — setup, doctor, version
3
- import { copyFileSync, existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, readdirSync } from "fs";
3
+ import { copyFileSync, existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, readdirSync, unlinkSync } from "fs";
4
4
  import { join, dirname } from "path";
5
5
  import { homedir } from "os";
6
6
  import { execSync } from "child_process";
@@ -246,9 +246,115 @@ function cmdSetup() {
246
246
  console.log(`\n${DIM}설치 위치: ${CLAUDE_DIR}${RESET}\n`);
247
247
  }
248
248
 
249
- function cmdDoctor() {
250
- console.log(`\n ${AMBER}${BOLD}⬡ triflux doctor${RESET} ${VER}\n`);
249
+ function cmdDoctor(options = {}) {
250
+ const { fix = false, reset = false } = options;
251
+ const modeLabel = reset ? ` ${RED}--reset${RESET}` : fix ? ` ${YELLOW}--fix${RESET}` : "";
252
+ console.log(`\n ${AMBER}${BOLD}⬡ triflux doctor${RESET} ${VER}${modeLabel}\n`);
251
253
  console.log(` ${LINE}`);
254
+
255
+ // ── reset 모드: 캐시 전체 초기화 ──
256
+ if (reset) {
257
+ section("Cache Reset");
258
+ const cacheDir = join(CLAUDE_DIR, "cache");
259
+ const resetFiles = [
260
+ "claude-usage-cache.json",
261
+ ".claude-refresh-lock",
262
+ "codex-rate-limits-cache.json",
263
+ "gemini-quota-cache.json",
264
+ "gemini-project-id.json",
265
+ "gemini-session-cache.json",
266
+ "gemini-rpm-tracker.json",
267
+ "sv-accumulator.json",
268
+ "mcp-inventory.json",
269
+ "cli-issues.jsonl",
270
+ "triflux-update-check.json",
271
+ ];
272
+ let cleared = 0;
273
+ for (const name of resetFiles) {
274
+ const fp = join(cacheDir, name);
275
+ if (existsSync(fp)) {
276
+ try { unlinkSync(fp); cleared++; ok(`삭제됨: ${name}`); }
277
+ catch (e) { fail(`삭제 실패: ${name} — ${e.message}`); }
278
+ }
279
+ }
280
+ if (cleared === 0) {
281
+ ok("삭제할 캐시 파일 없음 (이미 깨끗함)");
282
+ } else {
283
+ console.log("");
284
+ ok(`${BOLD}${cleared}개${RESET} 캐시 파일 초기화 완료`);
285
+ }
286
+ // 캐시 즉시 재생성
287
+ console.log("");
288
+ section("Cache Rebuild");
289
+ const mcpCheck = join(PKG_ROOT, "scripts", "mcp-check.mjs");
290
+ if (existsSync(mcpCheck)) {
291
+ try {
292
+ execSync(`"${process.execPath}" "${mcpCheck}"`, { timeout: 15000, stdio: "ignore" });
293
+ ok("MCP 인벤토리 재생성됨");
294
+ } catch { warn("MCP 인벤토리 재생성 실패 — 다음 세션에서 자동 재시도"); }
295
+ }
296
+ const hudScript = join(CLAUDE_DIR, "hud", "hud-qos-status.mjs");
297
+ if (existsSync(hudScript)) {
298
+ try {
299
+ execSync(`"${process.execPath}" "${hudScript}" --refresh-gemini`, { timeout: 15000, stdio: "ignore" });
300
+ ok("Gemini 쿼터 캐시 재생성됨");
301
+ } catch { warn("Gemini 쿼터 캐시 재생성 실패"); }
302
+ }
303
+ info("Claude/Codex 캐시는 다음 사용 시 자동 생성됩니다");
304
+ console.log(`\n ${LINE}`);
305
+ console.log(` ${GREEN_BRIGHT}${BOLD}✓ 캐시 초기화 + 재생성 완료${RESET}\n`);
306
+ return;
307
+ }
308
+
309
+ // ── fix 모드: 파일 동기화 + 캐시 정리 후 진단 ──
310
+ if (fix) {
311
+ section("Auto Fix");
312
+ syncFile(
313
+ join(PKG_ROOT, "scripts", "cli-route.sh"),
314
+ join(CLAUDE_DIR, "scripts", "cli-route.sh"),
315
+ "cli-route.sh"
316
+ );
317
+ syncFile(
318
+ join(PKG_ROOT, "hud", "hud-qos-status.mjs"),
319
+ join(CLAUDE_DIR, "hud", "hud-qos-status.mjs"),
320
+ "hud-qos-status.mjs"
321
+ );
322
+ // 스킬 동기화
323
+ const fSkillsSrc = join(PKG_ROOT, "skills");
324
+ const fSkillsDst = join(CLAUDE_DIR, "skills");
325
+ if (existsSync(fSkillsSrc)) {
326
+ let sc = 0, st = 0;
327
+ for (const name of readdirSync(fSkillsSrc)) {
328
+ const src = join(fSkillsSrc, name, "SKILL.md");
329
+ const dst = join(fSkillsDst, name, "SKILL.md");
330
+ if (!existsSync(src)) continue;
331
+ st++;
332
+ const dstDir = dirname(dst);
333
+ if (!existsSync(dstDir)) mkdirSync(dstDir, { recursive: true });
334
+ if (!existsSync(dst)) { copyFileSync(src, dst); sc++; }
335
+ else if (readFileSync(src, "utf8") !== readFileSync(dst, "utf8")) { copyFileSync(src, dst); sc++; }
336
+ }
337
+ if (sc > 0) ok(`스킬: ${sc}/${st}개 업데이트됨`);
338
+ else ok(`스킬: ${st}개 최신 상태`);
339
+ }
340
+ // 에러/스테일 캐시 정리
341
+ const fCacheDir = join(CLAUDE_DIR, "cache");
342
+ const staleNames = ["claude-usage-cache.json", ".claude-refresh-lock", "codex-rate-limits-cache.json"];
343
+ let cleaned = 0;
344
+ for (const name of staleNames) {
345
+ const fp = join(fCacheDir, name);
346
+ if (!existsSync(fp)) continue;
347
+ try {
348
+ const parsed = JSON.parse(readFileSync(fp, "utf8"));
349
+ if (parsed.error || name.startsWith(".")) { unlinkSync(fp); cleaned++; ok(`에러 캐시 정리: ${name}`); }
350
+ } catch { try { unlinkSync(fp); cleaned++; ok(`손상된 캐시 정리: ${name}`); } catch {} }
351
+ }
352
+ if (cleaned === 0) info("에러 캐시 없음");
353
+ console.log(`\n ${LINE}`);
354
+ info("수정 완료 — 아래 진단 결과를 확인하세요");
355
+ console.log("");
356
+ }
357
+
252
358
  let issues = 0;
253
359
 
254
360
  // 1. cli-route.sh
@@ -673,6 +779,8 @@ ${updateNotice}
673
779
 
674
780
  ${WHITE_BRIGHT}tfx setup${RESET} ${GRAY}파일 동기화 + HUD 설정${RESET}
675
781
  ${WHITE_BRIGHT}tfx doctor${RESET} ${GRAY}CLI 진단 + 이슈 확인${RESET}
782
+ ${DIM} --fix${RESET} ${GRAY}진단 + 자동 수정${RESET}
783
+ ${DIM} --reset${RESET} ${GRAY}캐시 전체 초기화${RESET}
676
784
  ${WHITE_BRIGHT}tfx update${RESET} ${GRAY}최신 버전으로 업데이트${RESET}
677
785
  ${WHITE_BRIGHT}tfx list${RESET} ${GRAY}설치된 스킬 목록${RESET}
678
786
  ${WHITE_BRIGHT}tfx version${RESET} ${GRAY}버전 표시${RESET}
@@ -683,6 +791,7 @@ ${updateNotice}
683
791
  ${WHITE_BRIGHT}/tfx-codex${RESET} ${GRAY}Codex 전용 모드${RESET}
684
792
  ${BLUE}/tfx-gemini${RESET} ${GRAY}Gemini 전용 모드${RESET}
685
793
  ${AMBER}/tfx-setup${RESET} ${GRAY}HUD 설정 + 진단${RESET}
794
+ ${YELLOW}/tfx-doctor${RESET} ${GRAY}진단 + 수리 + 캐시 초기화${RESET}
686
795
 
687
796
  ${LINE}
688
797
  ${GRAY}github.com/tellang/triflux${RESET}
@@ -695,7 +804,12 @@ const cmd = process.argv[2] || "help";
695
804
 
696
805
  switch (cmd) {
697
806
  case "setup": cmdSetup(); break;
698
- case "doctor": cmdDoctor(); break;
807
+ case "doctor": {
808
+ const fix = process.argv.includes("--fix");
809
+ const reset = process.argv.includes("--reset");
810
+ cmdDoctor({ fix, reset });
811
+ break;
812
+ }
699
813
  case "update": cmdUpdate(); break;
700
814
  case "list": case "ls": cmdList(); break;
701
815
  case "version": case "--version": case "-v": cmdVersion(); break;
@@ -1261,10 +1261,10 @@ function getClaudeRows(stdin, claudeUsage, combinedSvPct) {
1261
1261
  const weeklyPercent = claudeUsage?.weeklyPercent ?? 0;
1262
1262
  const fiveHourReset = claudeUsage?.fiveHourResetsAt
1263
1263
  ? formatResetRemaining(claudeUsage.fiveHourResetsAt)
1264
- : (claudeUsage ? "n/a" : "--h--m");
1264
+ : "n/a";
1265
1265
  const weeklyReset = claudeUsage?.weeklyResetsAt
1266
1266
  ? formatResetRemainingDayHour(claudeUsage.weeklyResetsAt)
1267
- : (claudeUsage ? "n/a" : "--d--h");
1267
+ : "n/a";
1268
1268
 
1269
1269
  const hasData = claudeUsage != null;
1270
1270
 
@@ -1273,7 +1273,7 @@ function getClaudeRows(stdin, claudeUsage, combinedSvPct) {
1273
1273
  if (!hasData) {
1274
1274
  const quotaSection = cols < 40
1275
1275
  ? `${dim("--%/--%")} ${dim("ctx:")}${colorByPercent(contextPercent, `${contextPercent}%`)}`
1276
- : `${dim("5h --% 1w --% sv:-- ctx:")}${colorByPercent(contextPercent, `${contextPercent}%`)}`;
1276
+ : `${dim("5h --% 1w --% sv:--% ctx:")}${colorByPercent(contextPercent, `${contextPercent}%`)}`;
1277
1277
  return [{ prefix, left: quotaSection, right: "" }];
1278
1278
  }
1279
1279
  if (cols < 40) {
@@ -1429,16 +1429,16 @@ function getProviderRow(provider, marker, markerColor, qosProfile, accountsConfi
1429
1429
  const usedP = clampPercent((1 - (bucket.remainingFraction ?? 1)) * 100);
1430
1430
  const rstRemaining = formatResetRemaining(bucket.resetTime) || "n/a";
1431
1431
  quotaSection = `${dim("1d:")}${tierBar(usedP, provAnsi)}${colorByProvider(usedP, formatPercentCell(usedP), provFn)} ${dim(formatTimeCell(rstRemaining))} ` +
1432
- `${dim("1w:")}${tierInfBar()}${dim("\u221E%".padStart(PERCENT_CELL_WIDTH))} ${dim(formatTimeCellDH("-d--h"))}`;
1432
+ `${dim("1w:")}${tierInfBar()}${dim("\u221E%".padStart(PERCENT_CELL_WIDTH))} ${dim(formatTimeCellDH("n/a"))}`;
1433
1433
  } else {
1434
1434
  quotaSection = `${dim("1d:")}${tierDimBar()}${dim(formatPlaceholderPercentCell())} ` +
1435
- `${dim(formatTimeCell("--h--m"))} ${dim("1w:")}${tierInfBar()}${dim("\u221E%".padStart(PERCENT_CELL_WIDTH))} ${dim(formatTimeCellDH("-d--h"))}`;
1435
+ `${dim(formatTimeCell("n/a"))} ${dim("1w:")}${tierInfBar()}${dim("\u221E%".padStart(PERCENT_CELL_WIDTH))} ${dim(formatTimeCellDH("n/a"))}`;
1436
1436
  }
1437
1437
  }
1438
1438
 
1439
1439
  // 폴백: 쿼터 데이터 없을 때
1440
1440
  if (!quotaSection) {
1441
- quotaSection = `${dim("5h:")}${tierDimBar()}${dim("--%")} ${dim("1w:")}${tierDimBar()}${dim("--%")}`;
1441
+ quotaSection = `${dim("5h:")}${tierDimBar()}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${tierDimBar()}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("n/a"))}`;
1442
1442
  }
1443
1443
 
1444
1444
  const prefix = `${bold(markerColor(`${marker}`))}:`;
@@ -1575,8 +1575,8 @@ async function main() {
1575
1575
 
1576
1576
  // 비활성 줄 dim 래핑 (rows 순서: [claude, codex, gemini])
1577
1577
  if (outputLines.length >= 3) {
1578
- if (!codexActive) outputLines[1] = `${DIM}${stripAnsi(outputLines[1])}${RESET}`;
1579
- if (!geminiActive) outputLines[2] = `${DIM}${stripAnsi(outputLines[2])}${RESET}`;
1578
+ if (!codexActive) outputLines[1] = `${DIM}${outputLines[1]}${RESET}`;
1579
+ if (!geminiActive) outputLines[2] = `${DIM}${outputLines[2]}${RESET}`;
1580
1580
  }
1581
1581
 
1582
1582
  // 선행 개행: 알림 배너(노란 글씨)가 빈 첫 줄에 오도록 → HUD 내용 보호
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
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": {
@@ -656,3 +656,10 @@ t3 → t4 (t3-implementation.md, 4.1KB)
656
656
  |------|------|
657
657
  | `~/.claude/scripts/cli-route.sh` | CLI 라우팅 래퍼 (필수) |
658
658
  | `/omc-teams` | tmux 기반 CLI 워커 (별도) |
659
+
660
+ ## Troubleshooting
661
+
662
+ CLI 실행 오류나 HUD 문제 발생 시:
663
+ 1. `/tfx-doctor` — 진단 실행
664
+ 2. `/tfx-doctor --fix` — 자동 수정
665
+ 3. `/tfx-doctor --reset` — 캐시 초기화
@@ -1,12 +1,12 @@
1
- ---
2
- name: tfx-codex
3
- description: Codex-Only 오케스트레이터. tfx-auto 워크플로우를 Codex 전용으로 고정합니다.
4
- triggers:
5
- - tfx-codex
6
- argument-hint: "\"작업 설명\" | N:codex \"작업 설명\""
7
- ---
8
-
9
- # tfx-codex — Codex-Only 오케스트레이터
1
+ ---
2
+ name: tfx-codex
3
+ description: Codex-Only 오케스트레이터. tfx-auto 워크플로우를 Codex 전용으로 고정합니다.
4
+ triggers:
5
+ - tfx-codex
6
+ argument-hint: "\"작업 설명\" | N:codex \"작업 설명\""
7
+ ---
8
+
9
+ # tfx-codex — Codex-Only 오케스트레이터
10
10
 
11
11
  > Codex CLI만 사용하여 모든 외부 CLI 작업을 라우팅합니다.
12
12
  > Gemini CLI가 없는 환경에서 사용합니다.
@@ -59,3 +59,10 @@ argument-hint: "\"작업 설명\" | N:codex \"작업 설명\""
59
59
 
60
60
  - [Codex CLI](https://github.com/openai/codex): `npm install -g @openai/codex`
61
61
  - Gemini CLI 불필요
62
+
63
+ ## Troubleshooting
64
+
65
+ 문제가 발생하면:
66
+ 1. `/tfx-doctor` — 진단 실행
67
+ 2. `/tfx-doctor --fix` — 자동 수정
68
+ 3. `/tfx-doctor --reset` — 캐시 초기화 (HUD 이상 시)
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: tfx-doctor
3
+ description: >
4
+ triflux 진단 및 수리 도구. CLI 미발견, HUD 미표시, 캐시 오류,
5
+ 스킬 미설치 등 문제를 진단하고 자동 수정합니다.
6
+ Use when: not working, broken, error, 안 돼, 이상해, 에러, 캐시, reset
7
+ triggers:
8
+ - tfx-doctor
9
+ argument-hint: "[--fix|--reset]"
10
+ ---
11
+
12
+ # tfx-doctor — triflux 진단 및 수리
13
+
14
+ > 뭔가 안 될 때, HUD가 이상할 때, CLI가 안 보일 때 실행하세요.
15
+
16
+ ## 사용법
17
+
18
+ ```
19
+ /tfx-doctor ← 진단만 (읽기 전용)
20
+ /tfx-doctor --fix ← 진단 + 자동 수정
21
+ /tfx-doctor --reset ← 캐시 전체 초기화
22
+ ```
23
+
24
+ ## 동작
25
+
26
+ ### 기본 모드 (`/tfx-doctor`)
27
+
28
+ `triflux doctor`를 실행하여 다음을 진단합니다:
29
+
30
+ - cli-route.sh 설치 상태
31
+ - HUD 설치 및 설정 상태
32
+ - Codex/Gemini/Claude CLI 경로 (크로스 셸)
33
+ - 스킬 설치 상태
34
+ - 플러그인 등록 상태
35
+ - MCP 인벤토리 캐시
36
+ - CLI 이슈 트래커
37
+
38
+ ### 수정 모드 (`/tfx-doctor --fix`)
39
+
40
+ 진단 전에 자동 수정을 시도합니다:
41
+
42
+ 1. cli-route.sh, HUD, 스킬 파일 재동기화
43
+ 2. 에러/손상된 캐시 파일 정리
44
+ 3. 수정 완료 후 전체 진단 실행 → 결과 보고
45
+
46
+ ### 초기화 모드 (`/tfx-doctor --reset`)
47
+
48
+ HUD 캐시 및 모든 triflux 관련 캐시를 전체 삭제합니다:
49
+
50
+ | 삭제 대상 | 설명 |
51
+ |-----------|------|
52
+ | claude-usage-cache.json | Claude 사용량 캐시 |
53
+ | codex-rate-limits-cache.json | Codex 레이트 리밋 캐시 |
54
+ | gemini-quota/session/rpm cache | Gemini 할당량/세션/RPM 캐시 |
55
+ | sv-accumulator.json | 절약량 누적 캐시 |
56
+ | mcp-inventory.json | MCP 서버 인벤토리 |
57
+ | cli-issues.jsonl | CLI 이슈 로그 |
58
+ | triflux-update-check.json | 업데이트 확인 캐시 |
59
+ | .claude-refresh-lock | 리프레시 락 파일 |
60
+
61
+ 초기화 후 다음 세션에서 캐시가 새로 생성됩니다.
62
+
63
+ ## 실행 방법
64
+
65
+ ```bash
66
+ # 진단만
67
+ Bash("triflux doctor")
68
+
69
+ # 진단 + 자동 수정
70
+ Bash("triflux doctor --fix")
71
+
72
+ # 캐시 전체 초기화
73
+ Bash("triflux doctor --reset")
74
+ ```
75
+
76
+ 결과를 사용자에게 보고합니다.
77
+
78
+ ## 자동 제안 트리거
79
+
80
+ 사용자가 다음과 같이 말하면 이 스킬 실행을 고려하세요:
81
+
82
+ - "HUD가 안 보여" / "HUD 이상해" / "상태줄이 안 나와"
83
+ - "codex/gemini가 안 돼" / "CLI 안 됨"
84
+ - "캐시 초기화" / "리셋" / "reset"
85
+ - "triflux 안 돼" / "뭔가 안 돼" / "에러"
86
+
87
+ ## 에러 처리
88
+
89
+ | 상황 | 처리 |
90
+ |------|------|
91
+ | 캐시 디렉토리 없음 | 정상 — 삭제할 파일 없음 보고 |
92
+ | 파일 삭제 권한 없음 | 수동 삭제 안내 |
93
+ | --fix 후에도 이슈 남음 | Codex/Gemini 설치는 수동 필요 안내 |
@@ -1,12 +1,12 @@
1
- ---
2
- name: tfx-gemini
3
- description: Gemini-Only 오케스트레이터. tfx-auto 워크플로우를 Gemini 전용으로 고정합니다.
4
- triggers:
5
- - tfx-gemini
6
- argument-hint: "\"작업 설명\" | N:gemini \"작업 설명\""
7
- ---
8
-
9
- # tfx-gemini — Gemini-Only 오케스트레이터
1
+ ---
2
+ name: tfx-gemini
3
+ description: Gemini-Only 오케스트레이터. tfx-auto 워크플로우를 Gemini 전용으로 고정합니다.
4
+ triggers:
5
+ - tfx-gemini
6
+ argument-hint: "\"작업 설명\" | N:gemini \"작업 설명\""
7
+ ---
8
+
9
+ # tfx-gemini — Gemini-Only 오케스트레이터
10
10
 
11
11
  > Gemini CLI만 사용하여 모든 외부 CLI 작업을 라우팅합니다.
12
12
  > Codex CLI가 없는 환경에서 사용합니다.
@@ -76,3 +76,10 @@ argument-hint: "\"작업 설명\" | N:gemini \"작업 설명\""
76
76
 
77
77
  - [Gemini CLI](https://github.com/google-gemini/gemini-cli): `npm install -g @google/gemini-cli`
78
78
  - Codex CLI 불필요
79
+
80
+ ## Troubleshooting
81
+
82
+ 문제가 발생하면:
83
+ 1. `/tfx-doctor` — 진단 실행
84
+ 2. `/tfx-doctor --fix` — 자동 수정
85
+ 3. `/tfx-doctor --reset` — 캐시 초기화 (HUD 이상 시)
@@ -1,12 +1,12 @@
1
- ---
2
- name: tfx-setup
3
- description: triflux 초기 설정 및 진단을 수행합니다.
4
- triggers:
5
- - tfx-setup
6
- argument-hint: "[doctor]"
7
- ---
8
-
9
- # tfx-setup — triflux 초기 설정 및 진단
1
+ ---
2
+ name: tfx-setup
3
+ description: triflux 초기 설정 및 진단을 수행합니다.
4
+ triggers:
5
+ - tfx-setup
6
+ argument-hint: "[doctor]"
7
+ ---
8
+
9
+ # tfx-setup — triflux 초기 설정 및 진단
10
10
 
11
11
  > 설치 후 최초 1회 실행 권장. HUD 설정, CLI 확인, 전체 진단을 수행합니다.
12
12
 
@@ -95,3 +95,10 @@ Bash("triflux doctor")
95
95
  | `settings.json` 파싱 실패 | 백업 생성 후 새로 작성 |
96
96
  | 기존 statusLine이 다른 HUD | 사용자에게 덮어쓸지 확인 |
97
97
  | node.exe 경로에 공백 | 큰따옴표로 감싸기 |
98
+
99
+ ## Troubleshooting
100
+
101
+ 설정 후에도 문제가 있으면:
102
+ 1. `/tfx-doctor` — 상세 진단 실행
103
+ 2. `/tfx-doctor --fix` — 파일 동기화 + 캐시 정리
104
+ 3. `/tfx-doctor --reset` — 캐시 전체 초기화 (HUD 이상 시)