triflux 2.2.0 → 2.4.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.
- package/bin/tfx-doctor.mjs +1 -1
- package/bin/tfx-setup.mjs +1 -1
- package/bin/triflux.mjs +101 -4
- package/hud/hud-qos-status.mjs +91 -20
- package/package.json +1 -1
- package/skills/tfx-auto/SKILL.md +7 -0
- package/skills/tfx-codex/SKILL.md +16 -9
- package/skills/tfx-doctor/SKILL.md +93 -0
- package/skills/tfx-gemini/SKILL.md +16 -9
- package/skills/tfx-setup/SKILL.md +16 -9
package/bin/tfx-doctor.mjs
CHANGED
package/bin/tfx-setup.mjs
CHANGED
package/bin/triflux.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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,98 @@ function cmdSetup() {
|
|
|
246
246
|
console.log(`\n${DIM}설치 위치: ${CLAUDE_DIR}${RESET}\n`);
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
function cmdDoctor() {
|
|
250
|
-
|
|
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
|
+
info("다음 세션 시작 시 캐시가 새로 생성됩니다");
|
|
287
|
+
console.log(`\n ${LINE}`);
|
|
288
|
+
console.log(` ${GREEN_BRIGHT}${BOLD}✓ 캐시 초기화 완료${RESET}\n`);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ── fix 모드: 파일 동기화 + 캐시 정리 후 진단 ──
|
|
293
|
+
if (fix) {
|
|
294
|
+
section("Auto Fix");
|
|
295
|
+
syncFile(
|
|
296
|
+
join(PKG_ROOT, "scripts", "cli-route.sh"),
|
|
297
|
+
join(CLAUDE_DIR, "scripts", "cli-route.sh"),
|
|
298
|
+
"cli-route.sh"
|
|
299
|
+
);
|
|
300
|
+
syncFile(
|
|
301
|
+
join(PKG_ROOT, "hud", "hud-qos-status.mjs"),
|
|
302
|
+
join(CLAUDE_DIR, "hud", "hud-qos-status.mjs"),
|
|
303
|
+
"hud-qos-status.mjs"
|
|
304
|
+
);
|
|
305
|
+
// 스킬 동기화
|
|
306
|
+
const fSkillsSrc = join(PKG_ROOT, "skills");
|
|
307
|
+
const fSkillsDst = join(CLAUDE_DIR, "skills");
|
|
308
|
+
if (existsSync(fSkillsSrc)) {
|
|
309
|
+
let sc = 0, st = 0;
|
|
310
|
+
for (const name of readdirSync(fSkillsSrc)) {
|
|
311
|
+
const src = join(fSkillsSrc, name, "SKILL.md");
|
|
312
|
+
const dst = join(fSkillsDst, name, "SKILL.md");
|
|
313
|
+
if (!existsSync(src)) continue;
|
|
314
|
+
st++;
|
|
315
|
+
const dstDir = dirname(dst);
|
|
316
|
+
if (!existsSync(dstDir)) mkdirSync(dstDir, { recursive: true });
|
|
317
|
+
if (!existsSync(dst)) { copyFileSync(src, dst); sc++; }
|
|
318
|
+
else if (readFileSync(src, "utf8") !== readFileSync(dst, "utf8")) { copyFileSync(src, dst); sc++; }
|
|
319
|
+
}
|
|
320
|
+
if (sc > 0) ok(`스킬: ${sc}/${st}개 업데이트됨`);
|
|
321
|
+
else ok(`스킬: ${st}개 최신 상태`);
|
|
322
|
+
}
|
|
323
|
+
// 에러/스테일 캐시 정리
|
|
324
|
+
const fCacheDir = join(CLAUDE_DIR, "cache");
|
|
325
|
+
const staleNames = ["claude-usage-cache.json", ".claude-refresh-lock", "codex-rate-limits-cache.json"];
|
|
326
|
+
let cleaned = 0;
|
|
327
|
+
for (const name of staleNames) {
|
|
328
|
+
const fp = join(fCacheDir, name);
|
|
329
|
+
if (!existsSync(fp)) continue;
|
|
330
|
+
try {
|
|
331
|
+
const parsed = JSON.parse(readFileSync(fp, "utf8"));
|
|
332
|
+
if (parsed.error || name.startsWith(".")) { unlinkSync(fp); cleaned++; ok(`에러 캐시 정리: ${name}`); }
|
|
333
|
+
} catch { try { unlinkSync(fp); cleaned++; ok(`손상된 캐시 정리: ${name}`); } catch {} }
|
|
334
|
+
}
|
|
335
|
+
if (cleaned === 0) info("에러 캐시 없음");
|
|
336
|
+
console.log(`\n ${LINE}`);
|
|
337
|
+
info("수정 완료 — 아래 진단 결과를 확인하세요");
|
|
338
|
+
console.log("");
|
|
339
|
+
}
|
|
340
|
+
|
|
252
341
|
let issues = 0;
|
|
253
342
|
|
|
254
343
|
// 1. cli-route.sh
|
|
@@ -673,6 +762,8 @@ ${updateNotice}
|
|
|
673
762
|
|
|
674
763
|
${WHITE_BRIGHT}tfx setup${RESET} ${GRAY}파일 동기화 + HUD 설정${RESET}
|
|
675
764
|
${WHITE_BRIGHT}tfx doctor${RESET} ${GRAY}CLI 진단 + 이슈 확인${RESET}
|
|
765
|
+
${DIM} --fix${RESET} ${GRAY}진단 + 자동 수정${RESET}
|
|
766
|
+
${DIM} --reset${RESET} ${GRAY}캐시 전체 초기화${RESET}
|
|
676
767
|
${WHITE_BRIGHT}tfx update${RESET} ${GRAY}최신 버전으로 업데이트${RESET}
|
|
677
768
|
${WHITE_BRIGHT}tfx list${RESET} ${GRAY}설치된 스킬 목록${RESET}
|
|
678
769
|
${WHITE_BRIGHT}tfx version${RESET} ${GRAY}버전 표시${RESET}
|
|
@@ -683,6 +774,7 @@ ${updateNotice}
|
|
|
683
774
|
${WHITE_BRIGHT}/tfx-codex${RESET} ${GRAY}Codex 전용 모드${RESET}
|
|
684
775
|
${BLUE}/tfx-gemini${RESET} ${GRAY}Gemini 전용 모드${RESET}
|
|
685
776
|
${AMBER}/tfx-setup${RESET} ${GRAY}HUD 설정 + 진단${RESET}
|
|
777
|
+
${YELLOW}/tfx-doctor${RESET} ${GRAY}진단 + 수리 + 캐시 초기화${RESET}
|
|
686
778
|
|
|
687
779
|
${LINE}
|
|
688
780
|
${GRAY}github.com/tellang/triflux${RESET}
|
|
@@ -695,7 +787,12 @@ const cmd = process.argv[2] || "help";
|
|
|
695
787
|
|
|
696
788
|
switch (cmd) {
|
|
697
789
|
case "setup": cmdSetup(); break;
|
|
698
|
-
case "doctor":
|
|
790
|
+
case "doctor": {
|
|
791
|
+
const fix = process.argv.includes("--fix");
|
|
792
|
+
const reset = process.argv.includes("--reset");
|
|
793
|
+
cmdDoctor({ fix, reset });
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
699
796
|
case "update": cmdUpdate(); break;
|
|
700
797
|
case "list": case "ls": cmdList(); break;
|
|
701
798
|
case "version": case "--version": case "-v": cmdVersion(); break;
|
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import https from "node:https";
|
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { spawn, execSync } from "node:child_process";
|
|
9
9
|
|
|
10
|
-
const VERSION = "
|
|
10
|
+
const VERSION = "2.0";
|
|
11
11
|
|
|
12
12
|
// ============================================================================
|
|
13
13
|
// ANSI 색상 (OMC colors.js 스키마 일치)
|
|
@@ -109,7 +109,15 @@ const ACCOUNTS_STATE_PATH = join(homedir(), ".omc", "state", "cli_accounts_state
|
|
|
109
109
|
const CLAUDE_CREDENTIALS_PATH = join(homedir(), ".claude", ".credentials.json");
|
|
110
110
|
const CLAUDE_USAGE_CACHE_PATH = join(homedir(), ".claude", "cache", "claude-usage-cache.json");
|
|
111
111
|
const OMC_PLUGIN_USAGE_CACHE_PATH = join(homedir(), ".claude", "plugins", "oh-my-claudecode", ".usage-cache.json");
|
|
112
|
-
const
|
|
112
|
+
const CLAUDE_USAGE_STALE_MS_SOLO = 5 * 60 * 1000; // OMC 없을 때: 5분 캐시
|
|
113
|
+
const CLAUDE_USAGE_STALE_MS_WITH_OMC = 15 * 60 * 1000; // OMC 있을 때: 15분 (OMC가 30초마다 갱신)
|
|
114
|
+
|
|
115
|
+
// OMC 활성 여부에 따라 캐시 TTL 동적 결정
|
|
116
|
+
function getClaudeUsageStaleMs() {
|
|
117
|
+
return existsSync(OMC_PLUGIN_USAGE_CACHE_PATH)
|
|
118
|
+
? CLAUDE_USAGE_STALE_MS_WITH_OMC
|
|
119
|
+
: CLAUDE_USAGE_STALE_MS_SOLO;
|
|
120
|
+
}
|
|
113
121
|
const CLAUDE_USAGE_429_BACKOFF_MS = 10 * 60 * 1000; // 429 에러 시 10분 backoff
|
|
114
122
|
const CLAUDE_USAGE_ERROR_BACKOFF_MS = 3 * 60 * 1000; // 기타 에러 시 3분 backoff
|
|
115
123
|
const CLAUDE_API_TIMEOUT_MS = 10_000;
|
|
@@ -263,7 +271,13 @@ function selectTier(stdin, claudeUsage = null) {
|
|
|
263
271
|
|
|
264
272
|
// 1) 명시적 tier 강제 설정
|
|
265
273
|
const forcedTier = hudConfig?.tier;
|
|
266
|
-
if (["full", "normal", "compact", "nano"].includes(forcedTier)) return forcedTier;
|
|
274
|
+
if (["full", "normal", "compact", "nano", "micro"].includes(forcedTier)) return forcedTier;
|
|
275
|
+
|
|
276
|
+
// 1.5) maxLines=1 → micro (1줄 모드: 알림 배너/분할화면 대응)
|
|
277
|
+
if (Number(hudConfig?.lines) === 1) return "micro";
|
|
278
|
+
|
|
279
|
+
// 1.6) 분할화면 감지: 열 < 80이면 micro (COMPACT_MODE보다 우선)
|
|
280
|
+
if ((getTerminalColumns() || 120) < 80) return "micro";
|
|
267
281
|
|
|
268
282
|
// 2) 기존 모드 플래그 존중
|
|
269
283
|
if (MINIMAL_MODE) return "nano";
|
|
@@ -275,6 +289,7 @@ function selectTier(stdin, claudeUsage = null) {
|
|
|
275
289
|
// 4) 터미널 행/열에서 상태영역 예산 추정
|
|
276
290
|
const rows = getTerminalRows();
|
|
277
291
|
const cols = getTerminalColumns() || 120;
|
|
292
|
+
|
|
278
293
|
let budget;
|
|
279
294
|
if (rows >= 40) budget = 6;
|
|
280
295
|
else if (rows >= 35) budget = 5;
|
|
@@ -285,6 +300,7 @@ function selectTier(stdin, claudeUsage = null) {
|
|
|
285
300
|
|
|
286
301
|
// 5) 인디케이터 줄 추정
|
|
287
302
|
let indicatorRows = 1; // bypass permissions (거의 항상 표시)
|
|
303
|
+
indicatorRows += 1; // 선행 개행 가드 (알림 배너 우회용 빈 줄)
|
|
288
304
|
const contextPercent = getContextPercent(stdin);
|
|
289
305
|
if (contextPercent >= 85) indicatorRows += 1; // "Context low" 배너
|
|
290
306
|
// Claude Code 사용량 경고 (노란색 배너: "You've used X% of your ... limit")
|
|
@@ -301,7 +317,8 @@ function selectTier(stdin, claudeUsage = null) {
|
|
|
301
317
|
const totalVisualRows = (3 * visualRowsPerLine) + indicatorRows;
|
|
302
318
|
if (totalVisualRows <= budget) return tier;
|
|
303
319
|
}
|
|
304
|
-
|
|
320
|
+
// 어떤 tier도 budget에 안 맞으면 micro (1줄 모드)
|
|
321
|
+
return "micro";
|
|
305
322
|
}
|
|
306
323
|
|
|
307
324
|
// full tier 전용: 게이지 바 접두사 (normal 이하 tier에서는 빈 문자열)
|
|
@@ -415,6 +432,50 @@ function renderAlignedRows(rows) {
|
|
|
415
432
|
});
|
|
416
433
|
}
|
|
417
434
|
|
|
435
|
+
// micro tier: 모든 프로바이더를 1줄로 압축 (알림 배너/분할화면 대응)
|
|
436
|
+
// 형식: c:16/3 x:5/2 g:∞ sv:143% ctx:53%
|
|
437
|
+
function getMicroLine(stdin, claudeUsage, codexBuckets, geminiSession, geminiBucket, combinedSvPct) {
|
|
438
|
+
const ctx = getContextPercent(stdin);
|
|
439
|
+
|
|
440
|
+
// Claude 5h/1w
|
|
441
|
+
const cF = claudeUsage ? clampPercent(claudeUsage.fiveHourPercent ?? 0) : null;
|
|
442
|
+
const cW = claudeUsage ? clampPercent(claudeUsage.weeklyPercent ?? 0) : null;
|
|
443
|
+
const cVal = cF != null
|
|
444
|
+
? `${colorByProvider(cF, `${cF}`, claudeOrange)}${dim("/")}${colorByProvider(cW, `${cW}`, claudeOrange)}`
|
|
445
|
+
: dim("--/--");
|
|
446
|
+
|
|
447
|
+
// Codex 5h/1w
|
|
448
|
+
let xVal = dim("--/--");
|
|
449
|
+
if (codexBuckets) {
|
|
450
|
+
const mb = codexBuckets.codex || codexBuckets[Object.keys(codexBuckets)[0]];
|
|
451
|
+
if (mb) {
|
|
452
|
+
const xF = isResetPast(mb.primary?.resets_at) ? 0 : clampPercent(mb.primary?.used_percent ?? 0);
|
|
453
|
+
const xW = isResetPast(mb.secondary?.resets_at) ? 0 : clampPercent(mb.secondary?.used_percent ?? 0);
|
|
454
|
+
xVal = `${colorByProvider(xF, `${xF}`, codexWhite)}${dim("/")}${colorByProvider(xW, `${xW}`, codexWhite)}`;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Gemini
|
|
459
|
+
let gVal;
|
|
460
|
+
if (geminiBucket) {
|
|
461
|
+
const gU = clampPercent(geminiBucket.usedPercent ?? 0);
|
|
462
|
+
gVal = colorByProvider(gU, `${gU}`, geminiBlue);
|
|
463
|
+
} else if ((geminiSession?.total || 0) > 0) {
|
|
464
|
+
gVal = geminiBlue("\u221E");
|
|
465
|
+
} else {
|
|
466
|
+
gVal = dim("--");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// sv (trimmed)
|
|
470
|
+
const sv = formatSvPct(combinedSvPct || 0).trim();
|
|
471
|
+
|
|
472
|
+
return `${bold(claudeOrange("c"))}${dim(":")}${cVal} ` +
|
|
473
|
+
`${bold(codexWhite("x"))}${dim(":")}${xVal} ` +
|
|
474
|
+
`${bold(geminiBlue("g"))}${dim(":")}${gVal} ` +
|
|
475
|
+
`${dim("sv:")}${sv} ` +
|
|
476
|
+
`${dim("ctx:")}${colorByPercent(ctx, `${ctx}%`)}`;
|
|
477
|
+
}
|
|
478
|
+
|
|
418
479
|
function clampPercent(value) {
|
|
419
480
|
const numeric = Number(value);
|
|
420
481
|
if (!Number.isFinite(numeric)) return 0;
|
|
@@ -444,7 +505,9 @@ function normalizeTimeToken(value) {
|
|
|
444
505
|
|
|
445
506
|
function formatTimeCell(value) {
|
|
446
507
|
const text = normalizeTimeToken(value);
|
|
447
|
-
|
|
508
|
+
// 시간값(숫자 포함)은 0패딩, 비시간값(n/a 등)은 공백패딩
|
|
509
|
+
const padChar = /\d/.test(text) ? "0" : " ";
|
|
510
|
+
return `(${text.padStart(TIME_CELL_INNER_WIDTH, padChar)})`;
|
|
448
511
|
}
|
|
449
512
|
|
|
450
513
|
// 주간(d/h) 전용 — 최대 7d00h(5자)이므로 공백 불필요
|
|
@@ -635,7 +698,7 @@ function readClaudeUsageSnapshot() {
|
|
|
635
698
|
|
|
636
699
|
// 1차: 자체 캐시에 유효 데이터가 있는 경우
|
|
637
700
|
if (cache?.data) {
|
|
638
|
-
const isFresh = ageMs <
|
|
701
|
+
const isFresh = ageMs < getClaudeUsageStaleMs();
|
|
639
702
|
return { data: cache.data, shouldRefresh: !isFresh };
|
|
640
703
|
}
|
|
641
704
|
|
|
@@ -651,10 +714,9 @@ function readClaudeUsageSnapshot() {
|
|
|
651
714
|
writeClaudeUsageCache(omcCache.data);
|
|
652
715
|
return { data: omcCache.data, shouldRefresh: false };
|
|
653
716
|
}
|
|
654
|
-
// stale OMC fallback 또는
|
|
717
|
+
// stale OMC fallback 또는 null (--% 플레이스홀더 표시, 가짜 0% 방지)
|
|
655
718
|
const staleData = omcCache?.data?.fiveHourPercent != null ? stripStaleResets(omcCache.data) : null;
|
|
656
|
-
|
|
657
|
-
return { data: fallback, shouldRefresh: false };
|
|
719
|
+
return { data: staleData, shouldRefresh: false };
|
|
658
720
|
}
|
|
659
721
|
}
|
|
660
722
|
|
|
@@ -665,14 +727,14 @@ function readClaudeUsageSnapshot() {
|
|
|
665
727
|
const omcAge = Number.isFinite(omcCache.timestamp) ? Date.now() - omcCache.timestamp : Number.MAX_SAFE_INTEGER;
|
|
666
728
|
if (omcAge < OMC_CACHE_MAX_AGE_MS) {
|
|
667
729
|
writeClaudeUsageCache(omcCache.data);
|
|
668
|
-
return { data: omcCache.data, shouldRefresh: omcAge >
|
|
730
|
+
return { data: omcCache.data, shouldRefresh: omcAge > getClaudeUsageStaleMs() };
|
|
669
731
|
}
|
|
670
732
|
// stale이어도 data: null보다는 오래된 데이터를 fallback으로 표시
|
|
671
733
|
return { data: stripStaleResets(omcCache.data), shouldRefresh: true };
|
|
672
734
|
}
|
|
673
735
|
|
|
674
|
-
// 캐시/fallback 모두 없음:
|
|
675
|
-
return { data:
|
|
736
|
+
// 캐시/fallback 모두 없음: null 반환 → --% 플레이스홀더 + 리프레시 시도
|
|
737
|
+
return { data: null, shouldRefresh: true };
|
|
676
738
|
}
|
|
677
739
|
|
|
678
740
|
function writeClaudeUsageCache(data, errorInfo = null) {
|
|
@@ -732,7 +794,7 @@ function scheduleClaudeUsageRefresh() {
|
|
|
732
794
|
const omcCache = readJson(OMC_PLUGIN_USAGE_CACHE_PATH, null);
|
|
733
795
|
if (omcCache?.data?.fiveHourPercent != null) {
|
|
734
796
|
const omcAge = Number.isFinite(omcCache.timestamp) ? Date.now() - omcCache.timestamp : Infinity;
|
|
735
|
-
if (omcAge <
|
|
797
|
+
if (omcAge < getClaudeUsageStaleMs()) {
|
|
736
798
|
writeClaudeUsageCache(omcCache.data); // HUD 캐시에 복사만
|
|
737
799
|
return;
|
|
738
800
|
}
|
|
@@ -751,7 +813,7 @@ function scheduleClaudeUsageRefresh() {
|
|
|
751
813
|
|
|
752
814
|
try {
|
|
753
815
|
const child = spawn(process.execPath, [scriptPath, CLAUDE_REFRESH_FLAG], {
|
|
754
|
-
detached:
|
|
816
|
+
detached: process.platform !== "win32",
|
|
755
817
|
stdio: "ignore",
|
|
756
818
|
windowsHide: true,
|
|
757
819
|
});
|
|
@@ -1050,7 +1112,7 @@ function scheduleGeminiQuotaRefresh(accountId) {
|
|
|
1050
1112
|
process.execPath,
|
|
1051
1113
|
[scriptPath, GEMINI_REFRESH_FLAG, "--account", accountId || "gemini-main"],
|
|
1052
1114
|
{
|
|
1053
|
-
detached:
|
|
1115
|
+
detached: process.platform !== "win32",
|
|
1054
1116
|
stdio: "ignore",
|
|
1055
1117
|
windowsHide: true,
|
|
1056
1118
|
},
|
|
@@ -1082,7 +1144,7 @@ function scheduleCodexRateLimitRefresh() {
|
|
|
1082
1144
|
if (!scriptPath) return;
|
|
1083
1145
|
try {
|
|
1084
1146
|
const child = spawn(process.execPath, [scriptPath, CODEX_REFRESH_FLAG], {
|
|
1085
|
-
detached:
|
|
1147
|
+
detached: process.platform !== "win32",
|
|
1086
1148
|
stdio: "ignore",
|
|
1087
1149
|
windowsHide: true,
|
|
1088
1150
|
});
|
|
@@ -1113,7 +1175,7 @@ function scheduleGeminiSessionRefresh() {
|
|
|
1113
1175
|
if (!scriptPath) return;
|
|
1114
1176
|
try {
|
|
1115
1177
|
const child = spawn(process.execPath, [scriptPath, GEMINI_SESSION_REFRESH_FLAG], {
|
|
1116
|
-
detached:
|
|
1178
|
+
detached: process.platform !== "win32",
|
|
1117
1179
|
stdio: "ignore",
|
|
1118
1180
|
windowsHide: true,
|
|
1119
1181
|
});
|
|
@@ -1486,6 +1548,14 @@ async function main() {
|
|
|
1486
1548
|
// 인디케이터 인식 tier 선택 (stdin + Claude 사용량 기반)
|
|
1487
1549
|
CURRENT_TIER = selectTier(stdin, claudeUsageSnapshot.data);
|
|
1488
1550
|
|
|
1551
|
+
// micro tier: 1줄 모드 (알림 배너/분할화면 대응)
|
|
1552
|
+
if (CURRENT_TIER === "micro") {
|
|
1553
|
+
const microLine = getMicroLine(stdin, claudeUsageSnapshot.data, codexBuckets,
|
|
1554
|
+
geminiSession, geminiBucket, combinedSvPct);
|
|
1555
|
+
process.stdout.write(`\x1b[0m${microLine}\n`);
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1489
1559
|
const codexQuotaData = codexBuckets ? { type: "codex", buckets: codexBuckets } : null;
|
|
1490
1560
|
const geminiQuotaData = { type: "gemini", quotaBucket: geminiBucket, session: geminiSession };
|
|
1491
1561
|
|
|
@@ -1509,12 +1579,13 @@ async function main() {
|
|
|
1509
1579
|
if (!geminiActive) outputLines[2] = `${DIM}${stripAnsi(outputLines[2])}${RESET}`;
|
|
1510
1580
|
}
|
|
1511
1581
|
|
|
1512
|
-
//
|
|
1582
|
+
// 선행 개행: 알림 배너(노란 글씨)가 빈 첫 줄에 오도록 → HUD 내용 보호
|
|
1583
|
+
// Context low(≥85%) 시 추가 개행으로 배너 분리
|
|
1513
1584
|
const contextPercent = getContextPercent(stdin);
|
|
1514
|
-
const
|
|
1585
|
+
const leadingBreaks = contextPercent >= 85 ? "\n\n" : "\n";
|
|
1515
1586
|
// 줄별 RESET: Claude Code TUI 스타일 간섭 방지 (색상 밝기 버그 수정)
|
|
1516
1587
|
const resetedLines = outputLines.map(line => `\x1b[0m${line}`);
|
|
1517
|
-
process.stdout.write(`${
|
|
1588
|
+
process.stdout.write(`${leadingBreaks}${resetedLines.join("\n")}\n`);
|
|
1518
1589
|
}
|
|
1519
1590
|
|
|
1520
1591
|
main().catch(() => {
|
package/package.json
CHANGED
package/skills/tfx-auto/SKILL.md
CHANGED
|
@@ -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 이상 시)
|