@triflux/core 10.50.1 → 10.50.2

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 (55) hide show
  1. package/hub/index.mjs +2 -8
  2. package/hub/lib/cto-env.mjs +0 -23
  3. package/hub/paths.mjs +0 -6
  4. package/hub/pipeline/index.mjs +0 -190
  5. package/hub/pipeline/transitions.mjs +0 -10
  6. package/hub/reflexion.mjs +1 -84
  7. package/hud/cli-policy.mjs +1 -5
  8. package/hud/colors.mjs +0 -13
  9. package/hud/constants.mjs +4 -135
  10. package/hud/context-monitor.mjs +3 -64
  11. package/hud/hud-qos-status.mjs +43 -225
  12. package/hud/providers/claude.mjs +4 -16
  13. package/hud/providers/gemini.mjs +7 -698
  14. package/hud/renderers.mjs +40 -294
  15. package/hud/terminal.mjs +1 -63
  16. package/hud/utils.mjs +0 -24
  17. package/package.json +1 -2
  18. package/scripts/lib/gemini-profiles.mjs +1 -1
  19. package/scripts/lib/legacy-mcp-cleanup.mjs +14 -3
  20. package/scripts/lib/psmux-info.mjs +18 -49
  21. package/scripts/lib/skill-template.mjs +0 -26
  22. package/hub/codex-compat.mjs +0 -12
  23. package/hub/fullcycle.mjs +0 -113
  24. package/hub/gemini-adapter.mjs +0 -174
  25. package/hub/intent.mjs +0 -321
  26. package/hub/lib/mcp-response-cache.mjs +0 -239
  27. package/hub/lib/path-utils.mjs +0 -165
  28. package/hub/lib/timeline-adapter.mjs +0 -190
  29. package/hub/pipeline/gates/confidence.mjs +0 -75
  30. package/hub/pipeline/gates/consensus.mjs +0 -106
  31. package/hub/pipeline/gates/index.mjs +0 -9
  32. package/hub/pipeline/gates/selfcheck.mjs +0 -120
  33. package/hub/quality/deslop.mjs +0 -274
  34. package/hub/research.mjs +0 -155
  35. package/hub/role-control-events.mjs +0 -113
  36. package/hub/routing/complexity.mjs +0 -269
  37. package/hub/routing/index.mjs +0 -122
  38. package/hub/routing/q-learning.mjs +0 -387
  39. package/hub/token-mode.mjs +0 -228
  40. package/hud/mission-board.mjs +0 -56
  41. package/hud/providers/cto.mjs +0 -76
  42. package/scripts/lib/agent-json.mjs +0 -27
  43. package/scripts/lib/async.mjs +0 -174
  44. package/scripts/lib/cli-agy.mjs +0 -85
  45. package/scripts/lib/cli-codex.mjs +0 -62
  46. package/scripts/lib/cli-gemini.mjs +0 -37
  47. package/scripts/lib/env.mjs +0 -14
  48. package/scripts/lib/hub.mjs +0 -48
  49. package/scripts/lib/pid.mjs +0 -63
  50. package/scripts/lib/quota.mjs +0 -47
  51. package/scripts/lib/skill-state.mjs +0 -220
  52. package/scripts/lib/team.mjs +0 -75
  53. package/scripts/lib/timeout.mjs +0 -66
  54. package/scripts/lib/tmp.mjs +0 -26
  55. package/scripts/lib/toml.mjs +0 -132
package/hud/renderers.mjs CHANGED
@@ -1,8 +1,7 @@
1
1
  // ============================================================================
2
2
  // 라인 렌더러 (tier별 행 생성)
3
3
  // ============================================================================
4
- import { existsSync, readdirSync } from "node:fs";
5
- import { homedir } from "node:os";
4
+ import { readdirSync, statSync } from "node:fs";
6
5
  import { join } from "node:path";
7
6
  import {
8
7
  bold,
@@ -13,7 +12,6 @@ import {
13
12
  colorByPercent,
14
13
  colorByProvider,
15
14
  dim,
16
- GEMINI_BLUE,
17
15
  geminiBlue,
18
16
  green,
19
17
  red,
@@ -22,13 +20,11 @@ import {
22
20
  import {
23
21
  ACCOUNT_LABEL_WIDTH,
24
22
  FIVE_HOUR_MS,
25
- ONE_DAY_MS,
26
23
  PROVIDER_PREFIX_WIDTH,
27
24
  SEVEN_DAY_MS,
28
- TEAM_STATE_PATH,
25
+ TEAM_STATE_DIR,
29
26
  } from "./constants.mjs";
30
27
  import { buildContextUsageView } from "./context-monitor.mjs";
31
- import { deriveGeminiLimits } from "./providers/gemini.mjs";
32
28
  import { getTerminalColumns, tierBar, tierDimBar } from "./terminal.mjs";
33
29
  import {
34
30
  clampPercent,
@@ -37,89 +33,40 @@ import {
37
33
  formatPlaceholderPercentCell,
38
34
  formatResetRemaining,
39
35
  formatResetRemainingDayHour,
40
- formatSavings,
41
36
  formatTimeCell,
42
37
  formatTimeCellDH,
43
- formatTokenCount,
44
38
  padAnsiRight,
45
39
  readJson,
46
40
  stripAnsi,
47
41
  truncateAnsi,
48
42
  } from "./utils.mjs";
49
43
 
50
- function formatGeminiLimitValue(limit, provFn) {
51
- if (limit?.unlimited) return provFn("\u221E");
52
- const usedP = limit?.usedPct;
53
- if (usedP == null) return dim(formatPlaceholderPercentCell());
54
- return colorByProvider(usedP, formatPercentCell(usedP), provFn);
55
- }
56
-
57
- function getGeminiLimitUsedPercent(bucket, limit) {
58
- if (limit?.unlimited) return null;
59
- if (limit?.usedPct != null) return limit.usedPct;
60
- return clampPercent(Math.round((1 - (bucket?.remainingFraction ?? 1)) * 100));
61
- }
62
-
63
- function formatGeminiCompactValue(bucket, limit, provFn) {
64
- if (limit?.unlimited) return provFn("\u221E");
65
- const usedP = getGeminiLimitUsedPercent(bucket, limit);
66
- return usedP != null ? colorByProvider(usedP, `${usedP}`, provFn) : dim("--");
67
- }
68
-
69
44
  // ============================================================================
70
- // 최근 벤치마크 diff 파일 읽기
45
+ // tfx-multi 상태 행 생성 (v2.2 HUD 통합)
71
46
  // ============================================================================
72
- export function readLatestBenchmarkDiff() {
73
- const diffsDir = join(homedir(), ".omc", "state", "cx-auto-tokens", "diffs");
74
- if (!existsSync(diffsDir)) return null;
47
+ export function getTeamRow(currentTier, stateDir = TEAM_STATE_DIR) {
48
+ let latestPath = null;
49
+ let latestMtime = Date.now() - 24 * 60 * 60 * 1000;
75
50
  try {
76
- const files = readdirSync(diffsDir)
77
- .filter((f) => f.endsWith(".json"))
78
- .sort()
79
- .reverse();
80
- if (files.length === 0) return null;
81
- return readJson(join(diffsDir, files[0]), null);
51
+ for (const name of readdirSync(stateDir)) {
52
+ if (!/^team-state-.+\.json$/.test(name)) continue;
53
+ const path = join(stateDir, name);
54
+ try {
55
+ const stat = statSync(path);
56
+ if (stat.isFile() && stat.mtimeMs > latestMtime) {
57
+ latestPath = path;
58
+ latestMtime = stat.mtimeMs;
59
+ }
60
+ } catch {
61
+ // 세션 종료 중 사라진 파일은 제외한다.
62
+ }
63
+ }
82
64
  } catch {
83
65
  return null;
84
66
  }
85
- }
86
-
87
- /**
88
- * 파이프라인 벤치마크 diff 결과를 HUD 요약 문자열로 포맷
89
- */
90
- export function formatTokenSummary(diff) {
91
- if (!diff?.delta?.total || !diff?.savings) return "";
92
- const t = diff.delta.total;
93
- const s = diff.savings;
94
-
95
- const inputStr = formatTokenCount(t.input);
96
- const outputStr = formatTokenCount(t.output);
97
- const actualStr = formatSavings(s.actualCost);
98
- const _claudeStr = formatSavings(s.claudeCost);
99
- const savedPct =
100
- s.claudeCost > 0 ? Math.round((s.saved / s.claudeCost) * 100) : 0;
101
-
102
- return (
103
- `${dim("tok:")}${inputStr}${dim("in")} ${outputStr}${dim("out")} ` +
104
- `${dim("cost:")}${actualStr} ` +
105
- `${dim("sv:")}${green(formatSavings(s.saved))}${dim("(")}${savedPct}%${dim(")")}`
106
- );
107
- }
108
-
109
- // ============================================================================
110
- // tfx-multi 상태 행 생성 (v2.2 HUD 통합)
111
- // ============================================================================
112
- export function getTeamRow(currentTier) {
113
- const teamState = readJson(TEAM_STATE_PATH, null);
67
+ const teamState = latestPath ? readJson(latestPath, null) : null;
114
68
  if (!teamState?.sessionName) return null;
115
69
 
116
- // 팀 생존 확인: startedAt 기준 24시간 초과면 stale로 간주
117
- if (
118
- teamState.startedAt &&
119
- Date.now() - teamState.startedAt > 24 * 60 * 60 * 1000
120
- )
121
- return null;
122
-
123
70
  const workers = (teamState.members || []).filter((m) => m.role === "worker");
124
71
  if (!workers.length) return null;
125
72
 
@@ -177,38 +124,6 @@ export function getTeamRow(currentTier) {
177
124
  };
178
125
  }
179
126
 
180
- // ============================================================================
181
- // Mission Board 렌더러
182
- // ============================================================================
183
-
184
- const STATUS_ICON = {
185
- active: "*",
186
- idle: ".",
187
- done: "+",
188
- failed: "!",
189
- };
190
-
191
- /**
192
- * Mission Board 상태를 1줄 compact 포맷으로 렌더링한다.
193
- * 예: "MB: exec:+ ui:* perf:. [2/3 67%]"
194
- * @param {{ agents: Array<{name: string, status: string, progress: number}>, dagLevel: number, totalProgress: number } | null} state
195
- * @returns {string}
196
- */
197
- export function renderMissionBoard(state) {
198
- if (!state) return "";
199
-
200
- const { agents, totalProgress } = state;
201
- const done = agents.filter((a) => a.status === "done").length;
202
- const total = agents.length;
203
-
204
- const parts = agents.map((a) => {
205
- const icon = STATUS_ICON[a.status] ?? "?";
206
- return `${a.name}:${icon}`;
207
- });
208
-
209
- return `MB: ${parts.join(" ")} [${done}/${total} ${totalProgress}%]`;
210
- }
211
-
212
127
  // ============================================================================
213
128
  // 행 정렬 렌더링
214
129
  // ============================================================================
@@ -254,15 +169,11 @@ export function getMicroLine(
254
169
  contextView,
255
170
  claudeUsage,
256
171
  codexBuckets,
257
- geminiSession,
258
- geminiBucket,
259
- geminiMarker = "g",
172
+ antigravityReady,
260
173
  options = {},
261
174
  ) {
262
- // showCodex / showGemini 는 machine profile 의 TFX_DISABLE_* 게이트다. 기본값이
263
- // true 라 8번째 인자를 주지 않는 기존 호출은 그대로 동작한다.
264
175
  const { showCodex = true, showGemini = true } = options;
265
- const ctxView = contextView || buildContextUsageView({}, null);
176
+ const ctxView = contextView || buildContextUsageView({});
266
177
  // Claude 5h/1w
267
178
  const cF =
268
179
  claudeUsage?.fiveHourPercent != null
@@ -294,17 +205,6 @@ export function getMicroLine(
294
205
  }
295
206
  }
296
207
 
297
- // Gemini
298
- let gVal;
299
- if (geminiBucket) {
300
- const gl = deriveGeminiLimits(geminiBucket);
301
- gVal = formatGeminiCompactValue(geminiBucket, gl, geminiBlue);
302
- } else if ((geminiSession?.total || 0) > 0) {
303
- gVal = geminiBlue("\u221E");
304
- } else {
305
- gVal = dim("--");
306
- }
307
-
308
208
  const cols = getTerminalColumns() || 120;
309
209
  // 세그먼트를 모아 join 한다. 차단된 프로바이더를 뺄 때 공백이 겹치지 않는다.
310
210
  const segments = [`${bold(claudeOrange("c"))}${dim(":")}${cVal}`];
@@ -312,7 +212,9 @@ export function getMicroLine(
312
212
  segments.push(`${bold(codexWhite("x"))}${dim(":")}${xVal}`);
313
213
  }
314
214
  if (showGemini) {
315
- segments.push(`${bold(geminiBlue(geminiMarker))}${dim(":")}${gVal}`);
215
+ segments.push(
216
+ `${bold(geminiBlue(antigravityReady ? "a" : "g"))}${dim(":")}${antigravityReady ? "agy" : dim("--")}`,
217
+ );
316
218
  }
317
219
  segments.push(`${dim("CTX:")}${contextPercentText(ctxView)}`);
318
220
  return truncateAnsi(segments.join(" "), cols);
@@ -328,7 +230,7 @@ function contextPercentText(ctxView) {
328
230
  // Claude 행 렌더러
329
231
  // ============================================================================
330
232
  export function getClaudeRows(currentTier, contextView, claudeUsage) {
331
- const ctxView = contextView || buildContextUsageView({}, null);
233
+ const ctxView = contextView || buildContextUsageView({});
332
234
  const prefix = `${bold(claudeOrange("c"))}:`;
333
235
  // API 실측 데이터
334
236
  const fiveHourPercent = claudeUsage?.fiveHourPercent ?? null;
@@ -429,91 +331,38 @@ export function getAccountLabel(
429
331
  return label;
430
332
  }
431
333
 
432
- // 2슬롯: slot1 = 현재 장착 모델 약어 (Fh/Fm/Ph/Pl 등), slot2 = "Gn" Gemini family 통합
433
- // realQuota.pools.{current,gemini_family} 가 있으면 실데이터, 없으면 placeholder.
434
- function formatAntigravityQuotaSection(
435
- currentTier,
436
- realQuota,
437
- provFn,
438
- provAnsi,
439
- ) {
440
- const pools = realQuota?.pools || {};
441
- const abbr = realQuota?.currentAbbrev || "??";
442
- const currentBucket = pools.current;
443
- const familyBucket = pools.gemini_family;
444
- const hasData = Boolean(currentBucket || familyBucket);
445
-
446
- if (!hasData) {
447
- // 기존 placeholder 동작 유지
448
- if (currentTier === "minimal") {
449
- return `${dim(`${abbr}:`)}${dim(formatPlaceholderPercentCell())} ${dim("Gn:")}${dim(formatPlaceholderPercentCell())}`;
450
- }
451
- if (currentTier === "compact") {
452
- return `${dim(`${abbr}:`)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("Gn:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
453
- }
454
- return `${dim(`${abbr}:`)}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("Gn:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
455
- }
456
-
457
- const slot = (bucket, label, { withBar, withTime }) => {
458
- if (!bucket) {
459
- const bar = withBar ? tierDimBar(currentTier) : "";
460
- const base = `${dim(`${label}:`)}${bar}${dim(formatPlaceholderPercentCell())}`;
461
- return withTime ? `${base} ${dim(formatTimeCell("n/a"))}` : base;
462
- }
463
- const gl = deriveGeminiLimits(bucket);
464
- const usedP = getGeminiLimitUsedPercent(bucket, gl);
465
- const bar =
466
- withBar && usedP != null ? tierBar(currentTier, usedP, provAnsi) : "";
467
- const base = `${dim(`${label}:`)}${bar}${formatGeminiLimitValue(gl, provFn)}`;
468
- if (!withTime) return base;
469
- const rstRemaining =
470
- formatResetRemaining(bucket.resetTime, ONE_DAY_MS) || "n/a";
471
- return `${base} ${dim(formatTimeCell(rstRemaining))}`;
472
- };
473
-
474
- if (currentTier === "minimal") {
475
- return `${slot(currentBucket, abbr, { withBar: false, withTime: false })} ${slot(familyBucket, "Gn", { withBar: false, withTime: false })}`;
476
- }
477
- if (currentTier === "compact") {
478
- return `${slot(currentBucket, abbr, { withBar: false, withTime: true })} ${slot(familyBucket, "Gn", { withBar: false, withTime: true })}`;
479
- }
480
- return `${slot(currentBucket, abbr, { withBar: true, withTime: true })} ${slot(familyBucket, "Gn", { withBar: true, withTime: true })}`;
481
- }
482
-
483
334
  export function getProviderRow(
484
335
  currentTier,
485
336
  provider,
486
337
  marker,
487
338
  markerColor,
488
- qosProfile,
489
339
  accountsConfig,
490
340
  accountsState,
491
341
  realQuota,
492
342
  codexEmail,
493
- modelLabel,
494
343
  ) {
495
344
  const accountLabel = fitText(
496
345
  getAccountLabel(provider, accountsConfig, accountsState, codexEmail),
497
346
  ACCOUNT_LABEL_WIDTH,
498
347
  );
499
348
 
500
- const _modelLabelStr = modelLabel ? ` ${markerColor(modelLabel)}` : "";
501
-
502
- // 프로바이더별 색상 프로필
503
- const isGeminiFamily = provider === "gemini" || provider === "antigravity";
504
- const provAnsi =
505
- provider === "codex" ? CODEX_WHITE : isGeminiFamily ? GEMINI_BLUE : GREEN;
506
- const provFn =
507
- provider === "codex" ? codexWhite : isGeminiFamily ? geminiBlue : green;
508
-
349
+ const prefix = `${bold(markerColor(marker))}:`;
350
+ if (provider === "antigravity" || provider === "gemini") {
351
+ return {
352
+ prefix,
353
+ left:
354
+ provider === "antigravity"
355
+ ? markerColor(realQuota?.currentAbbrev || "agy")
356
+ : dim("--"),
357
+ right: accountLabel ? markerColor(accountLabel) : "",
358
+ };
359
+ }
360
+ const provAnsi = CODEX_WHITE;
361
+ const provFn = codexWhite;
509
362
  let quotaSection;
510
- const _extraRightSection = "";
511
363
 
512
364
  if (currentTier === "nano" || currentTier === "micro") {
513
365
  const minPrefix = `${bold(markerColor(`${marker}`))}:`;
514
- if (provider === "antigravity") {
515
- return { prefix: minPrefix, left: dim("--"), right: "" };
516
- }
517
366
  if (realQuota?.type === "codex") {
518
367
  const main =
519
368
  realQuota.buckets.codex ||
@@ -542,36 +391,10 @@ export function getProviderRow(
542
391
  };
543
392
  }
544
393
  }
545
- if (provider === "gemini" && realQuota?.type === "gemini") {
546
- const pools = realQuota.pools || {};
547
- if (pools.pro || pools.flash) {
548
- const pP = pools.pro ? deriveGeminiLimits(pools.pro) : null;
549
- const pF = pools.flash ? deriveGeminiLimits(pools.flash) : null;
550
- const pStr = pools.pro
551
- ? formatGeminiCompactValue(pools.pro, pP, provFn)
552
- : dim("--");
553
- const fStr = pools.flash
554
- ? formatGeminiCompactValue(pools.flash, pF, provFn)
555
- : dim("--");
556
- return {
557
- prefix: minPrefix,
558
- left: `${pStr}${dim("/")}${fStr}`,
559
- right: "",
560
- };
561
- }
562
- }
563
394
  return { prefix: minPrefix, left: dim("--/--"), right: "" };
564
395
  }
565
396
 
566
397
  if (currentTier === "minimal") {
567
- if (provider === "antigravity") {
568
- quotaSection = formatAntigravityQuotaSection(
569
- currentTier,
570
- realQuota,
571
- provFn,
572
- provAnsi,
573
- );
574
- }
575
398
  if (realQuota?.type === "codex") {
576
399
  const main =
577
400
  realQuota.buckets.codex ||
@@ -596,24 +419,9 @@ export function getProviderRow(
596
419
  quotaSection = `${dim("5h:")}${fCell} ${dim("1w:")}${wCell}`;
597
420
  }
598
421
  }
599
- if (provider === "gemini" && realQuota?.type === "gemini") {
600
- const pools = realQuota.pools || {};
601
- if (pools.pro || pools.flash) {
602
- const slot = (bucket, label) => {
603
- if (!bucket)
604
- return `${dim(label + ":")}${dim(formatPlaceholderPercentCell())}`;
605
- const gl = deriveGeminiLimits(bucket);
606
- return `${dim(label + ":")}${formatGeminiLimitValue(gl, provFn)}`;
607
- };
608
- quotaSection = `${slot(pools.pro, "Pr")} ${slot(pools.flash, "Fl")}`;
609
- } else {
610
- quotaSection = `${dim("Pr:")}${dim(formatPlaceholderPercentCell())} ${dim("Fl:")}${dim(formatPlaceholderPercentCell())}`;
611
- }
612
- }
613
422
  if (!quotaSection) {
614
423
  quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim("1w:")}${dim(formatPlaceholderPercentCell())}`;
615
424
  }
616
- const prefix = `${bold(markerColor(`${marker}`))}:`;
617
425
  return {
618
426
  prefix,
619
427
  left: quotaSection,
@@ -622,14 +430,6 @@ export function getProviderRow(
622
430
  }
623
431
 
624
432
  if (currentTier === "compact") {
625
- if (provider === "antigravity") {
626
- quotaSection = formatAntigravityQuotaSection(
627
- currentTier,
628
- realQuota,
629
- provFn,
630
- provAnsi,
631
- );
632
- }
633
433
  if (realQuota?.type === "codex") {
634
434
  const main =
635
435
  realQuota.buckets.codex ||
@@ -659,27 +459,9 @@ export function getProviderRow(
659
459
  if (main.mixedWindows) quotaSection += dim("*");
660
460
  }
661
461
  }
662
- if (provider === "gemini" && realQuota?.type === "gemini") {
663
- const pools = realQuota.pools || {};
664
- const hasAnyPool = pools.pro || pools.flash;
665
- if (hasAnyPool) {
666
- const slot = (bucket, label) => {
667
- if (!bucket)
668
- return `${dim(label + ":")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
669
- const gl = deriveGeminiLimits(bucket);
670
- const rstRemaining =
671
- formatResetRemaining(bucket.resetTime, ONE_DAY_MS) || "n/a";
672
- return `${dim(label + ":")}${formatGeminiLimitValue(gl, provFn)} ${dim(formatTimeCell(rstRemaining))}`;
673
- };
674
- quotaSection = `${slot(pools.pro, "Pr")} ${slot(pools.flash, "Fl")}`;
675
- } else {
676
- quotaSection = `${dim("Pr:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("Fl:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
677
- }
678
- }
679
462
  if (!quotaSection) {
680
463
  quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
681
464
  }
682
- const prefix = `${bold(markerColor(`${marker}`))}:`;
683
465
  const compactRight = [accountLabel ? markerColor(accountLabel) : ""]
684
466
  .filter(Boolean)
685
467
  .join(" ");
@@ -687,14 +469,6 @@ export function getProviderRow(
687
469
  }
688
470
 
689
471
  // full tier
690
- if (provider === "antigravity") {
691
- quotaSection = formatAntigravityQuotaSection(
692
- currentTier,
693
- realQuota,
694
- provFn,
695
- provAnsi,
696
- );
697
- }
698
472
 
699
473
  if (realQuota?.type === "codex") {
700
474
  const main =
@@ -737,43 +511,15 @@ export function getProviderRow(
737
511
  }
738
512
  }
739
513
 
740
- if (provider === "gemini" && realQuota?.type === "gemini") {
741
- const pools = realQuota.pools || {};
742
- const hasAnyPool = pools.pro || pools.flash;
743
-
744
- if (hasAnyPool) {
745
- const slot = (bucket, label) => {
746
- if (!bucket) {
747
- return `${dim(label + ":")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
748
- }
749
- const gl = deriveGeminiLimits(bucket);
750
- const usedP = getGeminiLimitUsedPercent(bucket, gl);
751
- const rstRemaining =
752
- formatResetRemaining(bucket.resetTime, ONE_DAY_MS) || "n/a";
753
- const bar = usedP != null ? tierBar(currentTier, usedP, provAnsi) : "";
754
- return `${dim(label + ":")}${bar}${formatGeminiLimitValue(gl, provFn)} ${dim(formatTimeCell(rstRemaining))}`;
755
- };
756
-
757
- quotaSection = `${slot(pools.pro, "Pr")} ${slot(pools.flash, "Fl")}`;
758
- } else {
759
- quotaSection =
760
- `${dim("Pr:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ` +
761
- `${dim("Fl:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
762
- }
763
- }
764
-
765
514
  // 폴백
766
515
  if (!quotaSection) {
767
516
  quotaSection = `${dim("5h:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
768
517
  }
769
518
 
770
- const prefix = `${bold(markerColor(`${marker}`))}:`;
771
519
  const accountSection = `${markerColor(accountLabel)}`;
772
- const modelLabelSection = modelLabel ? markerColor(modelLabel) : "";
773
- const rightParts = [accountSection, modelLabelSection].filter(Boolean);
774
520
  return {
775
521
  prefix,
776
522
  left: quotaSection,
777
- right: rightParts.join(` ${dim("|")} `),
523
+ right: accountSection,
778
524
  };
779
525
  }
package/hud/terminal.mjs CHANGED
@@ -60,56 +60,6 @@ export function getTerminalColumns() {
60
60
  return 0;
61
61
  }
62
62
 
63
- let _cachedRows = 0;
64
- export function getTerminalRows() {
65
- if (_cachedRows > 0) return _cachedRows;
66
- if (process.stdout.rows) {
67
- _cachedRows = process.stdout.rows;
68
- return _cachedRows;
69
- }
70
- if (process.stderr.rows) {
71
- _cachedRows = process.stderr.rows;
72
- return _cachedRows;
73
- }
74
- const envLines = Number(process.env.LINES);
75
- if (envLines > 0) {
76
- _cachedRows = envLines;
77
- return _cachedRows;
78
- }
79
- try {
80
- if (process.platform === "win32") {
81
- const raw = execSync("mode con", {
82
- timeout: 2000,
83
- stdio: ["pipe", "pipe", "pipe"],
84
- windowsHide: true,
85
- }).toString();
86
- const m =
87
- raw.match(/Lines[^:]*:\s*(\d+)/i) || raw.match(/줄[^:]*:\s*(\d+)/);
88
- if (m) {
89
- _cachedRows = Number(m[1]);
90
- return _cachedRows;
91
- }
92
- } else {
93
- const raw = execSync(
94
- "tput lines 2>/dev/null || stty size 2>/dev/null | awk '{print $1}'",
95
- {
96
- timeout: 2000,
97
- stdio: ["pipe", "pipe", "pipe"],
98
- },
99
- )
100
- .toString()
101
- .trim();
102
- if (raw && !Number.isNaN(Number(raw))) {
103
- _cachedRows = Number(raw);
104
- return _cachedRows;
105
- }
106
- }
107
- } catch {
108
- /* 감지 실패 */
109
- }
110
- return 0;
111
- }
112
-
113
63
  export function detectCompactMode() {
114
64
  // 1. 명시적 CLI 플래그
115
65
  if (process.argv.includes("--compact")) return true;
@@ -153,7 +103,7 @@ export function detectMinimalMode() {
153
103
  * 인디케이터 인식 + 터미널 크기 기반 tier 자동 선택.
154
104
  * main()에서 stdin 수신 후 호출하여 CURRENT_TIER 갱신.
155
105
  */
156
- export function selectTier(stdin, claudeUsage = null) {
106
+ export function selectTier() {
157
107
  const hudConfig = readJson(HUD_CONFIG_PATH, null);
158
108
 
159
109
  // 1) 명시적 tier 강제 설정
@@ -196,15 +146,3 @@ export function tierDimBar(currentTier) {
196
146
  ? DIM + "░".repeat(GAUGE_WIDTH) + RESET + " "
197
147
  : "";
198
148
  }
199
- // Gemini ∞% 전용: 무한 쿼터이므로 dim 회색 바
200
- export function tierInfBar(currentTier) {
201
- return currentTier === "full"
202
- ? DIM + "█".repeat(GAUGE_WIDTH) + RESET + " "
203
- : "";
204
- }
205
-
206
- // 테스트 지원: 캐시 초기화
207
- export function _resetTerminalCache() {
208
- _cachedColumns = 0;
209
- _cachedRows = 0;
210
- }
package/hud/utils.mjs CHANGED
@@ -176,17 +176,6 @@ export function getCliArgValue(flag) {
176
176
  return process.argv[idx + 1] || null;
177
177
  }
178
178
 
179
- export function formatDuration(ms) {
180
- if (!Number.isFinite(ms) || ms <= 0) return "n/a";
181
- const totalMinutes = Math.floor(ms / 60000);
182
- const days = Math.floor(totalMinutes / (60 * 24));
183
- const hours = Math.floor((totalMinutes % (60 * 24)) / 60);
184
- const minutes = totalMinutes % 60;
185
- if (days > 0) return hours > 0 ? `${days}d${hours}h` : `${days}d`;
186
- if (hours > 0) return minutes > 0 ? `${hours}h${minutes}m` : `${hours}h`;
187
- return `${minutes}m`;
188
- }
189
-
190
179
  export function formatTokenCount(n) {
191
180
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
192
181
  if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
@@ -261,13 +250,6 @@ export function formatResetRemainingDayHour(isoOrUnix, cycleMs = 0) {
261
250
  return `${String(days).padStart(2, "0")}d${String(hours).padStart(2, "0")}h`;
262
251
  }
263
252
 
264
- export function calcCooldownLeftSeconds(isoDatetime) {
265
- if (!isoDatetime) return 0;
266
- const cooldownMs = new Date(isoDatetime).getTime() - Date.now();
267
- if (!Number.isFinite(cooldownMs) || cooldownMs <= 0) return 0;
268
- return Math.ceil(cooldownMs / 1000);
269
- }
270
-
271
253
  export function getProviderAccountId(provider, accountsConfig, accountsState) {
272
254
  const providerState = accountsState?.providers?.[provider] || {};
273
255
  const selectedId = providerState.last_selected_id;
@@ -333,9 +315,3 @@ export function createHttpsPost(https, timeoutMs) {
333
315
  });
334
316
  };
335
317
  }
336
-
337
- export function formatSavings(dollars) {
338
- if (dollars >= 100) return `$${Math.round(dollars)}`;
339
- if (dollars >= 10) return `$${dollars.toFixed(1)}`;
340
- return `$${dollars.toFixed(2)}`;
341
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triflux/core",
3
- "version": "10.50.1",
3
+ "version": "10.50.2",
4
4
  "description": "triflux core — CLI routing, pipeline, adapters. Zero native dependencies.",
5
5
  "type": "module",
6
6
  "main": "hub/index.mjs",
@@ -17,7 +17,6 @@
17
17
  "@iarna/toml": "^2.2.5",
18
18
  "pino": "^10.3.1",
19
19
  "pino-pretty": "^13.1.3",
20
- "tree-kill": "^1.2.2",
21
20
  "zod": "^4.0.0"
22
21
  },
23
22
  "files": [
@@ -59,7 +59,7 @@ const LEGACY_DEFAULT_MODELS = new Set(["Gemini 3.5 Flash (Medium)"]);
59
59
  // ── 용도별 effort (SSOT) ──
60
60
  // 한 세대(3.8 Flash)만 쓰고 역할에 따라 effort 를 나눈다. 키는 tfx 에이전트/역할 이름
61
61
  // (agent-map.json 의 에이전트, tfx multi --assign 의 role). bash(tfx-route.sh)와
62
- // node(execution-mode, gemini-adapter, cli-agy)가 모두 이 표를 읽는다.
62
+ // 실행 모드와 cli-agy가 이 표를 공유한다.
63
63
  // High : 판단이 결과를 좌우하는 역할 (검토, 설계, 디버깅, 분석)
64
64
  // Medium : 생성/작성/실행 (기본값)
65
65
  // Low : 분류, 요약, 추출, 번역처럼 짧고 기계적인 작업