@triflux/core 10.50.1 → 10.51.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.
Files changed (90) hide show
  1. package/hooks/agy-session-hook.mjs +11 -56
  2. package/hooks/codex-session-hook.mjs +11 -101
  3. package/hub/bridge.mjs +1 -79
  4. package/hub/cli-adapter-base.mjs +12 -90
  5. package/hub/codex-adapter.mjs +0 -1
  6. package/hub/codex-preflight.mjs +5 -28
  7. package/hub/index.mjs +3 -9
  8. package/hub/lib/cto-env.mjs +0 -23
  9. package/hub/lib/known-errors.json +2 -2
  10. package/hub/lib/process-utils.mjs +0 -123
  11. package/hub/lib/spawn-trace.mjs +3 -2
  12. package/hub/lib/timeout-defaults.mjs +0 -1
  13. package/hub/paths.mjs +0 -6
  14. package/hub/pipeline/index.mjs +0 -190
  15. package/hub/pipeline/transitions.mjs +0 -10
  16. package/hub/reflexion.mjs +1 -84
  17. package/hub/role-contract.mjs +0 -2
  18. package/hud/cli-policy.mjs +0 -26
  19. package/hud/colors.mjs +0 -13
  20. package/hud/constants.mjs +7 -153
  21. package/hud/context-monitor.mjs +3 -64
  22. package/hud/hud-qos-status.mjs +54 -264
  23. package/hud/providers/claude.mjs +4 -16
  24. package/hud/providers/gemini.mjs +83 -689
  25. package/hud/renderers.mjs +59 -367
  26. package/hud/terminal.mjs +1 -63
  27. package/hud/utils.mjs +2 -26
  28. package/package.json +1 -4
  29. package/scripts/lib/agent-route-policy.mjs +162 -14
  30. package/scripts/lib/claudemd-scanner.mjs +2 -199
  31. package/scripts/lib/cli-claude.mjs +0 -2
  32. package/scripts/lib/context.mjs +3 -19
  33. package/scripts/lib/env-probe.mjs +5 -64
  34. package/scripts/lib/gemini-profiles.mjs +2 -69
  35. package/scripts/lib/legacy-mcp-cleanup.mjs +14 -3
  36. package/scripts/lib/machine-profile.mjs +3 -17
  37. package/scripts/lib/mcp-filter.mjs +109 -53
  38. package/scripts/lib/mcp-health.mjs +2 -2
  39. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  40. package/scripts/lib/psmux-info.mjs +25 -104
  41. package/scripts/lib/session-presence.mjs +1 -1
  42. package/scripts/lib/skill-template.mjs +0 -271
  43. package/hub/codex-compat.mjs +0 -12
  44. package/hub/dynamic-routing-engine.mjs +0 -511
  45. package/hub/fullcycle.mjs +0 -113
  46. package/hub/gemini-adapter.mjs +0 -174
  47. package/hub/intent.mjs +0 -321
  48. package/hub/lib/mcp-response-cache.mjs +0 -239
  49. package/hub/lib/path-utils.mjs +0 -165
  50. package/hub/lib/risk-tier.mjs +0 -53
  51. package/hub/lib/ssh-command.mjs +0 -231
  52. package/hub/lib/staged-file-detect.mjs +0 -43
  53. package/hub/lib/tfx-route-args.mjs +0 -353
  54. package/hub/lib/timeline-adapter.mjs +0 -190
  55. package/hub/pipeline/gates/confidence.mjs +0 -75
  56. package/hub/pipeline/gates/consensus.mjs +0 -106
  57. package/hub/pipeline/gates/index.mjs +0 -9
  58. package/hub/pipeline/gates/selfcheck.mjs +0 -120
  59. package/hub/quality/deslop.mjs +0 -274
  60. package/hub/research.mjs +0 -155
  61. package/hub/role-control-events.mjs +0 -113
  62. package/hub/routing/complexity.mjs +0 -269
  63. package/hub/routing/index.mjs +0 -122
  64. package/hub/routing/q-learning.mjs +0 -387
  65. package/hub/routing-snapshot.mjs +0 -261
  66. package/hub/token-mode.mjs +0 -228
  67. package/hud/mission-board.mjs +0 -56
  68. package/hud/providers/cto.mjs +0 -76
  69. package/mesh/index.mjs +0 -69
  70. package/mesh/mesh-budget.mjs +0 -137
  71. package/mesh/mesh-heartbeat.mjs +0 -100
  72. package/mesh/mesh-protocol.mjs +0 -96
  73. package/mesh/mesh-queue.mjs +0 -163
  74. package/mesh/mesh-registry.mjs +0 -78
  75. package/mesh/mesh-router.mjs +0 -77
  76. package/scripts/lib/agent-json.mjs +0 -27
  77. package/scripts/lib/async.mjs +0 -174
  78. package/scripts/lib/cli-agy.mjs +0 -85
  79. package/scripts/lib/cli-codex.mjs +0 -62
  80. package/scripts/lib/cli-gemini.mjs +0 -37
  81. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  82. package/scripts/lib/env.mjs +0 -14
  83. package/scripts/lib/hub.mjs +0 -48
  84. package/scripts/lib/pid.mjs +0 -63
  85. package/scripts/lib/quota.mjs +0 -47
  86. package/scripts/lib/skill-state.mjs +0 -220
  87. package/scripts/lib/team.mjs +0 -75
  88. package/scripts/lib/timeout.mjs +0 -66
  89. package/scripts/lib/tmp.mjs +0 -26
  90. package/scripts/lib/toml.mjs +0 -132
package/hud/renderers.mjs CHANGED
@@ -1,9 +1,6 @@
1
1
  // ============================================================================
2
2
  // 라인 렌더러 (tier별 행 생성)
3
3
  // ============================================================================
4
- import { existsSync, readdirSync } from "node:fs";
5
- import { homedir } from "node:os";
6
- import { join } from "node:path";
7
4
  import {
8
5
  bold,
9
6
  CLAUDE_ORANGE,
@@ -13,22 +10,20 @@ import {
13
10
  colorByPercent,
14
11
  colorByProvider,
15
12
  dim,
13
+ GAUGE_WIDTH,
16
14
  GEMINI_BLUE,
17
15
  geminiBlue,
18
- green,
19
- red,
20
16
  yellow,
21
17
  } from "./colors.mjs";
22
18
  import {
23
19
  ACCOUNT_LABEL_WIDTH,
24
20
  FIVE_HOUR_MS,
25
21
  ONE_DAY_MS,
22
+ PERCENT_CELL_WIDTH,
26
23
  PROVIDER_PREFIX_WIDTH,
27
24
  SEVEN_DAY_MS,
28
- TEAM_STATE_PATH,
29
25
  } from "./constants.mjs";
30
26
  import { buildContextUsageView } from "./context-monitor.mjs";
31
- import { deriveGeminiLimits } from "./providers/gemini.mjs";
32
27
  import { getTerminalColumns, tierBar, tierDimBar } from "./terminal.mjs";
33
28
  import {
34
29
  clampPercent,
@@ -37,178 +32,13 @@ import {
37
32
  formatPlaceholderPercentCell,
38
33
  formatResetRemaining,
39
34
  formatResetRemainingDayHour,
40
- formatSavings,
41
35
  formatTimeCell,
42
36
  formatTimeCellDH,
43
- formatTokenCount,
44
37
  padAnsiRight,
45
- readJson,
46
38
  stripAnsi,
47
39
  truncateAnsi,
48
40
  } from "./utils.mjs";
49
41
 
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
- // ============================================================================
70
- // 최근 벤치마크 diff 파일 읽기
71
- // ============================================================================
72
- export function readLatestBenchmarkDiff() {
73
- const diffsDir = join(homedir(), ".omc", "state", "cx-auto-tokens", "diffs");
74
- if (!existsSync(diffsDir)) return null;
75
- 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);
82
- } catch {
83
- return null;
84
- }
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);
114
- if (!teamState?.sessionName) return null;
115
-
116
- // 팀 생존 확인: startedAt 기준 24시간 초과면 stale로 간주
117
- if (
118
- teamState.startedAt &&
119
- Date.now() - teamState.startedAt > 24 * 60 * 60 * 1000
120
- )
121
- return null;
122
-
123
- const workers = (teamState.members || []).filter((m) => m.role === "worker");
124
- if (!workers.length) return null;
125
-
126
- const tasks = teamState.tasks || [];
127
- const completed = tasks.filter((t) => t.status === "completed").length;
128
- const failed = tasks.filter((t) => t.status === "failed").length;
129
- const total = tasks.length || workers.length;
130
-
131
- // 경과 시간 (80col 이상에서만 표시)
132
- const elapsed =
133
- teamState.startedAt && (currentTier === "full" || currentTier === "compact")
134
- ? `${Math.round((Date.now() - teamState.startedAt) / 60000)}m`
135
- : "";
136
-
137
- // CLI 브랜드: 단일문자 + ANSI 색상 (x=codex, g=gemini, c=claude)
138
- const cliTag = (cli) =>
139
- cli === "codex"
140
- ? bold(codexWhite("x"))
141
- : cli === "gemini"
142
- ? bold(geminiBlue("g"))
143
- : bold(claudeOrange("c"));
144
- // 멤버 상태: 태그 + 상태기호 (60col 이상)
145
- const memberIcons =
146
- currentTier === "full" ||
147
- currentTier === "compact" ||
148
- currentTier === "minimal"
149
- ? workers
150
- .map((m) => {
151
- const task = tasks.find((t) => t.owner === m.name);
152
- const status =
153
- task?.status === "completed"
154
- ? green("\u2713")
155
- : task?.status === "in_progress"
156
- ? yellow("\u22EF")
157
- : task?.status === "failed"
158
- ? red("\u2717")
159
- : dim("\u25CC");
160
- return `${cliTag(m.cli)}${status}`;
161
- })
162
- .join(" ")
163
- : "";
164
-
165
- // 진행 텍스트
166
- const doneText =
167
- failed > 0
168
- ? `${completed}/${total} ${red(`${failed}\u2717`)}`
169
- : `${completed}/${total}`;
170
-
171
- const leftText = elapsed ? `${doneText} ${dim(elapsed)}` : doneText;
172
-
173
- return {
174
- prefix: bold(claudeOrange("\u25B2")),
175
- left: leftText,
176
- right: memberIcons,
177
- };
178
- }
179
-
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
42
  // ============================================================================
213
43
  // 행 정렬 렌더링
214
44
  // ============================================================================
@@ -227,9 +57,7 @@ export function renderAlignedRows(rows) {
227
57
  if (!hasRight) {
228
58
  return truncateAnsi(`${prefix} ${row.left}`, cols);
229
59
  }
230
- // 자기 left 대비 패딩 상한: 최대 2칸까지만 패딩 (과도한 공백 방지)
231
- const ownLen = stripAnsi(row.left).length;
232
- const effectiveWidth = Math.min(rawLeftWidth, ownLen + 2);
60
+ const effectiveWidth = rawLeftWidth;
233
61
  const left = padAnsiRight(row.left, effectiveWidth);
234
62
  // 우선순위 기반 truncate: right 먼저 축소, 그래도 넘치면 left 축소
235
63
  // prefix(PROVIDER_PREFIX_WIDTH) + " "(1) + left(effectiveWidth) + " | "(3) + right
@@ -254,15 +82,10 @@ export function getMicroLine(
254
82
  contextView,
255
83
  claudeUsage,
256
84
  codexBuckets,
257
- geminiSession,
258
- geminiBucket,
259
- geminiMarker = "g",
260
85
  options = {},
261
86
  ) {
262
- // showCodex / showGemini 는 machine profile 의 TFX_DISABLE_* 게이트다. 기본값이
263
- // true 라 8번째 인자를 주지 않는 기존 호출은 그대로 동작한다.
264
- const { showCodex = true, showGemini = true } = options;
265
- const ctxView = contextView || buildContextUsageView({}, null);
87
+ const { showCodex = true, showAntigravity = true } = options;
88
+ const ctxView = contextView || buildContextUsageView({});
266
89
  // Claude 5h/1w
267
90
  const cF =
268
91
  claudeUsage?.fiveHourPercent != null
@@ -294,30 +117,32 @@ export function getMicroLine(
294
117
  }
295
118
  }
296
119
 
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
120
  const cols = getTerminalColumns() || 120;
309
121
  // 세그먼트를 모아 join 한다. 차단된 프로바이더를 뺄 때 공백이 겹치지 않는다.
310
122
  const segments = [`${bold(claudeOrange("c"))}${dim(":")}${cVal}`];
311
123
  if (showCodex) {
312
124
  segments.push(`${bold(codexWhite("x"))}${dim(":")}${xVal}`);
313
125
  }
314
- if (showGemini) {
315
- segments.push(`${bold(geminiBlue(geminiMarker))}${dim(":")}${gVal}`);
126
+ if (showAntigravity) {
127
+ const marker = options.antigravityQuota ? bold(geminiBlue("a")) : dim("a");
128
+ segments.push(
129
+ `${marker}${dim(":")}${antigravityPercentText(options.antigravityQuota)}${options.antigravityQuota?.stale ? dim("*") : ""}`,
130
+ );
316
131
  }
317
132
  segments.push(`${dim("CTX:")}${contextPercentText(ctxView)}`);
318
133
  return truncateAnsi(segments.join(" "), cols);
319
134
  }
320
135
 
136
+ function antigravityPercentText(quota) {
137
+ return quota?.usedPercent != null
138
+ ? colorByProvider(
139
+ quota.usedPercent,
140
+ formatPercentCell(quota.usedPercent),
141
+ geminiBlue,
142
+ )
143
+ : dim("--".padStart(PERCENT_CELL_WIDTH));
144
+ }
145
+
321
146
  // context 는 토큰 수 대신 사용률만 보여 준다.
322
147
  function contextPercentText(ctxView) {
323
148
  const text = ctxView.display === "--" ? "--" : `${ctxView.percent}%`;
@@ -328,7 +153,7 @@ function contextPercentText(ctxView) {
328
153
  // Claude 행 렌더러
329
154
  // ============================================================================
330
155
  export function getClaudeRows(currentTier, contextView, claudeUsage) {
331
- const ctxView = contextView || buildContextUsageView({}, null);
156
+ const ctxView = contextView || buildContextUsageView({});
332
157
  const prefix = `${bold(claudeOrange("c"))}:`;
333
158
  // API 실측 데이터
334
159
  const fiveHourPercent = claudeUsage?.fiveHourPercent ?? null;
@@ -422,98 +247,68 @@ export function getAccountLabel(
422
247
  const providerState = accountsState?.providers?.[provider] || {};
423
248
  const lastId = providerState.last_selected_id;
424
249
  const picked = providerConfig.find((a) => a.id === lastId) ||
425
- providerConfig[0] || { id: `${provider}-main`, label: provider };
250
+ providerConfig[0] || {
251
+ id: `${provider}-main`,
252
+ label: provider === "antigravity" ? "agy" : provider,
253
+ };
426
254
  let label = picked.label || picked.id;
427
255
  if (codexEmail) label = codexEmail;
428
256
  if (label.includes("@")) label = label.split("@")[0];
429
257
  return label;
430
258
  }
431
259
 
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
260
  export function getProviderRow(
484
261
  currentTier,
485
262
  provider,
486
263
  marker,
487
264
  markerColor,
488
- qosProfile,
489
265
  accountsConfig,
490
266
  accountsState,
491
267
  realQuota,
492
268
  codexEmail,
493
- modelLabel,
494
269
  ) {
495
270
  const accountLabel = fitText(
496
271
  getAccountLabel(provider, accountsConfig, accountsState, codexEmail),
497
272
  ACCOUNT_LABEL_WIDTH,
498
273
  );
499
274
 
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
-
275
+ const prefix = `${bold(markerColor(marker))}:`;
276
+ if (provider === "antigravity") {
277
+ const usedPercent = realQuota?.usedPercent;
278
+ const active = usedPercent != null;
279
+ const quotaPercent = antigravityPercentText(realQuota);
280
+ const right = `${active ? markerColor(accountLabel) : dim(accountLabel)}${realQuota?.stale ? dim(" [stale]") : ""}`;
281
+ if (currentTier === "nano" || currentTier === "micro") {
282
+ return {
283
+ prefix: active ? prefix : dim(`${marker}:`),
284
+ left: quotaPercent,
285
+ right,
286
+ };
287
+ }
288
+ const bar = active
289
+ ? tierBar(currentTier, usedPercent, GEMINI_BLUE)
290
+ : currentTier === "full"
291
+ ? " ".repeat(GAUGE_WIDTH + 1)
292
+ : "";
293
+ const reset =
294
+ (Date.parse(realQuota?.resetTime) - Date.now() >= ONE_DAY_MS
295
+ ? formatResetRemainingDayHour(realQuota.resetTime)
296
+ : formatResetRemaining(realQuota?.resetTime)) || "n/a";
297
+ const showTime = currentTier === "full" || currentTier === "compact";
298
+ // /usage의 모델별 값에는 5h/1w 식별자가 없으므로 창을 추측하지 않는다.
299
+ const slot = `${dim("--:")}${bar}${quotaPercent}${showTime ? ` ${dim(formatTimeCell(reset))}` : ""}`;
300
+ return {
301
+ prefix: active ? prefix : dim(`${marker}:`),
302
+ left: `${slot} ${" ".repeat(stripAnsi(slot).length)}`,
303
+ right,
304
+ };
305
+ }
306
+ const provAnsi = CODEX_WHITE;
307
+ const provFn = codexWhite;
509
308
  let quotaSection;
510
- const _extraRightSection = "";
511
309
 
512
310
  if (currentTier === "nano" || currentTier === "micro") {
513
311
  const minPrefix = `${bold(markerColor(`${marker}`))}:`;
514
- if (provider === "antigravity") {
515
- return { prefix: minPrefix, left: dim("--"), right: "" };
516
- }
517
312
  if (realQuota?.type === "codex") {
518
313
  const main =
519
314
  realQuota.buckets.codex ||
@@ -542,36 +337,10 @@ export function getProviderRow(
542
337
  };
543
338
  }
544
339
  }
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
340
  return { prefix: minPrefix, left: dim("--/--"), right: "" };
564
341
  }
565
342
 
566
343
  if (currentTier === "minimal") {
567
- if (provider === "antigravity") {
568
- quotaSection = formatAntigravityQuotaSection(
569
- currentTier,
570
- realQuota,
571
- provFn,
572
- provAnsi,
573
- );
574
- }
575
344
  if (realQuota?.type === "codex") {
576
345
  const main =
577
346
  realQuota.buckets.codex ||
@@ -596,24 +365,9 @@ export function getProviderRow(
596
365
  quotaSection = `${dim("5h:")}${fCell} ${dim("1w:")}${wCell}`;
597
366
  }
598
367
  }
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
368
  if (!quotaSection) {
614
369
  quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim("1w:")}${dim(formatPlaceholderPercentCell())}`;
615
370
  }
616
- const prefix = `${bold(markerColor(`${marker}`))}:`;
617
371
  return {
618
372
  prefix,
619
373
  left: quotaSection,
@@ -622,14 +376,6 @@ export function getProviderRow(
622
376
  }
623
377
 
624
378
  if (currentTier === "compact") {
625
- if (provider === "antigravity") {
626
- quotaSection = formatAntigravityQuotaSection(
627
- currentTier,
628
- realQuota,
629
- provFn,
630
- provAnsi,
631
- );
632
- }
633
379
  if (realQuota?.type === "codex") {
634
380
  const main =
635
381
  realQuota.buckets.codex ||
@@ -659,27 +405,9 @@ export function getProviderRow(
659
405
  if (main.mixedWindows) quotaSection += dim("*");
660
406
  }
661
407
  }
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
408
  if (!quotaSection) {
680
409
  quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
681
410
  }
682
- const prefix = `${bold(markerColor(`${marker}`))}:`;
683
411
  const compactRight = [accountLabel ? markerColor(accountLabel) : ""]
684
412
  .filter(Boolean)
685
413
  .join(" ");
@@ -687,14 +415,6 @@ export function getProviderRow(
687
415
  }
688
416
 
689
417
  // full tier
690
- if (provider === "antigravity") {
691
- quotaSection = formatAntigravityQuotaSection(
692
- currentTier,
693
- realQuota,
694
- provFn,
695
- provAnsi,
696
- );
697
- }
698
418
 
699
419
  if (realQuota?.type === "codex") {
700
420
  const main =
@@ -737,43 +457,15 @@ export function getProviderRow(
737
457
  }
738
458
  }
739
459
 
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
460
  // 폴백
766
461
  if (!quotaSection) {
767
462
  quotaSection = `${dim("5h:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
768
463
  }
769
464
 
770
- const prefix = `${bold(markerColor(`${marker}`))}:`;
771
465
  const accountSection = `${markerColor(accountLabel)}`;
772
- const modelLabelSection = modelLabel ? markerColor(modelLabel) : "";
773
- const rightParts = [accountSection, modelLabelSection].filter(Boolean);
774
466
  return {
775
467
  prefix,
776
468
  left: quotaSection,
777
- right: rightParts.join(` ${dim("|")} `),
469
+ right: accountSection,
778
470
  };
779
471
  }
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
- }