pinglet-cli 0.6.2 → 0.8.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.
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.runStatusline = runStatusline;
37
+ exports.writeStatusline = writeStatusline;
37
38
  const fs = __importStar(require("fs"));
38
39
  const config_1 = require("../config");
39
40
  const cache_1 = require("../cache");
@@ -42,26 +43,11 @@ const picker_1 = require("../picker");
42
43
  const queue_1 = require("../queue");
43
44
  const claude_1 = require("../adapters/claude");
44
45
  const runtime_1 = require("../runtime");
45
- /**
46
- * online 캐시 유효 기간 — refresh(≈5분) 2주기. 서버의 온라인 판정 창
47
- * (ONLINE_WINDOW_MS 10분)과 정렬해, 서버가 이미 오프라인으로 세는 시점에
48
- * 클라이언트가 옛 숫자를 "지금"이라고 보여주지 않게 한다.
49
- */
50
- const ONLINE_STALE_MS = 10 * 60_000;
51
- /** 신선한 online 캐시가 있을 때, 나를 제외한 켜진 터미널 수 (없거나 혼자면 null). */
52
- function onlineOthers(now) {
53
- const cache = (0, cache_1.loadOnline)();
54
- if (!cache)
55
- return null;
56
- const t = Date.parse(cache.updatedAt);
57
- if (Number.isNaN(t) || now - t > ONLINE_STALE_MS)
58
- return null;
59
- const others = cache.onlineInstallations - 1; // 이 기기의 heartbeat 제외
60
- return others >= 1 ? others : null;
61
- }
46
+ const hud_1 = require("../hud");
62
47
  function readPayload() {
63
48
  try {
64
- return JSON.parse(fs.readFileSync(0, "utf8"));
49
+ const parsed = JSON.parse(fs.readFileSync(0, "utf8"));
50
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
65
51
  }
66
52
  catch {
67
53
  return {};
@@ -133,20 +119,24 @@ function rotateSpinner(state, now) {
133
119
  messageId: message.id,
134
120
  text: message.text,
135
121
  author: message.author,
122
+ url: message.url ?? null,
136
123
  shownAt: now,
137
124
  visibleMs: 0,
138
125
  };
139
126
  }
140
127
  /**
141
- * Claude Code statusLine hook 진입점.
128
+ * Claude Code statusLine hook 진입점 (로컬 잠금 안, 동기).
142
129
  * - spinner 회전(pool=1): ROTATE_MS마다 다음 메시지를 armed (설정 핫리로드로 즉시 반영)
143
130
  * - 노출 측정: stdin payload의 cost 델타로 armed 메시지의 실제 표시 시간을 누적
144
- * - statusline 표시: 지금 함께 켜져 있는 터미널 수
145
131
  * - flush/refresh 백그라운드 트리거 (Claude 사용 중 항상 호출되므로 이 경로에서)
132
+ * 출력할 줄과 payload를 돌려주면 호출부가 잠금을 놓고 `writeStatusline`으로 HUD까지 그린다.
146
133
  */
147
134
  function runStatusline() {
148
- if (!fs.existsSync(config_1.CONFIG_PATH) || !(0, config_1.loadConfig)().adapters.claude)
149
- return;
135
+ if (!fs.existsSync(config_1.CONFIG_PATH))
136
+ return undefined;
137
+ const config = (0, config_1.loadConfig)();
138
+ if (!config.adapters.claude)
139
+ return undefined;
150
140
  const payload = readPayload();
151
141
  const state = (0, cache_1.loadState)();
152
142
  const now = Date.now();
@@ -165,6 +155,24 @@ function runStatusline() {
165
155
  state.lastTickAt = now;
166
156
  (0, cache_1.saveState)(state);
167
157
  }
168
- const others = onlineOthers(now);
169
- process.stdout.write(others !== null ? (0, render_1.formatOnlineNow)(others) : "");
158
+ const lines = [];
159
+ // spinner와 같은 항목을 링크로 보여준다. HUD 세그먼트 폭 계산(hud/width)은
160
+ // OSC 8을 모르므로 HUD 안이 아니라 별도 줄로 둔다.
161
+ if (state.current?.url)
162
+ lines.push((0, render_1.formatNewsLink)(state.current.text, state.current.url));
163
+ return { payload, lines, hud: (0, hud_1.loadHudConfig)(config) };
164
+ }
165
+ /**
166
+ * 잠금 밖에서 HUD(모델·컨텍스트·사용량·활동)를 그려 뉴스 링크 줄 아래에 출력한다.
167
+ * HUD는 실패해도 위 줄에 영향을 주면 안 된다.
168
+ */
169
+ async function writeStatusline(tick, now = Date.now(), write = (text) => { process.stdout.write(text); }) {
170
+ const lines = [...tick.lines];
171
+ try {
172
+ lines.push(...(await (0, hud_1.renderHudLines)(tick.hud, tick.payload, now)));
173
+ }
174
+ catch {
175
+ // 손상된 transcript·git 오류 등 — 이번 tick은 HUD 없이 그린다.
176
+ }
177
+ write(lines.join("\n"));
170
178
  }
package/dist/config.js CHANGED
@@ -93,6 +93,7 @@ function loadConfig() {
93
93
  const object = (v) => !!v && typeof v === "object" && !Array.isArray(v);
94
94
  if (typeof existing.apiBaseUrl !== "string" || typeof existing.createdAt !== "string" ||
95
95
  (existing.userToken !== undefined && typeof existing.userToken !== "string") ||
96
+ (existing.hud !== undefined && !object(existing.hud)) ||
96
97
  (existing.installations !== undefined && !object(existing.installations)) ||
97
98
  (existing.adapters !== undefined && !object(existing.adapters))) {
98
99
  throw new Error(`Invalid ${exports.CONFIG_PATH}; original file preserved.`);
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.quotaBar = exports.contextBar = exports.critical = exports.warning = exports.label = exports.gitBranch = exports.git = exports.project = exports.model = exports.dim = exports.magenta = exports.cyan = exports.red = exports.yellow = exports.green = exports.RESET = void 0;
4
+ exports.contextColor = contextColor;
5
+ exports.quotaColor = quotaColor;
6
+ exports.RESET = "\x1b[0m";
7
+ const DIM = "\x1b[2m";
8
+ const ANSI_BY_NAME = {
9
+ dim: DIM,
10
+ red: "\x1b[31m",
11
+ green: "\x1b[32m",
12
+ yellow: "\x1b[33m",
13
+ magenta: "\x1b[35m",
14
+ cyan: "\x1b[36m",
15
+ brightBlue: "\x1b[94m",
16
+ brightMagenta: "\x1b[95m",
17
+ };
18
+ function resolveAnsi(value, fallback) {
19
+ if (value === undefined || value === null)
20
+ return fallback;
21
+ if (typeof value === "number")
22
+ return `\x1b[38;5;${value}m`;
23
+ if (typeof value === "string" && value.startsWith("#") && value.length === 7) {
24
+ const r = parseInt(value.slice(1, 3), 16);
25
+ const g = parseInt(value.slice(3, 5), 16);
26
+ const b = parseInt(value.slice(5, 7), 16);
27
+ return `\x1b[38;2;${r};${g};${b}m`;
28
+ }
29
+ return ANSI_BY_NAME[value] ?? fallback;
30
+ }
31
+ const colorize = (text, ansi) => `${ansi}${text}${exports.RESET}`;
32
+ const green = (t) => colorize(t, ANSI_BY_NAME.green);
33
+ exports.green = green;
34
+ const yellow = (t) => colorize(t, ANSI_BY_NAME.yellow);
35
+ exports.yellow = yellow;
36
+ const red = (t) => colorize(t, ANSI_BY_NAME.red);
37
+ exports.red = red;
38
+ const cyan = (t) => colorize(t, ANSI_BY_NAME.cyan);
39
+ exports.cyan = cyan;
40
+ const magenta = (t) => colorize(t, ANSI_BY_NAME.magenta);
41
+ exports.magenta = magenta;
42
+ const dim = (t) => colorize(t, DIM);
43
+ exports.dim = dim;
44
+ const model = (t, c) => colorize(t, resolveAnsi(c.model, ANSI_BY_NAME.cyan));
45
+ exports.model = model;
46
+ const project = (t, c) => colorize(t, resolveAnsi(c.project, ANSI_BY_NAME.yellow));
47
+ exports.project = project;
48
+ const git = (t, c) => colorize(t, resolveAnsi(c.git, ANSI_BY_NAME.magenta));
49
+ exports.git = git;
50
+ const gitBranch = (t, c) => colorize(t, resolveAnsi(c.gitBranch, ANSI_BY_NAME.cyan));
51
+ exports.gitBranch = gitBranch;
52
+ const label = (t, c) => colorize(t, resolveAnsi(c.label, DIM));
53
+ exports.label = label;
54
+ const warning = (t, c) => colorize(t, resolveAnsi(c.warning, ANSI_BY_NAME.yellow));
55
+ exports.warning = warning;
56
+ const critical = (t, c) => colorize(t, resolveAnsi(c.critical, ANSI_BY_NAME.red));
57
+ exports.critical = critical;
58
+ function contextColor(percent, c, thresholds) {
59
+ if (percent >= thresholds.critical)
60
+ return resolveAnsi(c.critical, ANSI_BY_NAME.red);
61
+ if (percent >= thresholds.warning)
62
+ return resolveAnsi(c.warning, ANSI_BY_NAME.yellow);
63
+ return resolveAnsi(c.context, ANSI_BY_NAME.green);
64
+ }
65
+ function quotaColor(percent, c) {
66
+ if (percent >= 90)
67
+ return resolveAnsi(c.critical, ANSI_BY_NAME.red);
68
+ if (percent >= 75)
69
+ return resolveAnsi(c.usageWarning, ANSI_BY_NAME.brightMagenta);
70
+ return resolveAnsi(c.usage, ANSI_BY_NAME.brightBlue);
71
+ }
72
+ function bar(percent, width, ansi, c) {
73
+ const safeWidth = Math.max(0, Math.round(width));
74
+ const safePercent = Number.isFinite(percent) ? Math.min(100, Math.max(0, percent)) : 0;
75
+ const filled = Math.round((safePercent / 100) * safeWidth);
76
+ return `${ansi}${c.barFilled.repeat(filled)}${DIM}${c.barEmpty.repeat(safeWidth - filled)}${exports.RESET}`;
77
+ }
78
+ const contextBar = (percent, width, c, thresholds) => bar(percent, width, contextColor(percent, c, thresholds), c);
79
+ exports.contextBar = contextBar;
80
+ const quotaBar = (percent, width, c) => bar(percent, width, quotaColor(percent, c), c);
81
+ exports.quotaBar = quotaBar;
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ /**
3
+ * HUD 설정 — claude-hud(jarrodwatts/claude-hud)와 같은 키 체계를 쓴다.
4
+ * `~/.pinglet/config.json`의 `hud` 섹션에 저장되며, 값이 없거나 잘못되면
5
+ * 기본값으로 되돌린다 (statusline 렌더 경로에서 예외를 던지지 않기 위해).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.PRESETS = exports.TOGGLE_KEYS = exports.DEFAULT_HUD_CONFIG = exports.DEFAULT_MERGE_GROUPS = exports.KNOWN_FIRST_LINE_SEGMENTS = exports.KNOWN_ELEMENTS = exports.DEFAULT_ELEMENT_ORDER = void 0;
9
+ exports.setToggle = setToggle;
10
+ exports.applyPreset = applyPreset;
11
+ exports.normalizeHudConfig = normalizeHudConfig;
12
+ exports.DEFAULT_ELEMENT_ORDER = [
13
+ "project",
14
+ "context",
15
+ "usage",
16
+ "promptCache",
17
+ "memory",
18
+ "tools",
19
+ "skills",
20
+ "agents",
21
+ "todos",
22
+ ];
23
+ exports.KNOWN_ELEMENTS = new Set(exports.DEFAULT_ELEMENT_ORDER);
24
+ exports.KNOWN_FIRST_LINE_SEGMENTS = new Set([
25
+ "model",
26
+ "project",
27
+ "advisor",
28
+ "sessionName",
29
+ "duration",
30
+ "cost",
31
+ "speed",
32
+ ]);
33
+ /** 컨텍스트·사용량·캐시는 한 줄로 묶는다 (폭이 모자라면 렌더러가 다시 나눈다). */
34
+ exports.DEFAULT_MERGE_GROUPS = [["context", "usage", "promptCache"]];
35
+ /** 설정 파일에 없을 때 쓰는 기본값 — claude-hud의 "full" 프리셋과 같다. */
36
+ exports.DEFAULT_HUD_CONFIG = {
37
+ enabled: true,
38
+ lineLayout: "expanded",
39
+ showSeparators: false,
40
+ pathLevels: 1,
41
+ maxWidth: null,
42
+ forceMaxWidth: false,
43
+ elementOrder: [...exports.DEFAULT_ELEMENT_ORDER],
44
+ projectLineOrder: [],
45
+ gitStatus: {
46
+ enabled: true,
47
+ showDirty: true,
48
+ showAheadBehind: true,
49
+ showFileStats: true,
50
+ branchOverflow: "truncate",
51
+ pushWarningThreshold: 0,
52
+ pushCriticalThreshold: 0,
53
+ },
54
+ display: {
55
+ showModel: true,
56
+ modelFormat: "full",
57
+ showProject: true,
58
+ showContextBar: true,
59
+ contextValue: "percent",
60
+ showTokenBreakdown: true,
61
+ showUsage: true,
62
+ usageValue: "percent",
63
+ usageBarEnabled: true,
64
+ showResetLabel: true,
65
+ usageCompact: false,
66
+ showModelScopedUsage: true,
67
+ timeFormat: "relative",
68
+ showTools: true,
69
+ toolNameMaxLength: 0,
70
+ toolsMaxVisible: 4,
71
+ showSkills: true,
72
+ showAgents: true,
73
+ showTodos: true,
74
+ showSessionName: true,
75
+ showDuration: true,
76
+ showCost: true,
77
+ showSpeed: true,
78
+ showEffortLevel: true,
79
+ effortFormat: "full",
80
+ showSessionTokens: true,
81
+ showCompactions: true,
82
+ showMemoryUsage: true,
83
+ showPromptCache: true,
84
+ promptCacheTtlSeconds: 300,
85
+ showCacheHitRate: true,
86
+ showAdvisor: true,
87
+ advisorOverride: "",
88
+ mergeGroups: exports.DEFAULT_MERGE_GROUPS.map((group) => [...group]),
89
+ contextWarningThreshold: 70,
90
+ contextCriticalThreshold: 85,
91
+ usageThreshold: 0,
92
+ sevenDayThreshold: 80,
93
+ },
94
+ colors: {
95
+ context: "green",
96
+ usage: "brightBlue",
97
+ warning: "yellow",
98
+ usageWarning: "brightMagenta",
99
+ critical: "red",
100
+ model: "cyan",
101
+ project: "yellow",
102
+ git: "magenta",
103
+ gitBranch: "cyan",
104
+ label: "dim",
105
+ barFilled: "█",
106
+ barEmpty: "░",
107
+ },
108
+ };
109
+ /** `pinglet hud --show/--hide`에서 받는 토글 이름 → 설정 경로. */
110
+ exports.TOGGLE_KEYS = {
111
+ model: "display.showModel",
112
+ project: "display.showProject",
113
+ context: "display.showContextBar",
114
+ "context-bar": "display.showContextBar",
115
+ usage: "display.showUsage",
116
+ "model-scoped-usage": "display.showModelScopedUsage",
117
+ tools: "display.showTools",
118
+ skills: "display.showSkills",
119
+ agents: "display.showAgents",
120
+ todos: "display.showTodos",
121
+ memory: "display.showMemoryUsage",
122
+ "prompt-cache": "display.showPromptCache",
123
+ "cache-hit": "display.showCacheHitRate",
124
+ advisor: "display.showAdvisor",
125
+ "session-name": "display.showSessionName",
126
+ duration: "display.showDuration",
127
+ cost: "display.showCost",
128
+ speed: "display.showSpeed",
129
+ effort: "display.showEffortLevel",
130
+ "session-tokens": "display.showSessionTokens",
131
+ compactions: "display.showCompactions",
132
+ "token-breakdown": "display.showTokenBreakdown",
133
+ "usage-bar": "display.usageBarEnabled",
134
+ "reset-label": "display.showResetLabel",
135
+ "usage-compact": "display.usageCompact",
136
+ git: "gitStatus.enabled",
137
+ "git-dirty": "gitStatus.showDirty",
138
+ "git-ahead-behind": "gitStatus.showAheadBehind",
139
+ "git-files": "gitStatus.showFileStats",
140
+ };
141
+ /** 프리셋이 손대는 토글 — 나머지 키는 프리셋을 바꿔도 그대로 둔다. */
142
+ const PRESET_TOGGLES = [
143
+ "model", "project", "context", "usage", "model-scoped-usage", "tools", "skills", "agents", "todos",
144
+ "memory", "prompt-cache", "cache-hit", "advisor", "session-name", "duration", "cost", "speed", "effort",
145
+ "session-tokens", "compactions", "git", "git-ahead-behind", "git-files",
146
+ ];
147
+ /**
148
+ * 프리셋 — claude-hud의 setup/configure 흐름과 같은 세 단계.
149
+ * full: 모두 켜기
150
+ * essential: 활동 줄(tools/skills/agents/todos) + 모델·프로젝트·컨텍스트·git 브랜치, 정보 최소
151
+ * minimal: 모델명 + context bar만
152
+ */
153
+ exports.PRESETS = {
154
+ full: new Set(PRESET_TOGGLES),
155
+ essential: new Set([
156
+ "model", "project", "context", "usage", "model-scoped-usage", "prompt-cache", "cache-hit",
157
+ "tools", "skills", "agents", "todos", "git",
158
+ ]),
159
+ minimal: new Set(["model", "context"]),
160
+ };
161
+ function setToggle(config, name, value) {
162
+ const target = exports.TOGGLE_KEYS[name];
163
+ if (!target)
164
+ return;
165
+ const [section, key] = target.split(".");
166
+ config[section][key] = value;
167
+ }
168
+ function applyPreset(config, preset) {
169
+ const next = structuredClone(config);
170
+ const on = exports.PRESETS[preset];
171
+ for (const name of PRESET_TOGGLES)
172
+ setToggle(next, name, on.has(name));
173
+ // 프리셋은 배치까지 정하므로 병합 그룹도 기본값(컨텍스트·사용량·캐시 한 줄)으로 되돌린다.
174
+ next.display.mergeGroups = exports.DEFAULT_MERGE_GROUPS.map((group) => [...group]);
175
+ return next;
176
+ }
177
+ const isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
178
+ const bool = (v, fallback) => (typeof v === "boolean" ? v : fallback);
179
+ const oneOf = (v, allowed, fallback) => allowed.includes(v) ? v : fallback;
180
+ const int = (v, fallback, min = 0, max = 100_000) => typeof v === "number" && Number.isFinite(v) && v >= min && v <= max ? Math.trunc(v) : fallback;
181
+ function uniqueKnown(value, known) {
182
+ if (!Array.isArray(value))
183
+ return [];
184
+ const out = [];
185
+ for (const item of value) {
186
+ if (typeof item === "string" && known.has(item) && !out.includes(item))
187
+ out.push(item);
188
+ }
189
+ return out;
190
+ }
191
+ const COLOR_NAMES = [
192
+ "dim", "red", "green", "yellow", "magenta", "cyan", "brightBlue", "brightMagenta",
193
+ ];
194
+ function colorValue(value, fallback) {
195
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 255)
196
+ return value;
197
+ if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value))
198
+ return value;
199
+ if (typeof value === "string" && COLOR_NAMES.includes(value))
200
+ return value;
201
+ return fallback;
202
+ }
203
+ /** 바 문자: 제어·서식 문자를 제외한 단일 grapheme만 허용한다 (터미널 방어). */
204
+ function barChar(value, fallback) {
205
+ if (typeof value !== "string" || value.length === 0 || value.length > 4)
206
+ return fallback;
207
+ if (/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(value))
208
+ return fallback;
209
+ return Array.from(value).length === 1 ? value : fallback;
210
+ }
211
+ /** 어떤 입력이 와도 유효한 HudConfig를 돌려준다. 알 수 없는 키는 버린다. */
212
+ function normalizeHudConfig(raw) {
213
+ const d = exports.DEFAULT_HUD_CONFIG;
214
+ if (!isObject(raw))
215
+ return structuredClone(d);
216
+ const display = isObject(raw.display) ? raw.display : {};
217
+ const git = isObject(raw.gitStatus) ? raw.gitStatus : {};
218
+ const colors = isObject(raw.colors) ? raw.colors : {};
219
+ const elementOrder = uniqueKnown(raw.elementOrder, exports.KNOWN_ELEMENTS);
220
+ const mergeGroups = Array.isArray(display.mergeGroups)
221
+ ? display.mergeGroups
222
+ .map((group) => uniqueKnown(group, exports.KNOWN_ELEMENTS))
223
+ .filter((group) => group.length > 1)
224
+ : d.display.mergeGroups.map((group) => [...group]);
225
+ const warning = int(display.contextWarningThreshold, d.display.contextWarningThreshold, 0, 100);
226
+ const critical = int(display.contextCriticalThreshold, d.display.contextCriticalThreshold, 0, 100);
227
+ return {
228
+ enabled: bool(raw.enabled, d.enabled),
229
+ lineLayout: oneOf(raw.lineLayout, ["compact", "expanded"], d.lineLayout),
230
+ showSeparators: bool(raw.showSeparators, d.showSeparators),
231
+ pathLevels: oneOf(raw.pathLevels, [1, 2, 3, "full"], d.pathLevels),
232
+ maxWidth: typeof raw.maxWidth === "number" && raw.maxWidth > 0 ? Math.min(1000, Math.trunc(raw.maxWidth)) : null,
233
+ forceMaxWidth: bool(raw.forceMaxWidth, d.forceMaxWidth),
234
+ elementOrder: elementOrder.length > 0 ? elementOrder : [...d.elementOrder],
235
+ projectLineOrder: uniqueKnown(raw.projectLineOrder, exports.KNOWN_FIRST_LINE_SEGMENTS),
236
+ gitStatus: {
237
+ enabled: bool(git.enabled, d.gitStatus.enabled),
238
+ showDirty: bool(git.showDirty, d.gitStatus.showDirty),
239
+ showAheadBehind: bool(git.showAheadBehind, d.gitStatus.showAheadBehind),
240
+ showFileStats: bool(git.showFileStats, d.gitStatus.showFileStats),
241
+ branchOverflow: oneOf(git.branchOverflow, ["truncate", "wrap"], d.gitStatus.branchOverflow),
242
+ pushWarningThreshold: int(git.pushWarningThreshold, d.gitStatus.pushWarningThreshold, 0, 10_000),
243
+ pushCriticalThreshold: int(git.pushCriticalThreshold, d.gitStatus.pushCriticalThreshold, 0, 10_000),
244
+ },
245
+ display: {
246
+ showModel: bool(display.showModel, d.display.showModel),
247
+ modelFormat: oneOf(display.modelFormat, ["full", "compact", "short"], d.display.modelFormat),
248
+ showProject: bool(display.showProject, d.display.showProject),
249
+ showContextBar: bool(display.showContextBar, d.display.showContextBar),
250
+ contextValue: oneOf(display.contextValue, ["percent", "tokens", "remaining", "both"], d.display.contextValue),
251
+ showTokenBreakdown: bool(display.showTokenBreakdown, d.display.showTokenBreakdown),
252
+ showUsage: bool(display.showUsage, d.display.showUsage),
253
+ usageValue: oneOf(display.usageValue, ["percent", "remaining"], d.display.usageValue),
254
+ usageBarEnabled: bool(display.usageBarEnabled, d.display.usageBarEnabled),
255
+ showResetLabel: bool(display.showResetLabel, d.display.showResetLabel),
256
+ usageCompact: bool(display.usageCompact, d.display.usageCompact),
257
+ showModelScopedUsage: bool(display.showModelScopedUsage, d.display.showModelScopedUsage),
258
+ timeFormat: oneOf(display.timeFormat, ["relative", "absolute", "both"], d.display.timeFormat),
259
+ showTools: bool(display.showTools, d.display.showTools),
260
+ toolNameMaxLength: int(display.toolNameMaxLength, d.display.toolNameMaxLength, 0, 64),
261
+ toolsMaxVisible: int(display.toolsMaxVisible, d.display.toolsMaxVisible, 0, 20),
262
+ showSkills: bool(display.showSkills, d.display.showSkills),
263
+ showAgents: bool(display.showAgents, d.display.showAgents),
264
+ showTodos: bool(display.showTodos, d.display.showTodos),
265
+ showSessionName: bool(display.showSessionName, d.display.showSessionName),
266
+ showDuration: bool(display.showDuration, d.display.showDuration),
267
+ showCost: bool(display.showCost, d.display.showCost),
268
+ showSpeed: bool(display.showSpeed, d.display.showSpeed),
269
+ showEffortLevel: bool(display.showEffortLevel, d.display.showEffortLevel),
270
+ effortFormat: oneOf(display.effortFormat, ["full", "symbol", "text"], d.display.effortFormat),
271
+ showSessionTokens: bool(display.showSessionTokens, d.display.showSessionTokens),
272
+ showCompactions: bool(display.showCompactions, d.display.showCompactions),
273
+ showMemoryUsage: bool(display.showMemoryUsage, d.display.showMemoryUsage),
274
+ showPromptCache: bool(display.showPromptCache, d.display.showPromptCache),
275
+ promptCacheTtlSeconds: int(display.promptCacheTtlSeconds, d.display.promptCacheTtlSeconds, 1, 86_400),
276
+ showCacheHitRate: bool(display.showCacheHitRate, d.display.showCacheHitRate),
277
+ showAdvisor: bool(display.showAdvisor, d.display.showAdvisor),
278
+ advisorOverride: typeof display.advisorOverride === "string" ? display.advisorOverride.slice(0, 64) : "",
279
+ mergeGroups,
280
+ contextWarningThreshold: Math.min(warning, critical),
281
+ contextCriticalThreshold: critical,
282
+ usageThreshold: int(display.usageThreshold, d.display.usageThreshold, 0, 100),
283
+ sevenDayThreshold: int(display.sevenDayThreshold, d.display.sevenDayThreshold, 0, 100),
284
+ },
285
+ colors: {
286
+ context: colorValue(colors.context, d.colors.context),
287
+ usage: colorValue(colors.usage, d.colors.usage),
288
+ warning: colorValue(colors.warning, d.colors.warning),
289
+ usageWarning: colorValue(colors.usageWarning, d.colors.usageWarning),
290
+ critical: colorValue(colors.critical, d.colors.critical),
291
+ model: colorValue(colors.model, d.colors.model),
292
+ project: colorValue(colors.project, d.colors.project),
293
+ git: colorValue(colors.git, d.colors.git),
294
+ gitBranch: colorValue(colors.gitBranch, d.colors.gitBranch),
295
+ label: colorValue(colors.label, d.colors.label),
296
+ barFilled: barChar(colors.barFilled, d.colors.barFilled),
297
+ barEmpty: barChar(colors.barEmpty, d.colors.barEmpty),
298
+ },
299
+ };
300
+ }