@triflux/core 10.50.0 → 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
@@ -4,60 +4,15 @@ import { dirname, join } from "node:path";
4
4
 
5
5
  import {
6
6
  CONTEXT_MONITOR_CACHE_PATH,
7
- CONTEXT_MONITOR_LEGACY_PATH,
8
7
  CONTEXT_MONITOR_LOG_DIR,
9
8
  } from "./constants.mjs";
10
- import { clampPercent, formatTokenCount, readJsonMigrate } from "./utils.mjs";
9
+ import { clampPercent, formatTokenCount } from "./utils.mjs";
11
10
 
12
11
  const DEFAULT_CONTEXT_LIMIT = 200_000;
13
12
  const MILLION_CONTEXT_LIMIT = 1_000_000;
14
13
  const MAX_CAPTURE_BYTES = 256 * 1024;
15
14
  const MAX_TOP_KEYS = 20;
16
15
 
17
- // stdin 이 context_window_size 를 제공하지 않을 때 모델 ID 로 한도를 추정한다.
18
- // Anthropic 공식 문서(2026-09-30 기준): Opus 4.6 이상, Sonnet 4.6/5/5.5,
19
- // Fable 5/5.1 = 1M. Sonnet 4.5 / Haiku 4.5 = 200K이며 [1m]도 유지한다.
20
- const MODEL_HINT_1M_PREFIXES = [
21
- "claude-sonnet-4-6",
22
- "claude-sonnet-5",
23
- "claude-fable-5",
24
- ];
25
-
26
- function normalizeModelId(modelId) {
27
- if (!modelId) return "";
28
- return String(modelId).toLowerCase();
29
- }
30
-
31
- function isMillionContextOpusModel(modelId) {
32
- const match = /^claude-opus-(\d+)(?:-(\d+))?(?:$|[-[\s])/u.exec(modelId);
33
- if (!match) return false;
34
-
35
- const major = Number(match[1]);
36
- const minor = Number(match[2] || 0);
37
- return major > 4 || (major === 4 && minor >= 6);
38
- }
39
-
40
- function isMillionContextModel(modelId) {
41
- const id = normalizeModelId(modelId);
42
- if (!id) return false;
43
- if (id.includes("[1m]")) return true;
44
- if (isMillionContextOpusModel(id)) return true;
45
- return MODEL_HINT_1M_PREFIXES.some((prefix) => id.startsWith(prefix));
46
- }
47
-
48
- function resolveModelLimit(modelId) {
49
- return isMillionContextModel(modelId)
50
- ? MILLION_CONTEXT_LIMIT
51
- : DEFAULT_CONTEXT_LIMIT;
52
- }
53
-
54
- export function shouldSuppressInfoOnlyContextStatus(modelId, contextLimit = 0) {
55
- const explicitLimit = Number(contextLimit);
56
- return explicitLimit > 0
57
- ? explicitLimit >= MILLION_CONTEXT_LIMIT
58
- : isMillionContextModel(modelId);
59
- }
60
-
61
16
  const WARNING_LEVELS = Object.freeze({
62
17
  ok: { min: 0, message: "" },
63
18
  info: { min: 60, message: "컨텍스트 절반 이상 사용" },
@@ -244,14 +199,6 @@ export function formatContextUsage(usedTokens, limitTokens, percent = null) {
244
199
  return `${formatTokenCount(used)}/${formatTokenCount(limit)} (${pct}%)`;
245
200
  }
246
201
 
247
- export function readContextMonitorSnapshot() {
248
- return readJsonMigrate(
249
- CONTEXT_MONITOR_CACHE_PATH,
250
- CONTEXT_MONITOR_LEGACY_PATH,
251
- null,
252
- );
253
- }
254
-
255
202
  function getStdinContextUsage(stdin) {
256
203
  const limitTokens = Number(stdin?.context_window?.context_window_size || 0);
257
204
  const nativePercent = Number(stdin?.context_window?.used_percentage);
@@ -284,14 +231,7 @@ function getStdinContextUsage(stdin) {
284
231
  return null;
285
232
  }
286
233
 
287
- export function deriveContextLimit(stdin) {
288
- const explicit = Number(stdin?.context_window?.context_window_size || 0);
289
- const modelHint = resolveModelLimit(stdin?.model?.id ?? stdin?.model);
290
- return Math.max(explicit, modelHint);
291
- }
292
-
293
- export function buildContextUsageView(stdin, snapshot = null) {
294
- const modelId = stdin?.model?.id ?? stdin?.model;
234
+ export function buildContextUsageView(stdin) {
295
235
  const stdinUsage = getStdinContextUsage(stdin);
296
236
  if (!stdinUsage) {
297
237
  return {
@@ -312,8 +252,7 @@ export function buildContextUsageView(stdin, snapshot = null) {
312
252
 
313
253
  const warning = classifyContextThreshold(percent);
314
254
  const showInfoOnlyStatus = !(
315
- warning.level === "info" &&
316
- shouldSuppressInfoOnlyContextStatus(modelId, stdinUsage.limitTokens)
255
+ warning.level === "info" && stdinUsage.limitTokens >= MILLION_CONTEXT_LIMIT
317
256
  );
318
257
  return {
319
258
  usedTokens,
@@ -1,11 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // ============================================================================
4
- // HUD QoS Status — 메인 오케스트레이터
5
- // 각 모듈에서 색상, 터미널, 프로바이더, 렌더러를 가져와 조합한다.
6
- // ============================================================================
7
-
8
- import { statSync } from "node:fs";
3
+ import { existsSync, statSync } from "node:fs";
4
+ import { homedir } from "node:os";
9
5
  import { join } from "node:path";
10
6
  import {
11
7
  resolveHudCliVisibility,
@@ -18,7 +14,6 @@ import {
18
14
  DIM,
19
15
  dim,
20
16
  geminiBlue,
21
- green,
22
17
  RESET,
23
18
  } from "./colors.mjs";
24
19
  import {
@@ -28,107 +23,54 @@ import {
28
23
  CLAUDE_BAND_MARKER_TTL_MS,
29
24
  CLAUDE_REFRESH_FLAG,
30
25
  CODEX_REFRESH_FLAG,
31
- GEMINI_FLASH_POOL,
32
- GEMINI_PRO_POOL,
33
- GEMINI_REFRESH_FLAG,
34
- GEMINI_SESSION_REFRESH_FLAG,
35
- QOS_PATH,
36
26
  TFX_PREFLIGHT_CACHE_PATH,
37
27
  TFX_PREFLIGHT_CACHE_STALE_MS,
38
28
  } from "./constants.mjs";
39
29
  import { buildContextUsageView } from "./context-monitor.mjs";
40
- import { getMissionBoardState } from "./mission-board.mjs";
41
- // Claude provider
42
30
  import {
43
31
  fetchClaudeUsage,
44
- readClaudeContextSnapshot,
45
32
  readClaudeUsageSnapshot,
46
33
  scheduleClaudeUsageRefresh,
47
34
  } from "./providers/claude.mjs";
48
- // Codex provider
49
35
  import {
50
36
  getCodexEmail,
37
+ hasBrokerCodexAccounts,
51
38
  readCodexRateLimitSnapshot,
52
39
  refreshCodexRateLimitsCache,
53
40
  scheduleCodexRateLimitRefresh,
54
41
  } from "./providers/codex.mjs";
55
- import { readCtoStatus } from "./providers/cto.mjs";
56
- // Gemini provider
57
42
  import {
58
- buildGeminiAuthContext,
59
- deriveGeminiFamilyBucket,
60
- fetchGeminiQuota,
61
43
  getAntigravityAccountLabel,
62
44
  getAntigravityCurrentModel,
63
45
  getAntigravityModelAbbrev,
64
- getAntigravityModelFamily,
65
46
  getGeminiEmail,
66
- readGeminiQuotaSnapshot,
67
- readGeminiSessionSnapshot,
68
- refreshGeminiSessionCache,
69
- scheduleGeminiQuotaRefresh,
70
- scheduleGeminiSessionRefresh,
71
47
  } from "./providers/gemini.mjs";
72
- // Renderers
73
48
  import {
74
- formatTokenSummary,
75
49
  getClaudeRows,
76
50
  getMicroLine,
77
51
  getProviderRow,
78
52
  getTeamRow,
79
- readLatestBenchmarkDiff,
80
53
  renderAlignedRows,
81
- renderMissionBoard,
82
54
  } from "./renderers.mjs";
83
55
  import { selectTier } from "./terminal.mjs";
84
- import {
85
- getCliArgValue,
86
- getProviderAccountId,
87
- readJson,
88
- readStdinJson,
89
- stripAnsi,
90
- } from "./utils.mjs";
56
+ import { readJson, readStdinJson } from "./utils.mjs";
91
57
 
92
- // ============================================================================
93
- // 메인
94
- // ============================================================================
95
58
  async function main() {
96
- // 백그라운드 Claude 사용량 리프레시
97
59
  if (process.argv.includes(CLAUDE_REFRESH_FLAG)) {
98
60
  await fetchClaudeUsage(true);
99
61
  return;
100
62
  }
101
-
102
63
  if (process.argv.includes(CODEX_REFRESH_FLAG)) {
103
64
  await refreshCodexRateLimitsCache();
104
65
  return;
105
66
  }
106
67
 
107
- if (process.argv.includes(GEMINI_SESSION_REFRESH_FLAG)) {
108
- refreshGeminiSessionCache();
109
- return;
110
- }
111
-
112
- // 백그라운드 Gemini 쿼터 리프레시 전용 실행 모드
113
- if (process.argv.includes(GEMINI_REFRESH_FLAG)) {
114
- const accountId = getCliArgValue("--account") || "gemini-main";
115
- const authContext = buildGeminiAuthContext(accountId);
116
- await fetchGeminiQuota(accountId, { authContext, forceRefresh: true });
117
- return;
118
- }
119
-
120
- // 메인 HUD 경로: 즉시 렌더 우선
121
68
  const stdinPromise = readStdinJson();
122
-
123
- const qosProfile = readJson(QOS_PATH, { providers: {} });
124
69
  const preflightCache = readJson(TFX_PREFLIGHT_CACHE_PATH, null);
125
70
  const preflightTimestamp = Number(preflightCache?.timestamp);
126
71
  const preflightFresh =
127
72
  Number.isFinite(preflightTimestamp) &&
128
73
  Date.now() - preflightTimestamp <= TFX_PREFLIGHT_CACHE_STALE_MS;
129
- // machine profile 의 TFX_DISABLE_* 정책. 차단된 CLI 는 행 자체를 그리지 않는다.
130
- // antigravity 가 꺼지면 antigravityReady 를 강제로 내려서 기존 슬롯 로직이 그대로
131
- // gemini 폴백 경로를 타게 한다.
132
74
  const { showCodex, antigravityAllowed } = resolveHudCliVisibility();
133
75
  const antigravityReady =
134
76
  antigravityAllowed &&
@@ -137,236 +79,112 @@ async function main() {
137
79
  const accountsConfig = readJson(ACCOUNTS_CONFIG_PATH, { providers: {} });
138
80
  const accountsState = readJson(ACCOUNTS_STATE_PATH, { providers: {} });
139
81
  const claudeUsageSnapshot = readClaudeUsageSnapshot();
140
- const contextSnapshot = readClaudeContextSnapshot();
141
- if (claudeUsageSnapshot.shouldRefresh) {
82
+ const codexSnapshot = readCodexRateLimitSnapshot();
83
+ // 설정이 없는 홈에서는 갱신 프로세스를 시작하지 않는다.
84
+ if (
85
+ claudeUsageSnapshot.shouldRefresh &&
86
+ existsSync(join(homedir(), ".claude"))
87
+ ) {
142
88
  scheduleClaudeUsageRefresh();
143
89
  }
144
- const geminiAccountId = getProviderAccountId(
145
- "gemini",
146
- accountsConfig,
147
- accountsState,
148
- );
149
- const codexSnapshot = readCodexRateLimitSnapshot();
150
- const geminiSessionSnapshot = readGeminiSessionSnapshot();
151
- const geminiAuthContext = buildGeminiAuthContext(geminiAccountId);
152
- const geminiQuotaSnapshot = readGeminiQuotaSnapshot(
153
- geminiAccountId,
154
- geminiAuthContext,
155
- );
156
- if (codexSnapshot.shouldRefresh) {
90
+ if (
91
+ showCodex &&
92
+ codexSnapshot.shouldRefresh &&
93
+ (existsSync(join(homedir(), ".codex")) || hasBrokerCodexAccounts())
94
+ ) {
157
95
  scheduleCodexRateLimitRefresh();
158
96
  }
159
- if (geminiSessionSnapshot.shouldRefresh) {
160
- scheduleGeminiSessionRefresh();
161
- }
162
- if (geminiQuotaSnapshot.shouldRefresh) {
163
- scheduleGeminiQuotaRefresh(geminiAccountId);
164
- }
165
97
 
166
- // 실측 데이터 추출
167
98
  const stdin = await stdinPromise;
168
- const contextView = buildContextUsageView(stdin, contextSnapshot);
99
+ const contextView = buildContextUsageView(stdin);
169
100
  const claudeUsage = claudeUsageSnapshot.data
170
101
  ? { ...claudeUsageSnapshot.data, stale: claudeUsageSnapshot.isStale }
171
- : claudeUsageSnapshot.data;
172
- const codexEmail = getCodexEmail();
173
- const geminiEmail = getGeminiEmail();
174
- const antigravityAccountLabel = getAntigravityAccountLabel();
102
+ : null;
175
103
  const codexBuckets = codexSnapshot.buckets;
176
- const geminiSession = geminiSessionSnapshot.session;
177
- const geminiQuota = geminiQuotaSnapshot.quota;
178
- const missionBoardState = await getMissionBoardState();
179
-
180
- // Gemini: 3풀 버킷 추출 (Pro/Flash/Lite — 각 풀 내 모델들은 쿼터 공유)
181
- const geminiModel = geminiSession?.model || "gemini-3-flash-preview";
182
- const geminiBuckets = geminiQuota?.buckets || [];
183
- const geminiBucket =
184
- geminiBuckets.find((b) => b.modelId === geminiModel) ||
185
- geminiBuckets.find((b) => b.modelId === "gemini-3-flash-preview") ||
186
- null;
187
- const geminiProBucket =
188
- geminiBuckets.find((b) => GEMINI_PRO_POOL.has(b.modelId)) || null;
189
- const geminiFlashBucket =
190
- geminiBuckets.find((b) => GEMINI_FLASH_POOL.has(b.modelId)) || null;
191
- const geminiLiteBucket =
192
- geminiBuckets.find((b) => b.modelId?.includes("flash-lite")) || null;
193
-
194
- // Antigravity 현재 장착 모델 + family 통합 bucket (slot1 = 장착 모델 약어, slot2 = Gn family)
195
- // slot1 quota: 현재 모델이 Gemini family면 family bucket, 아니면(Claude/GPT-OSS) placeholder.
196
- const antigravityModel = getAntigravityCurrentModel();
197
- const antigravityAbbrev = getAntigravityModelAbbrev(antigravityModel);
198
- const antigravityModelFamily = getAntigravityModelFamily(antigravityModel);
199
- const antigravityFamilyBucket = deriveGeminiFamilyBucket(geminiBuckets);
200
- const antigravitySlot1Bucket =
201
- antigravityModelFamily === "gemini" ? antigravityFamilyBucket : null;
202
-
203
- // 인디케이터 인식 tier 선택 (stdin + Claude 사용량 기반)
204
- const CURRENT_TIER = selectTier(stdin, claudeUsageSnapshot.data);
205
-
206
- // antigravity/gemini 슬롯 표시 판정. nano 와 나머지 tier 가 같은 판정을 쓴다.
104
+ const currentTier = selectTier();
105
+ const geminiEmail = getGeminiEmail();
207
106
  const showGeminiRow = shouldRenderGeminiFallbackRow({
208
107
  antigravityAllowed,
209
108
  antigravityReady,
210
109
  geminiEmail,
211
- geminiBucket,
212
- geminiSession,
213
110
  });
214
111
 
215
- // nano tier: 1줄 모드 (극소 폭 또는 알림 배너 대응)
216
- if (CURRENT_TIER === "nano") {
112
+ if (currentTier === "nano") {
217
113
  const microLine = getMicroLine(
218
114
  contextView,
219
115
  claudeUsage,
220
116
  codexBuckets,
221
- antigravityReady ? null : geminiSession,
222
- antigravityReady ? null : geminiBucket,
223
- antigravityReady ? "a" : "g",
117
+ antigravityReady,
224
118
  { showCodex, showGemini: showGeminiRow },
225
119
  );
226
120
  process.stdout.write(`\x1b[0m${microLine}\n`);
227
121
  return;
228
122
  }
229
123
 
230
- const codexQuotaData = codexBuckets
231
- ? { type: "codex", buckets: codexBuckets }
232
- : null;
233
- const geminiQuotaData = {
234
- type: "gemini",
235
- quotaBucket: geminiBucket,
236
- pools: {
237
- pro: geminiProBucket,
238
- flash: geminiFlashBucket,
239
- lite: geminiLiteBucket,
240
- },
241
- session: geminiSession,
242
- };
243
-
244
- const rows = [
245
- ...(isClaudeBandActive(stdin?.session_id)
246
- ? []
247
- : getClaudeRows(CURRENT_TIER, contextView, claudeUsage)),
248
- ];
249
-
250
- // 정책으로 생략된 행이 있으면 뒤따르는 행의 인덱스가 밀린다. dim 래핑이
251
- // 하드코딩 인덱스를 쓰지 않도록 실제 위치를 기록해 둔다.
124
+ const rows = isClaudeBandActive(stdin?.session_id)
125
+ ? []
126
+ : getClaudeRows(currentTier, contextView, claudeUsage);
252
127
  let codexRowIndex = -1;
253
128
  if (showCodex) {
254
129
  codexRowIndex = rows.length;
255
130
  rows.push(
256
131
  getProviderRow(
257
- CURRENT_TIER,
132
+ currentTier,
258
133
  "codex",
259
134
  "x",
260
135
  codexWhite,
261
- qosProfile,
262
136
  accountsConfig,
263
137
  accountsState,
264
- codexQuotaData,
265
- codexEmail,
266
- null,
138
+ codexBuckets ? { type: "codex", buckets: codexBuckets } : null,
139
+ getCodexEmail(),
267
140
  ),
268
141
  );
269
142
  }
270
-
271
143
  let geminiRowIndex = -1;
272
144
  if (showGeminiRow) {
273
145
  geminiRowIndex = rows.length;
274
146
  rows.push(
275
147
  getProviderRow(
276
- CURRENT_TIER,
148
+ currentTier,
277
149
  antigravityReady ? "antigravity" : "gemini",
278
150
  antigravityReady ? "a" : "g",
279
151
  geminiBlue,
280
- qosProfile,
281
152
  accountsConfig,
282
153
  accountsState,
283
154
  antigravityReady
284
155
  ? {
285
- type: "antigravity",
286
- pools: {
287
- current: antigravitySlot1Bucket,
288
- gemini_family: antigravityFamilyBucket,
289
- },
290
- currentAbbrev: antigravityAbbrev,
291
- currentModel: antigravityModel,
292
- currentFamily: antigravityModelFamily,
156
+ currentAbbrev: getAntigravityModelAbbrev(
157
+ getAntigravityCurrentModel(),
158
+ ),
293
159
  }
294
- : geminiQuotaData,
295
- antigravityReady ? antigravityAccountLabel : geminiEmail,
296
- null,
160
+ : null,
161
+ antigravityReady ? getAntigravityAccountLabel() : geminiEmail,
297
162
  ),
298
163
  );
299
164
  }
300
-
301
- const ctoStatus = readCtoStatus();
302
- if (ctoStatus) {
303
- const rightTag =
304
- CURRENT_TIER === "nano" || CURRENT_TIER === "micro"
305
- ? ""
306
- : stripAnsi(ctoStatus.rightTag || "").trim();
307
- rows.push({
308
- prefix: `${bold(claudeOrange("^"))}:`,
309
- left: `${dim("cto:")}${stripAnsi(ctoStatus.line)}`,
310
- right: rightTag ? dim(rightTag) : "",
311
- });
312
- }
313
-
314
- // tfx-multi 활성 시 팀 상태 행 추가 (v2.2)
315
- const teamRow = getTeamRow(CURRENT_TIER);
165
+ const teamRow = getTeamRow(currentTier);
316
166
  if (teamRow) rows.push(teamRow);
317
167
 
318
- const missionBoard = renderMissionBoard(missionBoardState);
319
- if (missionBoard) {
320
- rows.push({
321
- prefix: bold(claudeOrange("\u25B2")),
322
- left: missionBoard,
323
- right: "",
324
- });
325
- }
326
-
327
- // 최근 벤치마크 diff → 토큰 요약 행 추가
328
- const latestDiff = readLatestBenchmarkDiff();
329
- if (latestDiff) {
330
- const summary = formatTokenSummary(latestDiff);
331
- if (summary) {
332
- rows.push({ prefix: `${dim("$")}:`, left: summary, right: "" });
333
- }
334
- }
335
-
336
- // 비활성 프로바이더 dim 처리: 데이터 없으면 전체 줄 dim
337
- const codexActive = codexBuckets != null;
338
- const geminiActive =
339
- (geminiSession?.total || 0) > 0 ||
340
- geminiBucket != null ||
341
- geminiProBucket != null ||
342
- geminiFlashBucket != null;
343
-
344
168
  const outputLines = renderAlignedRows(rows);
345
-
346
- // 비활성 줄 dim 래핑. 정책으로 생략된 행은 인덱스가 -1 이라 건너뛴다.
347
- if (!codexActive && outputLines[codexRowIndex] != null) {
169
+ if (!codexBuckets && outputLines[codexRowIndex] != null) {
348
170
  outputLines[codexRowIndex] = `${DIM}${outputLines[codexRowIndex]}${RESET}`;
349
171
  }
350
- if (!geminiActive && outputLines[geminiRowIndex] != null) {
351
- outputLines[geminiRowIndex] =
352
- `${DIM}${outputLines[geminiRowIndex]}${RESET}`;
172
+ if (!antigravityReady && outputLines[geminiRowIndex] != null) {
173
+ outputLines[geminiRowIndex] = dim(outputLines[geminiRowIndex]);
353
174
  }
354
-
355
- // 선행 개행: 알림 배너(노란 글씨)가 빈 첫 줄에 오도록 → HUD 내용 보호
356
- const contextPercent = contextView.percent;
357
- const leadingBreaks = contextPercent >= 85 ? "\n\n" : "\n";
358
- // 줄별 RESET: Claude Code TUI 스타일 간섭 방지 (색상 밝기 버그 수정)
359
- const resetedLines = outputLines.map((line) => `\x1b[0m${line}`);
360
- process.stdout.write(`${leadingBreaks}${resetedLines.join("\n")}\n`);
175
+ // 알림 배너와 TUI 스타일이 HUD 내용에 겹치지 않도록 한다.
176
+ const leadingBreaks = contextView.percent >= 85 ? "\n\n" : "\n";
177
+ const resetLines = outputLines.map((line) => `\x1b[0m${line}`);
178
+ process.stdout.write(`${leadingBreaks}${resetLines.join("\n")}\n`);
361
179
  }
362
180
 
363
181
  main().catch(() => {
364
182
  process.stdout.write(
365
- `\x1b[0m${bold(claudeOrange("c"))}: ${dim("5h:")}${green("0%")} ${dim("(n/a)")} ${dim("1w:")}${green("0%")} ${dim("(n/a)")} ${dim("|")} ${dim("ctx:")}${green("0%")}\n`,
183
+ `\x1b[0m${bold(claudeOrange("c"))}: ${dim("5h:--% (n/a) 1w:--% (n/a) | ctx:--%")}\n`,
366
184
  );
367
185
  });
368
186
 
369
- // 프롬프트 위 band 가 같은 정보를 그리는 세션에서는 Claude 행을 중복해 그리지 않는다.
187
+ // 프롬프트 위 band가 같은 정보를 그리는 세션에서는 Claude 행을 생략한다.
370
188
  function isClaudeBandActive(sessionId) {
371
189
  if (!sessionId) return false;
372
190
  try {
@@ -25,7 +25,6 @@ import {
25
25
  SEVEN_DAY_MS,
26
26
  SPAWN_LOCK_TTL_MS,
27
27
  } from "../constants.mjs";
28
- import { readContextMonitorSnapshot } from "../context-monitor.mjs";
29
28
  import {
30
29
  advanceToNextCycle,
31
30
  clampPercent,
@@ -116,17 +115,14 @@ function normalizeClaudeCredentials(data, source, supportsUsageApi = true) {
116
115
  };
117
116
  }
118
117
 
119
- // Claude Code 2.1.x secure-storage 규칙(바이너리 실측, 2026-09-21):
120
- // - CLAUDE_SECURESTORAGE_CONFIG_DIR 가 정의돼 있으면 그 값이 스코프다. 빈 문자열은 기본 디렉터리를
121
- // 뜻해 접미사를 붙이지 않고, 값이 있으면 NFC 로 정규화한 뒤 해시한다.
122
- // - 아니면 CLAUDE_CONFIG_DIR 원문(경로 확장·정규화 없이)을 sha256 해 앞 8자리를 붙인다.
123
- // OMC 격리 세션의 `Claude Code-credentials-<hash>` 항목이 이 규칙으로 만들어진다.
118
+ // 격리된 설정 디렉터리는 기본 Keychain 항목과 다른 서비스 이름을 사용한다.
124
119
  export function getKeychainServiceName(env = process.env) {
125
120
  const secureDir = env.CLAUDE_SECURESTORAGE_CONFIG_DIR;
126
121
  let scope = "";
127
122
  if (secureDir !== undefined) {
128
123
  scope = secureDir ? secureDir.normalize("NFC") : "";
129
124
  } else {
125
+ // CLAUDE_CONFIG_DIR은 정규화하지 않고 빈 문자열은 기본값으로 본다.
130
126
  scope = env.CLAUDE_CONFIG_DIR || "";
131
127
  }
132
128
  if (!scope) return CLAUDE_KEYCHAIN_SERVICE;
@@ -168,9 +164,7 @@ function readClaudeKeychainRaw(
168
164
  }
169
165
  }
170
166
 
171
- // Keychain 항목은 (service, account) 복합 키다. Claude Code 는 항상 `-a <account>` 로 쓰고 읽으므로
172
- // 같은 키만 본다. `-a` 없는 조회는 "계정 없는 항목"이 아니라 같은 service 의 임의 계정 항목을 돌려주고,
173
- // `add-generic-password` 는 `-a` 가 필수라 그런 항목에는 되쓸 수도 없다(2026-09-21 실측, exit 2).
167
+ // service와 account를 함께 지정해야 다른 계정의 항목을 읽지 않는다.
174
168
  export function readClaudeKeychainEntry({
175
169
  execFileSyncFn = execFileSync,
176
170
  env = process.env,
@@ -372,9 +366,7 @@ function writeClaudeFileCredentials(
372
366
  writeCredentialFile(mergeClaudeCredentials(creds, data), filePath);
373
367
  }
374
368
 
375
- // 되쓰기는 자격증명을 읽어 온 저장소에만 한다. Keychain 출처를 파일에 쓰거나 파일 출처를 Keychain 에 쓰면
376
- // 계정이 다른 두 저장소가 섞이고(2026-09 실측: 파일=max 계정 만료분, Keychain=pro 계정 현재 로그인),
377
- // Claude Code 가 실제로 쓰는 Keychain 항목을 옛 계정 토큰으로 덮어쓴다.
369
+ // 자격증명을 읽어 온 저장소에만 써서 다른 계정의 토큰을 덮어쓰지 않는다.
378
370
  export function writeBackClaudeCredentials(
379
371
  creds,
380
372
  {
@@ -733,7 +725,3 @@ export function scheduleClaudeUsageRefresh() {
733
725
  });
734
726
  }
735
727
  }
736
-
737
- export function readClaudeContextSnapshot() {
738
- return readContextMonitorSnapshot();
739
- }