diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 9c6b6ca..42a4f4a 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -55,6 +55,21 @@ "category": "developer-tools", "tags": ["skills", "tdd", "testing", "development"], "keywords": ["skills", "tdd", "testing", "development"] + }, + { + "name": "balance-hud", + "description": "Real-time HUD statusline + API balance monitoring for Claude Code — context health, tool activity, agent tracking, todo progress, git status, and DeepSeek/OpenAI/Anthropic balance tracking with low-balance warnings", + "version": "2.0.0", + "author": { + "name": "NYRO" + }, + "source": "./plugins/balance-hud", + "category": "productivity", + "tags": ["hud", "balance", "monitoring", "statusline", "deepseek", "openai", "anthropic", "cost-tracking"], + "keywords": ["hud", "statusline", "balance", "api", "monitoring", "context", "tools", "agents", "todos", "deepseek", "openai", "anthropic", "cost-tracking", "claude-code"], + "homepage": "https://github.com/BFRKQSB7/balance-hud", + "repository": "https://github.com/BFRKQSB7/balance-hud", + "license": "MIT" } ] } diff --git a/plugins/balance-hud/.claude-plugin/plugin.json b/plugins/balance-hud/.claude-plugin/plugin.json new file mode 100644 index 0000000..fef92c1 --- /dev/null +++ b/plugins/balance-hud/.claude-plugin/plugin.json @@ -0,0 +1,15 @@ +{ + "name": "balance-hud", + "description": "Real-time HUD statusline for Claude Code with API balance monitoring. Shows context health, tool activity, agent tracking, todo progress, git status, and DeepSeek/OpenAI/Anthropic balance tracking with low-balance warnings.", + "version": "2.0.0", + "keywords": ["hud", "balance", "api", "monitoring", "deepseek", "openai", "anthropic", "cost-tracking", "statusline", "context", "tools", "agents", "todos", "claude-code"], + "author": { + "name": "NYRO", + "email": "" + }, + "commands": [ + "./commands/setup.md", + "./commands/configure.md" + ], + "license": "MIT" +} diff --git a/plugins/balance-hud/LICENSE b/plugins/balance-hud/LICENSE new file mode 100644 index 0000000..887daa7 --- /dev/null +++ b/plugins/balance-hud/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2026 Jarrod Watts — claude-hud (https://github.com/jarrodwatts/claude-hud) +Copyright (c) 2026 NYRO — balance-hud modifications and additions + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/balance-hud/NOTICE.md b/plugins/balance-hud/NOTICE.md new file mode 100644 index 0000000..913b95c --- /dev/null +++ b/plugins/balance-hud/NOTICE.md @@ -0,0 +1,54 @@ +# NOTICE — Third-Party Code + +This project includes code from the following third-party open source projects. + +--- + +## claude-hud + +- **Repository**: +- **Author**: Jarrod Watts +- **License**: MIT +- **Copyright**: Copyright (c) 2026 Jarrod Watts +- **Version Used**: v0.3.0 + +### Files Adapted + +The entire HUD rendering engine in `dist/` is adapted from claude-hud. Key components: + +| Directory / File | Description | +|---|---| +| `dist/render/` | HUD rendering pipeline (layout, colors, ANSI, wrapping) | +| `dist/render/lines/` | Individual line renderers (identity, project, usage, git, agents, tools, todos) | +| `dist/render/session-line.js` | Compact single-line mode | +| `dist/config.js` | Configuration loading and validation | +| `dist/config-reader.js` | Config file discovery and merging | +| `dist/stdin.js` | stdin JSON parsing | +| `dist/transcript.js` | Session transcript JSONL parsing | +| `dist/context-cache.js` | Context percentage caching | +| `dist/i18n/` | Internationalization (en, zh-Hans, zh) | +| `dist/utils/` | Terminal, truncation, sanitize utilities | +| `dist/version.js` | Claude Code version detection | +| `dist/git.js` | Git status parsing | +| `dist/speed-tracker.js` | Response speed tracking | +| `commands/setup.md` | Plugin setup command | +| `commands/configure.md` | Interactive configuration command | +| `.claude-plugin/plugin.json` | Plugin metadata structure | +| `hooks/hooks.json` | Hook definitions | + +### Modifications + +The balance-hud project adds the following on top of claude-hud: + +- **API Balance Monitoring**: `scripts/auto_refresh.mjs`, `scripts/hud_balance.mjs`, `scripts/balance_snapshot.mjs` — DeepSeek/OpenAI/Anthropic balance polling and ANSI display +- **Balance Integration**: `dist/external-usage.js` — balance snapshot loading into HUD context +- **Balance Rendering**: `renderBalanceLine()` in `dist/render/lines/usage.js` — balance label rendering +- **Usage Decoupling**: Usage line separated from Context line; Balance + Usage placed after DeepSeek balance +- **Color Adjustments**: Context bar safe-zone changed from bright blue to bright green +- **Bug Fix**: `MAX_BALANCE_LABEL_LENGTH` 50 → 512 (ANSI escape sequences are invisible width but occupy `string.length`) + +--- + +## Full License Texts + +See [LICENSE](LICENSE) for the MIT License covering this project, which includes the original claude-hud copyright. diff --git a/plugins/balance-hud/README.md b/plugins/balance-hud/README.md new file mode 100644 index 0000000..f99e292 --- /dev/null +++ b/plugins/balance-hud/README.md @@ -0,0 +1,267 @@ +# Balance HUD v2.0.0 + +> Claude Code 全功能插件 — 实时 HUD 状态栏 + API 余额监控 +> +> **基于 [claude-hud](https://github.com/jarrodwatts/claude-hud) by [Jarrod Watts](https://github.com/jarrodwatts) (MIT) 开发** + +![](https://img.shields.io/badge/version-2.0.0-blue) +![](https://img.shields.io/badge/license-MIT-green) +![](https://img.shields.io/badge/based%20on-claude--hud%20v0.3.0-orange) + +Balance HUD 将 **claude-hud** (全功能终端 HUD 状态栏) 与 **balance-hud** (API 余额实时监控) 整合为一个独立运行的插件。 + +## 功能 + +### 🖥️ HUD 状态栏 (来自 claude-hud v0.3.0) +- **上下文健康度** — 实时上下文使用率进度条,绿/黄/红三级预警 +- **用量监控** — 5小时/7天用量限制显示,含重置倒计时 +- **工具活动追踪** — 当前运行的 Tools (Edit/Read/Grep 等),完成计数 +- **Agent 状态** — 子 Agent 运行状态、模型、描述、耗时 +- **Todo 进度** — 任务列表完成进度显示 +- **Git 状态** — 分支名、脏状态、ahead/behind 提交数 +- **会话信息** — 会话时长、配置计数 (CLAUDE.md, rules, MCP, hooks) +- **多语言** — 英文 / 中文 标签切换 +- **高度可配** — 布局、颜色、元素排序、合并行等全部可自定义 + +### 💰 API 余额监控 (来自 balance-hud v1.1.3) +- **DeepSeek 实时余额** — 15s 自动轮询,实时余额 + 消耗追踪 +- **低余额预警** — 默认 ≤ ¥5 黄色提醒 + 红色充值警告,阈值可配 +- **会话独立消耗** — 每次启动重置计数 (PID 抢占式锁) +- **自动启动** — SessionStart 钩子,Claude Code 启动即后台运行 (async 不阻塞) + +## 快速安装 + +### 方式一:插件市场安装 (推荐) + +在 Claude Code 中运行: + +``` +/plugin marketplace add BFRKQSB7/balance-hud +/plugin install balance-hud@balance-hud +/balance-hud:setup +``` + +`/balance-hud:setup` 自动检测平台 + Shell + 运行时,配置 statusLine 并验证 HUD。 + +### 方式二:手动解压安装 + +从 [Releases](https://github.com/BFRKQSB7/balance-hud/releases) 下载 `balance-hud-v2.0.0.zip`: + +```bash +# macOS / Linux +unzip "balance-hud-v2.0.0.zip" -d ~/.claude/plugins/ + +# Windows (PowerShell) +Expand-Archive "balance-hud-v2.0.0.zip" -DestinationPath "$env:USERPROFILE\.claude\plugins\" +``` + +然后在 `~/.claude/settings.json` 中配置: + +```json +{ + "statusLine": { + "type": "command", + "command": "node ~/.claude/plugins/balance-hud/dist/index.js" + } +} +``` + +最后运行 `/balance-hud:setup` 验证配置。 + +### 方式三:交互式设置命令 + +如果插件已通过 marketplace 安装,直接运行 `/balance-hud:setup` — 自动检测平台、Shell、运行时路径,配置 statusLine 并验证 HUD。 + +## HUD 状态栏预览 + +### 默认展开布局 (3行) + +``` +[Opus] │ my-project git:(main*) +Context ████████░░ 76% │ Usage ██░░░░░░░░ 25% (1h 30m / 5h) +DeepSeek ¥13.37 | -¥0.93 (6.5%) 12:34:56 +``` + +### 紧凑布局 (1行) + +``` +[Opus] ████████░░ 76% | my-project git:(main*) | 5h: 25% | ⏱️ 5m │ DeepSeek ¥13.37 +``` + +### 低余额预警 + +``` +[Opus] │ my-project git:(main*) +Context ██████░░░░ 55% │ Usage ██░░░░░░░░ 25% (1h 30m / 5h) +⚠️ DeepSeek ¥3.50 | -¥7.53 (68.3%) — 请及时充值! +``` + +## 配置 + +### 基础配置 + +编辑 `~/.claude/plugins/balance-hud/config.json`,或运行 `/balance-hud:configure` 进入交互式配置。 + +```json +{ + "language": "zh-Hans", + "lineLayout": "expanded", + "display": { + "showTools": true, + "showAgents": true, + "showTodos": true, + "showDuration": true, + "showConfigCounts": true + } +} +``` + +### 低余额预警阈值 + +```bash +# 余额 ≤ ¥10 时预警 +node ~/.claude/plugins/balance-hud/scripts/auto_refresh.mjs --warn 10 + +# 余额 ≤ ¥2 时预警 +node ~/.claude/plugins/balance-hud/scripts/auto_refresh.mjs --warn 2 + +# 关闭预警 +node ~/.claude/plugins/balance-hud/scripts/auto_refresh.mjs --warn 0 +``` + +默认阈值 ¥5,持久保存在 `session_state.json`。 + +### 禁用 HUD + +```bash +# 临时禁用 (本次会话) +BALANCE_HUD_DISABLE=1 claude + +# 或兼容旧环境变量 +CLAUDE_HUD_DISABLE=1 claude +``` + +## 数据流 + +``` +SessionStart 钩子 (Claude Code 启动) + │ + ├──→ auto_refresh.mjs (后台进程, 每 15s 查询 DeepSeek API) + │ │ + │ ├── 写入 session_state.json (余额缓存) + │ └── 写入 balance_usage.json (HUD 快照) + │ + └──→ Claude Code 每 ~300ms 调用 statusLine + │ + └──→ dist/index.js (HUD 引擎) + │ + ├── stdin JSON (模型, 上下文, Token) + ├── transcript JSONL (工具, Agent, Todo) + ├── config 文件 (MCP, rules, hooks) + ├── balance_usage.json (余额标签, 自动检测) + │ + └──→ stdout → 多行 HUD 状态栏 +``` + +## 环境变量 + +| 变量 | 说明 | +|------|------| +| `DEEPSEEK_API_KEY` | DeepSeek API Key (优先级最高) | +| `ANTHROPIC_AUTH_TOKEN` | DeepSeek 回退 Key (Claude Code 自带) | +| `OPENAI_API_KEY` | OpenAI API Key | +| `BALANCE_HUD_DISABLE` | 设为 1 禁用 HUD | +| `CLAUDE_HUD_DISABLE` | 兼容旧版禁用变量 | +| `BALANCE_HUD_ALLOW_EXTRA_CMD` | 允许自定义命令标签 | +| `DEBUG` | 设为 `balance-hud` 或 `*` 开启调试日志 | + +## 文件结构 + +``` +plugins/balance-hud/ +├── .claude-plugin/ +│ ├── plugin.json # 插件元数据 +│ └── marketplace.json # 市场条目 +├── hooks/ +│ └── hooks.json # SessionStart 自动启动守护进程 +├── dist/ # HUD 引擎 (编译后的 JS) +│ ├── index.js # 入口点 +│ ├── config.js # 配置加载/验证 +│ ├── stdin.js # stdin JSON 解析 +│ ├── transcript.js # 会话 JSONL 解析 +│ ├── render/ # 渲染管线 +│ │ ├── index.js # 主渲染协调器 +│ │ ├── session-line.js # 紧凑模式单行 +│ │ ├── tools-line.js # 工具活动 +│ │ ├── agents-line.js # Agent 状态 +│ │ ├── todos-line.js # Todo 进度 +│ │ └── lines/ # 各行渲染器 +│ ├── i18n/ # 国际化 (en, zh-Hans, zh) +│ ├── utils/ # 工具函数 +│ └── ... +├── scripts/ +│ ├── auto_refresh.mjs # 余额刷新守护进程 (15s 轮询 + PID 锁 + --warn) +│ ├── hud_balance.mjs # 余额 HUD 渲染 (ANSI 彩色, 独立可用) +│ └── balance_snapshot.mjs # 余额快照生成器 +├── commands/ +│ ├── setup.md # /balance-hud:setup 安装配置命令 +│ └── configure.md # /balance-hud:configure 交互式配置命令 +├── config.json # 默认 HUD 配置 +├── session_state.json # 余额运行时缓存 +├── balance_usage.json # HUD 余额快照 (自动生成) +├── README.md +└── LICENSE +``` + +## 命令 + +| 命令 | 说明 | +|------|------| +| `/balance-hud:setup` | 自动检测环境,配置 statusLine | +| `/balance-hud:configure` | 交互式 HUD 配置 (布局、功能开关、颜色等) | + +## 颜色说明 (余额行) + +| 元素 | 颜色 | 说明 | +|------|------|------| +| `DeepSeek` | 蓝色 | 厂商标签 | +| `¥13.37` | 亮绿 / 亮黄 | 当前余额 (低余额变黄) | +| `-¥0.93` | 红色 | 已消耗金额 | +| `(6.5%)` | 品红 | 消耗百分比 | +| `12:34:56` | 橙色 | 刷新时间 | + +## 变更日志 + +### v2.0.0 +- **整合**:claude-hud v0.3.0 全功能 HUD 引擎 + balance-hud v1.1.3 余额监控 +- **新增**:全功能终端 HUD 状态栏 (上下文、工具、Agent、Todo、Git、用量) +- **新增**:多语言支持 (英文/中文) +- **新增**:交互式配置命令 `/balance-hud:configure` +- **新增**:余额数据自动集成到 HUD 状态栏 +- **保留**:SessionStart 自动启动余额监控守护进程 +- **保留**:低余额预警 (≤ ¥5 黄色 + 红色提醒) +- **保留**:PID 抢占式锁 (新会话自动杀旧进程) + +### v1.1.3 (balance-hud) +- Windows 兼容性修复 +- `"async": true` 原生支持 + +### v1.1.0 (balance-hud) +- 精简架构,PID 单实例锁 +- `--warn` 阈值设置 + +## 许可证 + +本项目基于以下开源项目构建,遵循 MIT License: + +| 项目 | 作者 | 许可证 | 说明 | +|------|------|--------|------| +| [claude-hud](https://github.com/jarrodwatts/claude-hud) | Jarrod Watts | MIT | 全功能 HUD 渲染引擎 (v0.3.0) | +| balance-hud | NYRO | MIT | API 余额监控 + 二次开发 | + +完整许可证文本见 [LICENSE](LICENSE)。第三方代码清单见 [NOTICE.md](NOTICE.md)。 + +## 致谢 + +- **[claude-hud](https://github.com/jarrodwatts/claude-hud)** by [Jarrod Watts](https://github.com/jarrodwatts) — 全功能终端 HUD 状态栏引擎。本项目的 HUD 渲染管线(`dist/render/`、`dist/config.js`、国际化等)基于 claude-hud v0.3.0 开发。 +- **[Claude Code](https://claude.ai/code)** by Anthropic — 插件平台与 statusLine API diff --git a/plugins/balance-hud/commands/configure.md b/plugins/balance-hud/commands/configure.md new file mode 100644 index 0000000..baf3a34 --- /dev/null +++ b/plugins/balance-hud/commands/configure.md @@ -0,0 +1,418 @@ +--- +description: Configure HUD display options (layout, language, presets, display elements) while preserving advanced manual overrides +allowed-tools: Read, Write, AskUserQuestion +--- + +# Configure Claude HUD + +**FIRST**: Use the Read tool to load `~/.claude/plugins/balance-hud/config.json` if it exists. + +Store current values and note whether config exists (determines which flow to use). + +## Core Features (on by default) + +These default to ON and are what most users keep. They ARE configurable +(`display.showModel`, `display.showContextBar`), but the guided flow keeps them +enabled — toggle them by editing `config.json` directly if needed: +- Model name `[Opus]` +- Context bar `████░░░░░░ 45%` + +Advanced settings such as `colors.*`, `pathLevels`, `maxWidth`, `forceMaxWidth`, +`elementOrder`, `display.mergeGroups`, `display.timeFormat`, `display.contextValue`, +`display.modelFormat`, `display.modelOverride`, `display.showProvider`, +`display.providerName`, `display.autocompactBuffer`, +`display.autoCompactWindow`, `display.promptCacheTtlSeconds`, +`display.usageThreshold`, `display.sevenDayThreshold`, +`display.environmentThreshold`, `display.contextWarningThreshold`, +`display.contextCriticalThreshold`, `display.advisorOverride`, and the +`display.externalUsage*` keys are preserved when saving but are not edited by +this guided flow. + +--- + +## Two Flows Based on Config State + +### Flow A: New User (no config) +Questions: **Layout → Preset → Language → Turn Off → Turn On → Custom Line** + +### Flow B: Update Config (config exists) +Questions: **Turn Off → Turn On → Git Style → Layout/Reset → Language → Custom Line** (6 questions max) + +--- + +## Flow A: New User (6 Questions) + +### Q1: Layout +- header: "Layout" +- question: "Choose your HUD layout:" +- multiSelect: false +- options: + - "Expanded (Recommended)" - Split into semantic lines (identity, project, environment, usage) + - "Compact" - Everything on one line + - "Compact + Separators" - One line with separator before activity + +### Q2: Preset +- header: "Preset" +- question: "Choose a starting configuration:" +- multiSelect: false +- options: + - "Full" - Everything enabled (Recommended) + - "Essential" - Activity + git, minimal info + - "Minimal" - Core only (model, context bar) + +### Q3: Language +- header: "Language" +- question: "Choose your HUD label language:" +- multiSelect: false +- options: + - "English (Recommended)" - Default, simplest onboarding path + - "中文" - Show HUD labels and status text in Chinese + +Save as `language: "en"` or `language: "zh-Hans"`. + +### Q4: Turn Off (based on chosen preset) +- header: "Turn Off" +- question: "Disable any of these? (enabled by your preset)" +- multiSelect: true +- options: **ONLY items that are ON in the chosen preset** (max 4) + - "Tools activity" - ◐ Edit: file.ts | ✓ Read ×3 + - "Agents status" - ◐ explore [haiku]: Finding code + - "Todo progress" - ▸ Fix bug (2/5 tasks) + - "Project name" - my-project path display + - "Added directories" - +repo +shared workspace directories from /add-dir + - "Git status" - git:(main*) branch indicator + - "Config counts" - 2 CLAUDE.md | 4 rules + - "Token breakdown" - (in: 45k, cache: 12k) + - "Output speed" - out: 42.1 tok/s + - "Usage limits" - 5h: 25% | 7d: 10% + - "Usage reset label" - show or hide the `resets in` prefix + - "Compact usage" - 5h: 25% (1h 30m) shorter format + - "Session duration" - ⏱️ 5m + - "Session name" - fix-auth-bug (session slug or custom title) + - "Session tokens" - Tokens 12.8M (in: 7k, out: 28k, cache: 12.8M) + - "Reasoning level" - ◑ high (low/medium/high/xhigh/max effort) + - "Output style" - style: explanatory (current output style name) + - "Session cost" - 💰 $0.42 + - "Skills activity" - active skills count + - "MCP status" - MCP server status + - "Memory usage" - process memory footprint + - "Prompt cache" - cache TTL countdown + - "Claude Code version" - the running CC version + - "Compaction count" - Compactions: 2 after /compact or auto-compaction + - "Advisor model" - Advisor: Opus 4.7 (when /advisor is configured) + +### Q5: Turn On (based on chosen preset) +- header: "Turn On" +- question: "Enable any of these? (disabled by your preset)" +- multiSelect: true +- options: **ONLY items that are OFF in the chosen preset** (max 4) + - (same list as above, filtered to OFF items) + +**Note:** If preset has all items ON (Full), Q5 shows "Nothing to enable - Full preset has everything!" +If preset has all items OFF (Minimal), Q4 shows "Nothing to disable - Minimal preset is already minimal!" + +### Q6: Custom Line (optional) +- header: "Custom Line" +- question: "Add a custom phrase to display in the HUD? (e.g. a motto, max 80 chars)" +- multiSelect: false +- options: + - "Skip" - No custom line + - "Enter custom text" - Ask user for their phrase via AskUserQuestion (free text input) + +If user chooses "Enter custom text", use AskUserQuestion to get their text. Save as `display.customLine` in config. + +--- + +## Flow B: Update Config (6 Questions) + +### Q1: Turn Off +- header: "Turn Off" +- question: "What do you want to DISABLE? (currently enabled)" +- multiSelect: true +- options: **ONLY items currently ON** (max 4, prioritize Activity first) + - "Tools activity" - ◐ Edit: file.ts | ✓ Read ×3 + - "Agents status" - ◐ explore [haiku]: Finding code + - "Todo progress" - ▸ Fix bug (2/5 tasks) + - "Project name" - my-project path display + - "Added directories" - +repo +shared workspace directories from /add-dir + - "Git status" - git:(main*) branch indicator + - "Session name" - fix-auth-bug (session slug or custom title) + - "Session tokens" - Tokens 12.8M (in: 7k, out: 28k, cache: 12.8M) + - "Reasoning level" - ◑ high (low/medium/high/xhigh/max effort) + - "Output style" - style: explanatory (current output style name) + - "Session cost" - 💰 $0.42 + - "Skills activity" - active skills count + - "MCP status" - MCP server status + - "Memory usage" - process memory footprint + - "Prompt cache" - cache TTL countdown + - "Claude Code version" - the running CC version + - "Compaction count" - Compactions: 2 after /compact or auto-compaction + - "Advisor model" - Advisor: Opus 4.7 (when /advisor is configured) + - "Usage bar style" - ██░░ 25% visual bar (only if usageBarEnabled is true) + - "Usage reset label" - show or hide the `resets in` prefix + - "Compact usage" - 5h: 25% (1h 30m) shorter format (only if usageCompact is false) + +If more than 4 items ON, show Activity items (Tools, Agents, Todos, Project, Git) first. +Info items (Counts, Tokens, Usage, Speed, Duration) can be turned off via "Reset to Minimal" in Q4. + +### Q2: Turn On +- header: "Turn On" +- question: "What do you want to ENABLE? (currently disabled)" +- multiSelect: true +- options: **ONLY items currently OFF** (max 4) + - "Config counts" - 2 CLAUDE.md | 4 rules + - "Token breakdown" - (in: 45k, cache: 12k) + - "Output speed" - out: 42.1 tok/s + - "Usage limits" - 5h: 25% | 7d: 10% + - "Usage bar style" - ██░░ 25% visual bar (only if usageBarEnabled is false) + - "Usage reset label" - show or hide the `resets in` prefix + - "Compact usage" - 5h: 25% (1h 30m) shorter format (only if usageCompact is false) + - "Added directories" - +repo +shared workspace directories from /add-dir + - "Session name" - fix-auth-bug (session slug or custom title) + - "Session tokens" - Tokens 12.8M (in: 7k, out: 28k, cache: 12.8M) + - "Session duration" - ⏱️ 5m + - "Reasoning level" - ◑ high (low/medium/high/xhigh/max effort) + - "Output style" - style: explanatory (current output style name) + - "Session cost" - 💰 $0.42 + - "Skills activity" - active skills count + - "MCP status" - MCP server status + - "Memory usage" - process memory footprint + - "Prompt cache" - cache TTL countdown + - "Claude Code version" - the running CC version + - "Compaction count" - Compactions: 2 after /compact or auto-compaction + - "Advisor model" - Advisor: Opus 4.7 (when /advisor is configured) + +### Q3: Git Style (only if Git is currently enabled) +- header: "Git Style" +- question: "How much git info to show?" +- multiSelect: false +- options: + - "Branch only" - git:(main) + - "Branch + dirty" - git:(main*) shows uncommitted changes + - "Full details" - git:(main* ↑2 ↓1) includes ahead/behind + - "File stats" - git:(main* !2 +1 ?3) Starship-compatible format + +**Skip Q3 if Git is OFF** - proceed to Q4. + +### Q4: Layout/Reset +- header: "Layout/Reset" +- question: "Change layout or reset to preset?" +- multiSelect: false +- options: + - "Keep current" - No layout/preset changes (current: Expanded/Compact/Compact + Separators) + - "Switch to Expanded" - Split into semantic lines (if not current) + - "Switch to Compact" - Everything on one line (if not current) + - "Reset to Full" - Enable everything + - "Reset to Essential" - Activity + git only + +### Q5: Language +- header: "Language" +- question: "Update HUD label language? (current: '{English or 中文}')" +- multiSelect: false +- options: + - "Keep current" - No change + - "English (Recommended)" - Use English HUD labels + - "中文" - Use Chinese HUD labels + +If user chooses "Keep current", leave `language` unchanged. +If user chooses "English (Recommended)", save `language: "en"`. +If user chooses "中文", save `language: "zh-Hans"`. + +### Q6: Custom Line (optional) +- header: "Custom Line" +- question: "Update your custom phrase? (currently: '{current customLine or none}')" +- multiSelect: false +- options: + - "Keep current" - No change (skip if no customLine set) + - "Enter custom text" - Set or update custom phrase (max 80 chars) + - "Remove" - Clear the custom line (only show if customLine is currently set) + +If user chooses "Enter custom text", use AskUserQuestion to get their text. Save as `display.customLine` in config. +If user chooses "Remove", set `display.customLine` to `""` in config. + +--- + +## Preset Definitions + +**Full** (everything ON): +- Activity: Tools ON, Skills ON, MCP ON, Agents ON, Todos ON +- Info: Added Dirs ON, Counts ON, Tokens ON, Usage ON, Reset Label ON, Cost ON, Duration ON, Session Name ON, Session Tokens ON, Reasoning Level ON, Output Style ON, Memory ON, Prompt Cache ON, CC Version ON, Compactions ON, Advisor ON +- Git: ON (with dirty indicator, no ahead/behind) + +**Essential** (activity + git): +- Activity: Tools ON, Agents ON, Todos ON +- Info: Counts OFF, Tokens OFF, Usage OFF, Duration ON, Session Name OFF, Session Tokens OFF +- Git: ON (with dirty indicator) + +**Minimal** (core only — this is the default): +- Activity: Tools OFF, Agents OFF, Todos OFF +- Info: Counts OFF, Tokens OFF, Usage OFF, Duration OFF, Session Name OFF, Session Tokens OFF +- Git: ON (with dirty indicator) + +--- + +## Layout Mapping + +| Option | Config | +|--------|--------| +| Expanded | `lineLayout: "expanded", showSeparators: false` | +| Compact | `lineLayout: "compact", showSeparators: false` | +| Compact + Separators | `lineLayout: "compact", showSeparators: true` | + +--- + +## Language Mapping + +| Option | Config | +|--------|--------| +| English (Recommended) | `language: "en"` | +| 中文 | `language: "zh-Hans"` | + +--- + +## Git Style Mapping + +| Option | Config | +|--------|--------| +| Branch only | `gitStatus: { enabled: true, showDirty: false, showAheadBehind: false, showFileStats: false }` | +| Branch + dirty | `gitStatus: { enabled: true, showDirty: true, showAheadBehind: false, showFileStats: false }` | +| Full details | `gitStatus: { enabled: true, showDirty: true, showAheadBehind: true, showFileStats: false }` | +| File stats | `gitStatus: { enabled: true, showDirty: true, showAheadBehind: false, showFileStats: true }` | + +--- + +## Element Mapping + +| Element | Config Key | +|---------|------------| +| Model name | `display.showModel` | +| Context bar | `display.showContextBar` | +| Tools activity | `display.showTools` | +| Skills activity | `display.showSkills` | +| MCP status | `display.showMcp` | +| Agents status | `display.showAgents` | +| Todo progress | `display.showTodos` | +| Project name | `display.showProject` | +| Added directories | `display.showAddedDirs` (layout via `display.addedDirsLayout`) | +| Git status | `gitStatus.enabled` | +| Config counts | `display.showConfigCounts` | +| Token breakdown | `display.showTokenBreakdown` | +| Output speed | `display.showSpeed` | +| Session cost | `display.showCost` | +| Usage limits | `display.showUsage` | +| Usage bar style | `display.usageBarEnabled` | +| Compact usage | `display.usageCompact` | +| Usage value | `display.usageValue` | +| Usage reset label | `display.showResetLabel` | +| Session name | `display.showSessionName` | +| Session duration | `display.showDuration` | +| Session tokens | `display.showSessionTokens` | +| Session start date | `display.showSessionStartDate` | +| Last response time | `display.showLastResponseAt` | +| Compaction count | `display.showCompactions` | +| Reasoning level | `display.showEffortLevel` | +| Output style | `display.showOutputStyle` | +| Memory usage | `display.showMemoryUsage` | +| Prompt cache | `display.showPromptCache` (TTL via `display.promptCacheTtlSeconds`) | +| Claude Code version | `display.showClaudeCodeVersion` | +| Advisor model | `display.showAdvisor` (override via `display.advisorOverride`) | +| Custom line | `display.customLine` | +| Custom line position | `display.customLinePosition` | + +**Defaults to ON (configurable booleans, kept enabled by the guided flow):** +- `display.showModel` (default `true`) +- `display.showContextBar` (default `true`) + +--- + +## Usage Style Mapping + +| Option | Config | Example | +|--------|--------|---------| +| Bar style | `usageBarEnabled: true` | `Usage ██░░ 25% (resets in 1h 30m)` | +| Text style | `usageBarEnabled: false` | `Usage 5h 25% (resets in 1h 30m)` | +| Compact | `usageCompact: true` | `5h: 25% (1h 30m)` — no "Usage" label, shorter reset format | + +`usageCompact` takes precedence over `usageBarEnabled` when both are set. Compact mode always uses the text format (no bar). + +**Note**: Usage style only applies when `display.showUsage: true`. When 7d usage >= 80%, it also shows with the same style. +Set `display.usageValue: "remaining"` manually to show remaining quota percentages while keeping warning thresholds based on used quota. + +--- + +## Processing Logic + +### For New Users (Flow A): +1. Apply chosen preset as base +2. Apply chosen language +3. Apply Turn Off selections (set those items to OFF) +4. Apply Turn On selections (set those items to ON) +5. Apply chosen layout + +### For Returning Users (Flow B): +1. Start from current config +2. Apply Turn Off selections (set to OFF, including usageBarEnabled if selected) +3. Apply Turn On selections (set to ON, including usageBarEnabled if selected) +4. Apply Git Style selection (if shown) +5. If "Reset to [preset]" selected, override with preset values +6. If layout change selected, apply it +7. If language change selected, apply it + +--- + +## Before Writing - Validate & Preview + +**GUARDS - Do NOT write config if:** +- User cancels (Esc) → say "Configuration cancelled." +- No changes from current config → say "No changes needed - config unchanged." + +**Show preview before saving:** + +1. **Summary of changes:** +``` +Layout: Compact → Expanded +Language: English → 中文 +Git style: Branch + dirty +Changes: + - Usage limits: OFF → ON + - Config counts: ON → OFF +``` + +2. **Preview of HUD (Expanded layout):** +``` +[Opus | Pro] │ my-project git:(main*) +Context ████░░░░░ 45% │ Usage ██░░░░░░░░ 25% (1h 30m / 5h) +◐ Edit: file.ts | ✓ Read ×3 +▸ Fix auth bug (2/5) +``` + +**Preview of HUD (Compact layout):** +``` +[Opus | Pro] ████░░░░░ 45% | my-project git:(main*) | 5h: 25% | ⏱️ 5m +◐ Edit: file.ts | ✓ Read ×3 +▸ Fix auth bug (2/5) +``` + +3. **Confirm**: "Save these changes?" + +--- + +## Write Configuration + +Write to `~/.claude/plugins/balance-hud/config.json`. + +Merge with existing config, preserving: +- `pathLevels` (not in configure flow) +- `display.usageThreshold` (advanced config) +- `display.environmentThreshold` (advanced config) +- `display.contextWarningThreshold` (advanced config) +- `display.contextCriticalThreshold` (advanced config) +- `colors` (advanced manual palette overrides) + +**Migration note**: Old configs with `layout: "default"` or `layout: "separators"` are automatically migrated to the new `lineLayout` + `showSeparators` format on load. + +--- + +## After Writing + +Say: "Configuration saved! The HUD will reflect your changes immediately." diff --git a/plugins/balance-hud/commands/setup.md b/plugins/balance-hud/commands/setup.md new file mode 100644 index 0000000..82c0855 --- /dev/null +++ b/plugins/balance-hud/commands/setup.md @@ -0,0 +1,743 @@ +--- +description: Configure balance-hud as your statusline +allowed-tools: Bash, Read, Edit, AskUserQuestion +--- + +**Note**: Placeholders like `{RUNTIME_PATH}`, `{SOURCE}`, and `{GENERATED_COMMAND}` should be substituted with actual detected values. + +## Step 0: Detect Ghost Installation (Run First) + +Check for inconsistent plugin state that can occur after failed installations: + +**macOS/Linux**: +```bash +# Check 1: Cache exists? +CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" +CACHE_EXISTS=$(ls -d "$CLAUDE_DIR/plugins/cache"/*/balance-hud 2>/dev/null && echo "YES" || echo "NO") + +# Check 2: Registry entry exists? +REGISTRY_EXISTS=$(grep -q "balance-hud" "$CLAUDE_DIR/plugins/installed_plugins.json" 2>/dev/null && echo "YES" || echo "NO") + +# Check 3: Temp files left behind? +TEMP_FILES=$(ls -d "$CLAUDE_DIR/plugins/cache/temp_local_"* 2>/dev/null | head -1) + +echo "Cache: $CACHE_EXISTS | Registry: $REGISTRY_EXISTS | Temp: ${TEMP_FILES:-none}" +``` + +**Windows (PowerShell)**: +```powershell +$claudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" } +$cache = (Get-ChildItem (Join-Path $claudeDir "plugins\cache") -Directory | ForEach-Object { Test-Path (Join-Path $_.FullName "balance-hud") }) -contains $true +$registry = (Get-Content (Join-Path $claudeDir "plugins\installed_plugins.json") -ErrorAction SilentlyContinue) -match "balance-hud" +$temp = Get-ChildItem (Join-Path $claudeDir "plugins\cache\temp_local_*") -ErrorAction SilentlyContinue +Write-Host "Cache: $cache | Registry: $registry | Temp: $($temp.Count) files" +``` + +### Interpreting Results + +| Cache | Registry | Meaning | Action | +|-------|----------|---------|--------| +| YES | YES | Normal install (may still be broken) | Continue to Step 1 | +| YES | NO | Ghost install - cache orphaned | Clean up cache | +| NO | YES | Ghost install - registry stale | Clean up registry | +| NO | NO | Not installed | Continue to Step 1 | + +If **temp files exist**, a previous install was interrupted. Clean them up. + +### Cleanup Commands + +If ghost installation detected, ask user if they want to reset. If yes: + +**macOS/Linux**: +```bash +CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" + +# Remove orphaned cache (handles both direct and marketplace installs) +rm -rf "$CLAUDE_DIR/plugins/cache"/*/balance-hud + +# Remove temp files from failed installs +rm -rf "$CLAUDE_DIR/plugins/cache/temp_local_"* + +# Reset registry (removes ALL plugins - warn user first!) +# Only run if user confirms they have no other plugins they want to keep: +echo '{"version": 2, "plugins": {}}' > "$CLAUDE_DIR/plugins/installed_plugins.json" +``` + +**Windows (PowerShell)**: +```powershell +$claudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" } + +# Remove orphaned cache (handles both direct and marketplace installs) +Get-ChildItem (Join-Path $claudeDir "plugins\cache") -Directory | ForEach-Object { Remove-Item -Recurse -Force (Join-Path $_.FullName "balance-hud") -ErrorAction SilentlyContinue } + +# Remove temp files +Remove-Item -Recurse -Force (Join-Path $claudeDir "plugins\cache\temp_local_*") -ErrorAction SilentlyContinue + +# Reset registry (removes ALL plugins - warn user first!) +'{"version": 2, "plugins": {}}' | Set-Content (Join-Path $claudeDir "plugins\installed_plugins.json") +``` + +After cleanup, tell user to **restart Claude Code** and run `/plugin install balance-hud` again. + +### Linux: Cross-Device Filesystem Check + +**On Linux only**, if install keeps failing, check for EXDEV issue: +```bash +[ "$(df --output=source ~ /tmp 2>/dev/null | tail -2 | uniq | wc -l)" = "2" ] && echo "CROSS_DEVICE" +``` + +If this outputs `CROSS_DEVICE`, `/tmp` and home are on different filesystems. This causes `EXDEV: cross-device link not permitted` during installation. Workaround: +```bash +mkdir -p ~/.cache/tmp && TMPDIR=~/.cache/tmp claude /plugin install balance-hud +``` + +This is a [Claude Code platform limitation](https://github.com/anthropics/claude-code/issues/14799). + +--- + +## Step 1: Detect Platform, Shell, and Runtime + +**IMPORTANT**: Use the environment context values (`Platform:` and `Shell:`) as your starting point. On `win32`, also check `$OSTYPE` via the Bash tool. Some Windows sessions report `Shell: powershell` while the command path exposed to Claude Code is Git Bash/MSYS2. When `$OSTYPE` is `msys` or `cygwin`, the PowerShell command format can fail before PowerShell runs because bash expands `$env:VAR`, `$p`, and `$(...)` expressions first (see [#531](https://github.com/jarrodwatts/balance-hud/issues/531)). + +**On `win32`, run this check first:** +```bash +echo $OSTYPE +``` + +| Platform | Shell | OSTYPE | Command Format | +|----------|-------|--------|----------------| +| `darwin` | any | any | bash (macOS instructions) | +| `linux` | any | any | bash (Linux instructions) | +| `win32` | `bash` | any | bash — Windows + Git Bash instructions | +| `win32` | `powershell`, `pwsh`, or `cmd` | `msys` or `cygwin` | bash — Windows + Git Bash instructions (the active command environment is MSYS/Cygwin; PowerShell syntax is unsafe here) | +| `win32` | `powershell`, `pwsh`, or `cmd` | other / empty | PowerShell — Windows + PowerShell instructions | + +--- + +**macOS/Linux** (Platform: `darwin` or `linux`): + +1. Get plugin path (sorted by dotted numeric version, not modification time): + ```bash + ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/*/balance-hud/*/ 2>/dev/null | awk -F/ '{ print $(NF-1) "\t" $(0) }' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+[[:space:]]' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2- + ``` + If empty, the plugin is not installed. Go back to Step 0 to check for ghost installation or EXDEV issues. If Step 0 was clean, ask the user to install via `/plugin install balance-hud` first. + +2. Get runtime absolute path: + - On `darwin` or `linux`, prefer bun for performance and fall back to node: + ```bash + command -v bun 2>/dev/null || command -v node 2>/dev/null + ``` + - On `win32` + `bash`, require node. Do not fall back to bun on Windows: + ```bash + command -v node 2>/dev/null + ``` + + If empty, stop setup and explain that the current shell cannot find the required runtime. + - On **Windows + Git Bash/MSYS2**, explicitly explain that the current Git Bash session could not find Node.js, even if Claude Code itself is installed. + - If `winget` is available, recommend: + ```bash + winget install OpenJS.NodeJS.LTS + ``` + - On Windows, ask the user to install Node.js LTS from https://nodejs.org/ + - On macOS/Linux, ask the user to install one of these: + - Node.js LTS from https://nodejs.org/ + - Bun from https://bun.sh/ + - After installation, ask the user to restart their shell and re-run `/balance-hud:setup`. + +3. Verify the runtime exists: + ```bash + ls -la {RUNTIME_PATH} + ``` + If it doesn't exist, re-detect or ask user to verify their installation. + +4. Determine source file based on runtime: + - On `darwin` or `linux`, use `src/index.ts` when the runtime is bun. Otherwise use `dist/index.js`. + - On Windows, always use `dist/index.js`. + +5. Generate command (quotes around runtime path handle spaces): + + The command exports `COLUMNS` so the HUD knows the real terminal width. + Claude Code pipes the subprocess stdout, so `process.stdout.columns` is + unavailable at runtime. Prefer Claude Code's inherited positive-integer + `COLUMNS`, then try `stty size /dev/null | awk '"'"'{print $2}'"'"');; esac; case "$cols" in ""|*[!0-9]*) cols=120;; esac; export COLUMNS=$(( cols > 4 ? cols - 4 : 1 )); plugin_dir=$(ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/*/balance-hud/*/ 2>/dev/null | awk -F/ '"'"'{ print $(NF-1) "\t" $(0) }'"'"' | grep -E '"'"'^[0-9]+\.[0-9]+\.[0-9]+[[:space:]]'"'"' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2-); exec "{RUNTIME_PATH}" --env-file /dev/null "${plugin_dir}{SOURCE}"' + ``` + + **When runtime is node**: + ``` + bash -c 'cols=${COLUMNS:-}; case "$cols" in ""|*[!0-9]*) cols=$(stty size /dev/null | awk '"'"'{print $2}'"'"');; esac; case "$cols" in ""|*[!0-9]*) cols=120;; esac; export COLUMNS=$(( cols > 4 ? cols - 4 : 1 )); plugin_dir=$(ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/*/balance-hud/*/ 2>/dev/null | awk -F/ '"'"'{ print $(NF-1) "\t" $(0) }'"'"' | grep -E '"'"'^[0-9]+\.[0-9]+\.[0-9]+[[:space:]]'"'"' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2-); exec "{RUNTIME_PATH}" "${plugin_dir}{SOURCE}"' + ``` + +**Windows + Git Bash** (Platform: `win32`, Shell: `bash`): + +Do not use PowerShell commands when the shell is bash. Claude Code invokes statusLine commands through bash, which will interpret PowerShell variables like `$env` and `$p` before PowerShell ever sees them. + +On Windows require `node` and always use `dist/index.js`. + +**Important**: Do **not** reuse the macOS/Linux awk-based command on Windows + Git Bash. The `awk` fragment requires `'"'"'` quoting to nest single quotes inside `bash -c '...'`. After JSON encoding and decoding, this quoting breaks on Windows Git Bash, causing a silent syntax error that prevents the HUD process from starting (see [#326](https://github.com/jarrodwatts/balance-hud/issues/326)). + +Instead, use `sort -V` (GNU version sort, included with Git for Windows) which avoids nested single quotes entirely. Also avoid wrapping the generated command in a second `bash -c ...` layer. Claude Code is already invoking the statusline through bash, so the direct shell command lets `exec` replace that shell instead of spawning an extra bash wrapper first. The command still exports `COLUMNS` so the HUD receives the real terminal width, and it uses the marketplace-aware cache glob: + + ``` + cols=${COLUMNS:-}; case "$cols" in ""|*[!0-9]*) cols=$(stty size /dev/null | awk '{print $2}');; esac; case "$cols" in ""|*[!0-9]*) cols=120;; esac; export COLUMNS=$(( cols > 4 ? cols - 4 : 1 )); plugin_dir=$(ls -1d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/*/balance-hud/*/ 2>/dev/null | sort -V | tail -1); exec "{RUNTIME_PATH}" "${plugin_dir}{SOURCE}" + ``` + +**Windows + PowerShell** (Platform: `win32`, Shell: `powershell`, `pwsh`, or `cmd`, OSTYPE: other/empty): + +> **Before proceeding**: if `echo $OSTYPE` returned `msys` or `cygwin`, use the **Windows + Git Bash** instructions above. In that environment, bash can expand PowerShell variables before PowerShell runs. + +1. Get plugin path: + ```powershell + $claudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" } + (Get-ChildItem (Join-Path $claudeDir "plugins\cache\*\balance-hud\*") -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -match '^\d+(\.\d+)+$' } | Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1).FullName + ``` + The trailing `\*` on the cache glob is required. Without it, `Get-ChildItem` returns the `balance-hud` directory itself, whose name does not match the `^\d+(\.\d+)+$` version pattern, so the lookup resolves to `$null` and any subsequent `Join-Path` throws (see [#521](https://github.com/jarrodwatts/balance-hud/issues/521)). + + If empty or errors, the plugin is not installed. Ask the user to install via marketplace first. + +2. Get runtime absolute path (require node on Windows): + ```powershell + if (Get-Command node -ErrorAction SilentlyContinue) { (Get-Command node).Source } else { Write-Error "Node.js not found" } + ``` + + If node is not found, stop setup and explain that the current PowerShell session cannot find Node.js. + - If `winget` is available, recommend: + ```powershell + winget install OpenJS.NodeJS.LTS + ``` + - Otherwise ask the user to install Node.js LTS, then restart PowerShell and re-run `/balance-hud:setup`. + - On Windows, do not offer Bun for statusLine setup. Use Node.js only. + +3. Use `dist\index.js`. + +4. Write the Windows statusline launcher. + + Windows PowerShell startup plus `Get-ChildItem | Sort-Object [version]` can exceed Claude Code's render cadence on every statusLine refresh. Write a small Node launcher once during setup, then invoke it through `cmd.exe` on each refresh. The launcher uses the setup-time validated `node.exe`, preserves update discovery by finding the latest installed `balance-hud` version, and prefers inherited `COLUMNS` before falling back to 120. + + The launcher file at `$claudeDir/plugins/balance-hud/statusline.mjs` should contain: + + ```js + import fs from 'node:fs'; + import os from 'node:os'; + import path from 'node:path'; + import { pathToFileURL } from 'node:url'; + + const envColumns = Number.parseInt(process.env.COLUMNS ?? '', 10); + const width = Number.isFinite(envColumns) && envColumns > 0 ? envColumns : 120; + process.env.COLUMNS = String(Math.max(1, width - 4)); + + const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'); + const cacheDir = path.join(claudeDir, 'plugins', 'cache'); + + function versionParts(value) { + const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(value); + return match ? match.slice(1, 4).map(Number) : null; + } + + function compareVersions(a, b) { + for (let i = 0; i < 3; i += 1) { + if (a[i] !== b[i]) return a[i] - b[i]; + } + return 0; + } + + const candidates = []; + try { + for (const marketplace of fs.readdirSync(cacheDir, { withFileTypes: true })) { + if (!marketplace.isDirectory()) continue; + const pluginRoot = path.join(cacheDir, marketplace.name, 'balance-hud'); + let versions = []; + try { + versions = fs.readdirSync(pluginRoot, { withFileTypes: true }); + } catch { + continue; + } + for (const version of versions) { + if (!version.isDirectory()) continue; + const parts = versionParts(version.name); + if (!parts) continue; + const dir = path.join(pluginRoot, version.name); + if (fs.existsSync(path.join(dir, 'dist', 'index.js'))) { + candidates.push({ dir, parts }); + } + } + } + } catch { + process.exit(0); + } + + candidates.sort((a, b) => compareVersions(a.parts, b.parts)); + const latest = candidates.at(-1); + if (!latest) process.exit(0); + + const hud = await import(pathToFileURL(path.join(latest.dir, 'dist', 'index.js')).href); + if (typeof hud.main === 'function') { + await hud.main(); + } + ``` + + Write it using `[System.IO.File]::WriteAllText` with `New-Object System.Text.UTF8Encoding $false` so the file is UTF-8 without a BOM: + + ```powershell + $wrapperDir = Join-Path $claudeDir "plugins\balance-hud" + New-Item -ItemType Directory -Force -Path $wrapperDir | Out-Null + $wrapperPath = Join-Path $wrapperDir "statusline.mjs" + $wrapperBody = @' + import fs from 'node:fs'; + import os from 'node:os'; + import path from 'node:path'; + import { pathToFileURL } from 'node:url'; + + const envColumns = Number.parseInt(process.env.COLUMNS ?? '', 10); + const width = Number.isFinite(envColumns) && envColumns > 0 ? envColumns : 120; + process.env.COLUMNS = String(Math.max(1, width - 4)); + + const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'); + const cacheDir = path.join(claudeDir, 'plugins', 'cache'); + + function versionParts(value) { + const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(value); + return match ? match.slice(1, 4).map(Number) : null; + } + + function compareVersions(a, b) { + for (let i = 0; i < 3; i += 1) { + if (a[i] !== b[i]) return a[i] - b[i]; + } + return 0; + } + + const candidates = []; + try { + for (const marketplace of fs.readdirSync(cacheDir, { withFileTypes: true })) { + if (!marketplace.isDirectory()) continue; + const pluginRoot = path.join(cacheDir, marketplace.name, 'balance-hud'); + let versions = []; + try { + versions = fs.readdirSync(pluginRoot, { withFileTypes: true }); + } catch { + continue; + } + for (const version of versions) { + if (!version.isDirectory()) continue; + const parts = versionParts(version.name); + if (!parts) continue; + const dir = path.join(pluginRoot, version.name); + if (fs.existsSync(path.join(dir, 'dist', 'index.js'))) { + candidates.push({ dir, parts }); + } + } + } + } catch { + process.exit(0); + } + + candidates.sort((a, b) => compareVersions(a.parts, b.parts)); + const latest = candidates.at(-1); + if (!latest) process.exit(0); + + const hud = await import(pathToFileURL(path.join(latest.dir, 'dist', 'index.js')).href); + if (typeof hud.main === 'function') { + await hud.main(); + } + '@ + [System.IO.File]::WriteAllText($wrapperPath, $wrapperBody, (New-Object System.Text.UTF8Encoding $false)) + ``` + + `$runtimePath` is the value detected in step 2 (the absolute path returned by `(Get-Command node).Source`, typically `C:\Program Files\nodejs\node.exe`). `Set-Content -Encoding UTF8` and `Out-File -Encoding UTF8` on Windows PowerShell 5.1 both emit a UTF-8 BOM. `WriteAllText` + `UTF8Encoding $false` writes without a BOM in both PowerShell 5.1 and PowerShell 7+. + +5. Generate command: + + ``` + {CMD_PATH} /d /s /c ""{RUNTIME_PATH}" "{WRAPPER_PATH}"" + ``` + + `{CMD_PATH}` is the absolute `cmd.exe` path, preferably `$env:SystemRoot\System32\cmd.exe`. `{WRAPPER_PATH}` is the value of `$wrapperPath` from step 4 (typically `C:\Users\\.claude\plugins\balance-hud\statusline.mjs`). If you build the string in PowerShell, use: + + ```powershell + $cmdPath = Join-Path $env:SystemRoot "System32\cmd.exe" + if (-not (Test-Path $cmdPath)) { $cmdPath = "cmd.exe" } + $generatedCommand = $cmdPath + ' /d /s /c ""' + $runtimePath + '" "' + $wrapperPath + '""' + ``` + +**WSL (Windows Subsystem for Linux)**: If running in WSL, use the macOS/Linux instructions. Ensure the plugin is installed in the Linux environment (`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/...`), not the Windows side. + +## Step 2: Test Command + +Run the generated command. It should produce output (the HUD lines) within a few seconds. + +- If it errors, do not proceed to Step 3. +- If it hangs for more than a few seconds, cancel and debug. +- This test catches issues like broken runtime binaries, missing plugins, or path problems. + +## Step 2.5: Detect Existing Statusline and Create Backup + +Before writing to `settings.json`, check whether a `statusLine` key already exists and protect the user's current configuration. This covers the existing-statusLine overwrite issue tracked in [#547](https://github.com/jarrodwatts/balance-hud/issues/547). + +### 2.5.1: Read the existing statusLine + +**macOS/Linux**: +```bash +SETTINGS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" +EXISTING_COMMAND="" +EXISTING_COMMAND_PREVIEW="" + +if [ -f "$SETTINGS" ]; then + EXISTING_COMMAND=$("{RUNTIME_PATH}" -e ' +const fs = require("fs"); +const settingsPath = process.argv[1]; + +try { + const text = fs.readFileSync(settingsPath, "utf8"); + if (text.trim() === "") process.exit(0); + + const json = JSON.parse(text); + const command = json && json.statusLine && typeof json.statusLine.command === "string" + ? json.statusLine.command + : ""; + process.stdout.write(command); +} catch (error) { + console.error("Unable to read statusLine.command from settings.json: " + error.message); + process.exit(1); +} +' "$SETTINGS") || exit 1 + + EXISTING_COMMAND_PREVIEW=$(printf '%s' "$EXISTING_COMMAND" | "{RUNTIME_PATH}" -e ' +let value = ""; +process.stdin.setEncoding("utf8"); +process.stdin.on("data", chunk => { value += chunk; }); +process.stdin.on("end", () => { + const redacted = value + .replace(/\b(Bearer)\s+["\x27]?[^"\x27\s]+/gi, "$1 [REDACTED]") + .replace(/\b(Authorization\s*:\s*)["\x27]?[^"\x27\s]+/gi, "$1[REDACTED]") + .replace(/\b(token|api[_-]?key|secret|password|pass|auth)(=|:)\s*["\x27]?[^"\x27\s]+/gi, "$1$2[REDACTED]") + .replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "sk-[REDACTED]") + .replace(/\bgh[pousr]_[A-Za-z0-9_]{8,}\b/g, "[GITHUB_TOKEN_REDACTED]") + .replace(/\s+/g, " ") + .trim(); + + process.stdout.write(redacted.length > 160 ? redacted.slice(0, 157) + "..." : redacted); +}); +') +fi +``` + +**Windows (PowerShell)**: +```powershell +$settingsPath = if ($env:CLAUDE_CONFIG_DIR) { Join-Path $env:CLAUDE_CONFIG_DIR "settings.json" } else { Join-Path $HOME ".claude\settings.json" } +$existingCommand = "" +$existingCommandPreview = "" +if (Test-Path $settingsPath) { + try { + $json = Get-Content $settingsPath -Raw | ConvertFrom-Json + if ($json.statusLine -and $json.statusLine.command) { + $existingCommand = $json.statusLine.command + } + } catch { + Write-Error "Unable to read statusLine.command from settings.json: $($_.Exception.Message)" + throw + } + + if ($existingCommand -ne "") { + $existingCommandPreview = $existingCommand ` + -replace "(?i)\b(Bearer)\s+[`"']?[^`"'\s]+", '$1 [REDACTED]' ` + -replace "(?i)\b(Authorization\s*:\s*)[`"']?[^`"'\s]+", '$1[REDACTED]' ` + -replace "(?i)\b(token|api[_-]?key|secret|password|pass|auth)(=|:)\s*[`"']?[^`"'\s]+", '$1$2[REDACTED]' ` + -replace "\bsk-[A-Za-z0-9_-]{8,}\b", 'sk-[REDACTED]' ` + -replace "\bgh[pousr]_[A-Za-z0-9_]{8,}\b", '[GITHUB_TOKEN_REDACTED]' ` + -replace "\s+", " " + $existingCommandPreview = $existingCommandPreview.Trim() + if ($existingCommandPreview.Length -gt 160) { + $existingCommandPreview = $existingCommandPreview.Substring(0, 157) + "..." + } + } +} +``` + +### 2.5.2: Classify the existing statusline + +If `EXISTING_COMMAND` / `$existingCommand` is non-empty, classify it: + +| Pattern in command | Classification | Source label | +|---|---|---| +| Contains `balance-hud` | **Reinstall** (own config) | `balance-hud` | +| Contains `claude-pace` | **Known project** | `claude-pace` | +| Contains `cc-statusline` or `ccstatusline` | **Known project** | `cc-statusline` | +| Contains `statusline.sh` or `statusline.js` or `statusline.py` | **Likely another statusline** | `statusline script` | +| Any other non-empty value | **Custom script** | `custom` | +| Empty / missing key | **Clean install** | (none) | + +### 2.5.3: Create a timestamped backup + +**Always** create a backup of `settings.json` before modifying it, regardless of whether a statusline exists. This protects against corruption (see [#315]) and gives users a recovery path. + +**macOS/Linux**: +```bash +SETTINGS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" +BACKUP_TIMESTAMP=$(date +%Y%m%d-%H%M%S) +BACKUP_PATH="" +if [ -f "$SETTINGS" ]; then + BACKUP_PATH="${SETTINGS}.bak.${BACKUP_TIMESTAMP}" + if cp "$SETTINGS" "$BACKUP_PATH"; then + echo "Backup created: $BACKUP_PATH" + else + echo "Failed to create backup at: $BACKUP_PATH" >&2 + exit 1 + fi +fi +``` + +**Windows (PowerShell)**: +```powershell +$backupPath = "" +if (Test-Path $settingsPath) { + $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' + $backupPath = "${settingsPath}.bak.${timestamp}" + Copy-Item $settingsPath $backupPath -ErrorAction Stop + Write-Host "Backup created: $backupPath" +} +``` + +### 2.5.4: Prompt the user if a statusline exists + +**If the statusline is empty (clean install)**: Skip this step. Proceed directly to Step 3. + +**If the statusline is balance-hud (reinstall)**: Skip this step. The new command replaces the old one — this is an idempotent update. Proceed to Step 3. + +**If the statusline belongs to a known project or is a custom script**: Use AskUserQuestion to ask the user what to do. + +Use AskUserQuestion: +- header: "Existing statusline detected" +- question: "Found an existing statusLine in settings.json:\n\n command preview: {REDACTED_COMMAND_PREVIEW}\n source: {SOURCE_LABEL}\n\nWhat would you like to do?" +- options: + - "Replace it with balance-hud (your current setup will be backed up)" + - "Keep my current statusline and exit setup (settings stay unchanged)" + - "Cancel setup without changing settings" + +Set `{REDACTED_COMMAND_PREVIEW}` to `EXISTING_COMMAND_PREVIEW` on macOS/Linux or `$existingCommandPreview` on Windows. Use only the redacted/truncated preview in the prompt and normal output. Do not print the full previous command because it may contain tokens or secrets. + +**If the user chooses "Keep" or "Cancel"**: Stop setup. The backup from 2.5.3 is still available if one was created. Tell the user: + +> No changes were made to your settings. Your existing statusline is preserved. Setup created no settings mutation apart from the backup file at `{BACKUP_PATH or $backupPath}` if that value is set. + +**If the user chooses "Replace"**: Proceed to Step 3. The backup from 2.5.3 ensures the previous configuration can be restored. + +### 2.5.5: Save the previous command for potential restoration + +Store the previous `statusLine.command` value in a file alongside the settings backup. This makes it easy to restore if the user later wants to switch back. + +**macOS/Linux**: +```bash +CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" +if [ -n "$EXISTING_COMMAND" ]; then + PREVIOUS_COMMAND_DIR="$CLAUDE_DIR/plugins/balance-hud" + PREVIOUS_COMMAND_PATH="$PREVIOUS_COMMAND_DIR/previous-statusline.txt" + mkdir -p "$PREVIOUS_COMMAND_DIR" + chmod 700 "$PREVIOUS_COMMAND_DIR" 2>/dev/null || true + if ( + umask 077 + printf '%s' "$EXISTING_COMMAND" > "$PREVIOUS_COMMAND_PATH" + ); then + chmod 600 "$PREVIOUS_COMMAND_PATH" 2>/dev/null || true + echo "Previous statusline command saved to: $PREVIOUS_COMMAND_PATH" + else + echo "Failed to save previous statusline command to: $PREVIOUS_COMMAND_PATH" >&2 + exit 1 + fi +fi +``` + +**Windows (PowerShell)**: +```powershell +$claudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" } +$pluginDir = Join-Path $claudeDir "plugins\balance-hud" +if (-not (Test-Path $pluginDir)) { New-Item -ItemType Directory -Force -Path $pluginDir | Out-Null } +if ($existingCommand -ne "") { + $previousCommandPath = Join-Path $pluginDir "previous-statusline.txt" + [System.IO.File]::WriteAllText($previousCommandPath, $existingCommand, (New-Object System.Text.UTF8Encoding $false)) + try { + $acl = Get-Acl $previousCommandPath + $acl.SetAccessRuleProtection($true, $false) + $rule = New-Object System.Security.AccessControl.FileSystemAccessRule( + [System.Security.Principal.WindowsIdentity]::GetCurrent().Name, + "FullControl", + "Allow" + ) + $acl.SetAccessRule($rule) + Set-Acl -Path $previousCommandPath -AclObject $acl + } catch { + Write-Warning "Saved previous statusline command, but could not tighten file ACLs: $($_.Exception.Message)" + } +} +``` + +--- + +## Step 3: Apply Configuration + +Read the settings file and merge in the statusLine config, preserving all existing settings: +- **Platform `darwin` or `linux`, or Platform `win32` + Shell `bash`**: `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json` +- **Platform `win32` + Shell `powershell`, `pwsh`, or `cmd`**: `settings.json` inside `$env:CLAUDE_CONFIG_DIR` when set, otherwise `Join-Path $HOME ".claude"` + +If the file doesn't exist, create it. If it contains invalid JSON, report the error and do not overwrite. +If a write fails with `File has been unexpectedly modified`, re-read the file and retry the merge once. + +**A timestamped backup was already created in Step 2.5.3.** If Step 2.5.4 prompted the user and they chose "Keep" or "Cancel", do not reach this step — setup has already exited. + +```json +{ + "statusLine": { + "type": "command", + "command": "{GENERATED_COMMAND}" + } +} +``` + +**JSON safety**: Write `settings.json` with a real JSON serializer or editor API, not manual string concatenation. +If you must inspect the saved JSON manually, the embedded bash command must preserve escaped backslashes inside the awk fragment. +For example, the saved JSON should contain `\\$(NF-1)` and `\\$0`, not `\$(NF-1)` and `\$0`. + +**Windows PowerShell 5.1 BOM**: on Windows PowerShell 5.1 (the default shell on Windows 10/11), `Set-Content -Encoding UTF8` and `Out-File -Encoding UTF8` emit a UTF-8 BOM (`EF BB BF`). RFC 8259 §8.1 forbids BOM in JSON. PowerShell 7+ added `-Encoding utf8NoBOM`, but PS 5.1 did not. Use `[System.IO.File]::WriteAllText` with `New-Object System.Text.UTF8Encoding $false` to write UTF-8 without a BOM from both PS versions: + +```powershell +[System.IO.File]::WriteAllText($path, $json, (New-Object System.Text.UTF8Encoding $false)) +``` + +Verify the first bytes are `7B 0D 0A` (`{` + CRLF) or `7B 0A` (`{` + LF), not `EF BB BF`: + +```powershell +[System.IO.File]::ReadAllBytes($path)[0..2] +``` + + +After successfully writing the config, tell the user: + +> ✅ Config written. **Please restart Claude Code now** — quit and run `claude` again in your terminal. +> Once restarted, run `/balance-hud:setup` again to complete Step 4 and verify the HUD is working. + +**Windows note**: Keep the restart guidance separate from runtime installation guidance. +- If the user just installed Node.js, they should restart their shell first so `node` is available in `PATH`. +- After `statusLine` is written successfully, they should fully quit Claude Code and launch a fresh session before judging whether the HUD setup worked. + +**Note**: The generated command dynamically finds and runs the latest installed plugin version. Updates are automatic - no need to re-run setup after plugin updates. If the HUD suddenly stops working, re-run `/balance-hud:setup` to verify the plugin is still installed. + +**Restoring a previous statusline**: If the user previously had a different statusline and wants to restore it, use the backup path printed in Step 2.5.3. The previous command is stored in `~/.claude/plugins/balance-hud/previous-statusline.txt`. To restore: +1. Find the most recent backup: `ls -t ~/.claude/settings.json.bak.* | head -1` +2. Copy it back: `cp ~/.claude/settings.json.bak.{timestamp} ~/.claude/settings.json` +3. Restart Claude Code. + +## Step 4: Optional Features + +After the statusLine is applied, ask the user if they'd like to enable additional HUD features beyond the default 2-line display. + +Use AskUserQuestion: +- header: "Extras" +- question: "Enable any optional HUD features? (all hidden by default)" +- multiSelect: true +- options: + - "Tools activity" — Shows running/completed tools (◐ Edit: file.ts | ✓ Read ×3) + - "Agents & Todos" — Shows subagent status and todo progress + - "Session info" — Shows session duration and config counts (CLAUDE.md, rules, MCPs) + - "Session name" — Shows session slug or custom title from /rename + - "Custom line" — Display a custom phrase in the HUD + +**If user selects any options**, write `plugins/balance-hud/config.json` inside the Claude config directory (`${CLAUDE_CONFIG_DIR:-$HOME/.claude}` on bash, `$env:CLAUDE_CONFIG_DIR` or `Join-Path $HOME ".claude"` on PowerShell). Create directories if needed: + +| Selection | Config keys | +|-----------|------------| +| Tools activity | `display.showTools: true` | +| Agents & Todos | `display.showAgents: true, display.showTodos: true` | +| Session info | `display.showDuration: true, display.showConfigCounts: true` | +| Session name | `display.showSessionName: true` | +| Custom line | `display.customLine: ""` — ask user for the text (max 80 chars) | + +Merge with existing config if the file already exists. Only write keys the user selected — don't write `false` for unselected items (defaults handle that). + +**If user selects nothing** (or picks "Other" and says skip/none), do not create a config file. The defaults are fine. + +--- + +## Step 5: Verify & Finish + +**First, confirm the user has restarted Claude Code** since Step 3 wrote the config. If they haven't, ask them to restart before proceeding — the HUD cannot appear in the same session where setup was run. + +Use AskUserQuestion: +- Question: "Setup complete! The HUD should appear below your input field. Is it working?" +- Options: "Yes, it's working" / "No, something's wrong" + +**If yes**: Ask the user if they'd like to ⭐ star the balance-hud repository on GitHub to support the project. If they agree and `gh` CLI is available, first check whether their `gh` version supports `gh repo star`. If it does, run `gh repo star jarrodwatts/balance-hud`. Otherwise fall back to `gh api -X PUT /user/starred/jarrodwatts/balance-hud`. Only run the star command if they explicitly say yes. + +**If no**: Debug systematically: + +1. **Restart Claude Code** (most common cause on macOS): + - The statusLine config requires a restart to take effect + - Quit Claude Code completely and run `claude` again, then re-run `/balance-hud:setup` to verify + - If you've already restarted, continue below + +2. **Verify config was applied**: + - Read settings file (`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json` on bash, or `settings.json` inside `$env:CLAUDE_CONFIG_DIR` when set, otherwise `Join-Path $HOME ".claude"` on PowerShell) + - Check statusLine.command exists and looks correct + - If command contains a hardcoded version path (not using the dynamic version-lookup command), it may be a stale config from a previous setup + +3. **Test the command manually** and capture error output: + ```bash + {GENERATED_COMMAND} 2>&1 + ``` + +4. **Common issues to check**: + + **"command not found" or empty output**: + - Runtime path might be wrong: `ls -la {RUNTIME_PATH}` + - On macOS with mise/nvm/asdf: the absolute path may have changed after a runtime update + - Symlinks may be stale: `command -v node` often returns a symlink that can break after version updates + - Solution: re-detect the runtime path (`command -v node` on Windows, `command -v bun` or `command -v node` on macOS/Linux), and verify with `realpath {RUNTIME_PATH}` (or `readlink -f {RUNTIME_PATH}`) to get the true absolute path + + **"No such file or directory" for plugin**: + - Plugin might not be installed: `ls "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/*/balance-hud/` + - Solution: reinstall plugin via marketplace + + **Windows shell mismatch (for example, "bash not recognized")**: + - Command format does not match `Platform:` + `Shell:` + - Solution: re-run Step 1 branch logic and use the matching variant + + **Windows: HUD shows only "initializing..." with no error (PowerShell shell, MSYS/Cygwin command environment)**: + - Root cause: `Shell: powershell` with `$OSTYPE=msys` or `$OSTYPE=cygwin`, causing bash to process the command before PowerShell + - Check: run `echo $OSTYPE` in the Bash tool — if it returns `msys` or `cygwin`, this is the issue + - Solution: re-run setup; when OSTYPE is `msys`/`cygwin`, follow the Windows + Git Bash path in Step 1 + + **Windows + PowerShell: HUD silent or "initializing..." with no error in any log (OSTYPE is not msys/cygwin)**: + - Symptoms: HUD stays at "initializing..." or shows nothing. Running the generated command interactively in a PowerShell prompt produces the expected HUD output, but the version invoked through Claude Code does not. + - Root cause: older setup commands used a PowerShell wrapper on every refresh. That path could fail when `[Console]::WindowWidth` threw `System.IO.IOException: The handle is invalid.`, when the cache glob resolved the `balance-hud` directory instead of a version directory, or when PowerShell startup exceeded the render cadence. + - Check: pipe stdin through `cmd.exe` to mirror Claude Code's invocation: + ```powershell + '{}' | & cmd.exe /c '{GENERATED_COMMAND}' + ``` + If you see either error, the existing setup predates the Node launcher format. Re-run `/balance-hud:setup` to regenerate `statusline.mjs` and a `cmd.exe`-launched command. See [#521](https://github.com/jarrodwatts/balance-hud/issues/521). + + **Windows: PowerShell execution policy error**: + - Run: `Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned` + + **Permission denied**: + - Runtime not executable: `chmod +x {RUNTIME_PATH}` + + **WSL confusion**: + - If using WSL, ensure plugin is installed in Linux environment, not Windows + - Check: `ls "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/*/balance-hud/` + +5. **If still stuck**: Show the user the exact command that was generated and the error, so they can report it or debug further diff --git a/plugins/balance-hud/config.json b/plugins/balance-hud/config.json new file mode 100644 index 0000000..128e0d7 --- /dev/null +++ b/plugins/balance-hud/config.json @@ -0,0 +1,86 @@ +{ + "language": "en", + "lineLayout": "expanded", + "showSeparators": false, + "pathLevels": 1, + "gitStatus": { + "enabled": true, + "showDirty": true, + "showAheadBehind": false, + "showFileStats": false, + "branchOverflow": "truncate", + "pushWarningThreshold": 0, + "pushCriticalThreshold": 0 + }, + "display": { + "showModel": true, + "showProject": true, + "showContextBar": true, + "contextValue": "percent", + "showConfigCounts": false, + "showCost": false, + "showDuration": false, + "showSpeed": false, + "showTokenBreakdown": true, + "showUsage": true, + "usageValue": "percent", + "usageBarEnabled": false, + "showResetLabel": true, + "usageCompact": false, + "showTools": false, + "showSkills": false, + "showMcp": false, + "toolNameMaxLength": 0, + "toolsMaxVisible": 4, + "showAgents": false, + "showTodos": false, + "showSessionName": false, + "showClaudeCodeVersion": false, + "showEffortLevel": false, + "showMemoryUsage": false, + "showPromptCache": false, + "promptCacheTtlSeconds": 300, + "showSessionTokens": false, + "showOutputStyle": false, + "showSessionStartDate": false, + "showLastResponseAt": false, + "showCompactions": false, + "mergeGroups": [], + "autocompactBuffer": "enabled", + "contextWarningThreshold": 70, + "contextCriticalThreshold": 85, + "usageThreshold": 0, + "sevenDayThreshold": 80, + "environmentThreshold": 0, + "externalUsagePath": "", + "externalUsageWritePath": "", + "externalUsageFreshnessMs": 300000, + "modelFormat": "full", + "modelOverride": "", + "showProvider": false, + "providerName": "", + "customLine": "", + "customLinePosition": "last", + "timeFormat": "relative", + "showAdvisor": false, + "advisorOverride": "", + "autoCompactWindow": null, + "showAddedDirs": true, + "addedDirsLayout": "inline" + }, + "colors": { + "context": "brightGreen", + "usage": "brightMagenta", + "warning": "yellow", + "usageWarning": "brightMagenta", + "critical": "red", + "model": "cyan", + "project": "yellow", + "git": "magenta", + "gitBranch": "cyan", + "label": "dim", + "custom": 208, + "barFilled": "█", + "barEmpty": "░" + } +} diff --git a/plugins/balance-hud/dist/claude-config-dir.d.ts b/plugins/balance-hud/dist/claude-config-dir.d.ts new file mode 100644 index 0000000..99f7599 --- /dev/null +++ b/plugins/balance-hud/dist/claude-config-dir.d.ts @@ -0,0 +1,4 @@ +export declare function getClaudeConfigDir(homeDir: string): string; +export declare function getClaudeConfigJsonPath(homeDir: string): string; +export declare function getHudPluginDir(homeDir: string): string; +//# sourceMappingURL=claude-config-dir.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/claude-config-dir.d.ts.map b/plugins/balance-hud/dist/claude-config-dir.d.ts.map new file mode 100644 index 0000000..a4bb152 --- /dev/null +++ b/plugins/balance-hud/dist/claude-config-dir.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"claude-config-dir.d.ts","sourceRoot":"","sources":["../src/claude-config-dir.ts"],"names":[],"mappings":"AAYA,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/claude-config-dir.js b/plugins/balance-hud/dist/claude-config-dir.js new file mode 100644 index 0000000..1807651 --- /dev/null +++ b/plugins/balance-hud/dist/claude-config-dir.js @@ -0,0 +1,24 @@ +import * as path from 'node:path'; +function expandHomeDirPrefix(inputPath, homeDir) { + if (inputPath === '~') { + return homeDir; + } + if (inputPath.startsWith('~/') || inputPath.startsWith('~\\')) { + return path.join(homeDir, inputPath.slice(2)); + } + return inputPath; +} +export function getClaudeConfigDir(homeDir) { + const envConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim(); + if (!envConfigDir) { + return path.join(homeDir, '.claude'); + } + return path.resolve(expandHomeDirPrefix(envConfigDir, homeDir)); +} +export function getClaudeConfigJsonPath(homeDir) { + return `${getClaudeConfigDir(homeDir)}.json`; +} +export function getHudPluginDir(homeDir) { + return path.join(getClaudeConfigDir(homeDir), 'plugins', 'balance-hud'); +} +//# sourceMappingURL=claude-config-dir.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/claude-config-dir.js.map b/plugins/balance-hud/dist/claude-config-dir.js.map new file mode 100644 index 0000000..94361a4 --- /dev/null +++ b/plugins/balance-hud/dist/claude-config-dir.js.map @@ -0,0 +1 @@ +{"version":3,"file":"claude-config-dir.js","sourceRoot":"","sources":["../src/claude-config-dir.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,SAAS,mBAAmB,CAAC,SAAiB,EAAE,OAAe;IAC7D,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAC3D,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AACzE,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/config-reader.d.ts b/plugins/balance-hud/dist/config-reader.d.ts new file mode 100644 index 0000000..09c5fd1 --- /dev/null +++ b/plugins/balance-hud/dist/config-reader.d.ts @@ -0,0 +1,9 @@ +export interface ConfigCounts { + claudeMdCount: number; + rulesCount: number; + mcpCount: number; + hooksCount: number; + outputStyle?: string; +} +export declare function countConfigs(cwd?: string): Promise; +//# sourceMappingURL=config-reader.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/config-reader.d.ts.map b/plugins/balance-hud/dist/config-reader.d.ts.map new file mode 100644 index 0000000..bb93cea --- /dev/null +++ b/plugins/balance-hud/dist/config-reader.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"config-reader.d.ts","sourceRoot":"","sources":["../src/config-reader.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA6ZD,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAoCtE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/config-reader.js b/plugins/balance-hud/dist/config-reader.js new file mode 100644 index 0000000..3628c25 --- /dev/null +++ b/plugins/balance-hud/dist/config-reader.js @@ -0,0 +1,396 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { createHash } from 'node:crypto'; +import { createDebug } from './debug.js'; +import { getClaudeConfigDir, getClaudeConfigJsonPath, getHudPluginDir } from './claude-config-dir.js'; +const debug = createDebug('config'); +function getMcpServerNames(filePath) { + if (!fs.existsSync(filePath)) + return new Set(); + try { + const content = fs.readFileSync(filePath, 'utf8'); + const config = JSON.parse(content); + if (config.mcpServers && typeof config.mcpServers === 'object') { + return new Set(Object.keys(config.mcpServers)); + } + } + catch (error) { + debug(`Failed to read MCP servers from ${filePath}:`, error); + } + return new Set(); +} +function getDisabledMcpServers(filePath, key) { + if (!fs.existsSync(filePath)) + return new Set(); + try { + const content = fs.readFileSync(filePath, 'utf8'); + const config = JSON.parse(content); + if (Array.isArray(config[key])) { + const validNames = config[key].filter((s) => typeof s === 'string'); + if (validNames.length !== config[key].length) { + debug(`${key} in ${filePath} contains non-string values, ignoring them`); + } + return new Set(validNames); + } + } + catch (error) { + debug(`Failed to read ${key} from ${filePath}:`, error); + } + return new Set(); +} +function countMcpServersInFile(filePath, excludeFrom) { + const servers = getMcpServerNames(filePath); + if (excludeFrom) { + const exclude = getMcpServerNames(excludeFrom); + for (const name of exclude) { + servers.delete(name); + } + } + return servers.size; +} +function countHooksInFile(filePath) { + if (!fs.existsSync(filePath)) + return 0; + try { + const content = fs.readFileSync(filePath, 'utf8'); + const config = JSON.parse(content); + if (config.hooks && typeof config.hooks === 'object') { + return Object.keys(config.hooks).length; + } + } + catch (error) { + debug(`Failed to read hooks from ${filePath}:`, error); + } + return 0; +} +function readStringSetting(filePath, key) { + if (!fs.existsSync(filePath)) + return undefined; + try { + const content = fs.readFileSync(filePath, 'utf8'); + const config = JSON.parse(content); + if (typeof config[key] === 'string') { + const value = config[key].trim(); + return value.length > 0 ? value : undefined; + } + } + catch (error) { + debug(`Failed to read ${key} from ${filePath}:`, error); + } + return undefined; +} +function countRulesInDir(rulesDir) { + if (!fs.existsSync(rulesDir)) + return 0; + let count = 0; + try { + const entries = fs.readdirSync(rulesDir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(rulesDir, entry.name); + if (entry.isDirectory()) { + count += countRulesInDir(fullPath); + } + else if (entry.isFile() && entry.name.endsWith('.md')) { + count++; + } + } + } + catch (error) { + debug(`Failed to read rules from ${rulesDir}:`, error); + } + return count; +} +function normalizePathForComparison(inputPath) { + let normalized = path.normalize(path.resolve(inputPath)); + const root = path.parse(normalized).root; + while (normalized.length > root.length && normalized.endsWith(path.sep)) { + normalized = normalized.slice(0, -1); + } + return process.platform === 'win32' ? normalized.toLowerCase() : normalized; +} +function pathsReferToSameLocation(pathA, pathB) { + if (normalizePathForComparison(pathA) === normalizePathForComparison(pathB)) { + return true; + } + if (!fs.existsSync(pathA) || !fs.existsSync(pathB)) { + return false; + } + try { + const realPathA = fs.realpathSync.native(pathA); + const realPathB = fs.realpathSync.native(pathB); + return normalizePathForComparison(realPathA) === normalizePathForComparison(realPathB); + } + catch (err) { + debug('Failed to compare paths %s and %s:', pathA, pathB, err instanceof Error ? err.message : err); + return false; + } +} +function getConfigCachePath(cwd, claudeConfigDir, homeDir) { + const identity = JSON.stringify({ cwd, claudeConfigDir }); + const hash = createHash('sha256').update(identity).digest('hex'); + return path.join(getHudPluginDir(homeDir), 'config-cache', `${hash}.json`); +} +function statSentinel(filePath) { + try { + const stat = fs.statSync(filePath); + return { mtimeMs: stat.mtimeMs, size: stat.size }; + } + catch (err) { + debug('Failed to stat sentinel %s:', filePath, err instanceof Error ? err.message : err); + return null; + } +} +function buildSentinelPaths(claudeDir, claudeConfigJsonPath, cwd) { + // Note: We sentinel CLAUDE.md directly instead of claudeDir because the + // cache itself is stored under claudeDir/plugins/, which would change + // claudeDir's mtime and immediately invalidate the cache on every write. + const paths = [ + path.join(claudeDir, 'CLAUDE.md'), + path.join(claudeDir, 'rules'), + path.join(claudeDir, 'settings.json'), + path.join(claudeDir, 'settings.local.json'), + claudeConfigJsonPath, + ]; + if (cwd) { + paths.push(cwd, path.join(cwd, '.claude'), path.join(cwd, '.claude', 'rules'), path.join(cwd, '.mcp.json'), path.join(cwd, '.claude', 'settings.json'), path.join(cwd, '.claude', 'settings.local.json')); + } + return paths; +} +function collectRuleDirectorySentinels(rulesDir) { + if (!fs.existsSync(rulesDir)) + return []; + const sentinels = [rulesDir]; + try { + const entries = fs.readdirSync(rulesDir, { withFileTypes: true }); + for (const entry of entries) { + if (!entry.isDirectory()) { + continue; + } + sentinels.push(...collectRuleDirectorySentinels(path.join(rulesDir, entry.name))); + } + } + catch (error) { + debug(`Failed to read rule sentinel paths from ${rulesDir}:`, error); + } + return sentinels; +} +function statSentinels(paths) { + const result = {}; + for (const p of paths) { + result[p] = statSentinel(p); + } + return result; +} +function ensurePrivateDir(dir) { + fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); + try { + fs.chmodSync(dir, 0o700); + } + catch { + // Best-effort: some filesystems do not support POSIX modes. + } +} +function sentinelsMatch(a, b) { + const keysA = Object.keys(a); + const keysB = Object.keys(b); + if (keysA.length !== keysB.length) + return false; + for (const key of keysA) { + const sa = a[key]; + const sb = b[key]; + if (sa === null && sb === null) + continue; + if (sa === null || sb === null) + return false; + if (sa.mtimeMs !== sb.mtimeMs || sa.size !== sb.size) + return false; + } + return true; +} +function isConfigCounts(value) { + if (!value || typeof value !== 'object') { + return false; + } + const counts = value; + return (typeof counts.claudeMdCount === 'number' + && Number.isFinite(counts.claudeMdCount) + && counts.claudeMdCount >= 0 + && typeof counts.rulesCount === 'number' + && Number.isFinite(counts.rulesCount) + && counts.rulesCount >= 0 + && typeof counts.mcpCount === 'number' + && Number.isFinite(counts.mcpCount) + && counts.mcpCount >= 0 + && typeof counts.hooksCount === 'number' + && Number.isFinite(counts.hooksCount) + && counts.hooksCount >= 0 + && (counts.outputStyle === undefined || typeof counts.outputStyle === 'string')); +} +function readConfigCache(cacheKey, homeDir) { + try { + const cachePath = getConfigCachePath(cacheKey.cwd, cacheKey.claudeConfigDir, homeDir); + const raw = fs.readFileSync(cachePath, 'utf8'); + const parsed = JSON.parse(raw); + if (parsed.key?.cwd !== cacheKey.cwd || parsed.key?.claudeConfigDir !== cacheKey.claudeConfigDir) { + return null; + } + if (!isConfigCounts(parsed.data)) { + return null; + } + return parsed; + } + catch (err) { + debug('Failed to read config cache:', err instanceof Error ? err.message : err); + return null; + } +} +function writeConfigCache(key, data, homeDir) { + try { + const cachePath = getConfigCachePath(key.cwd, key.claudeConfigDir, homeDir); + ensurePrivateDir(path.dirname(cachePath)); + const payload = { key, data }; + fs.writeFileSync(cachePath, JSON.stringify(payload), { encoding: 'utf8', mode: 0o600 }); + try { + fs.chmodSync(cachePath, 0o600); + } + catch { + // Best-effort: some filesystems do not support POSIX modes. + } + } + catch (err) { + debug('Failed to write config cache:', err instanceof Error ? err.message : err); + } +} +function computeConfigCountsFresh(cwd) { + let claudeMdCount = 0; + let rulesCount = 0; + let hooksCount = 0; + let outputStyle; + const homeDir = os.homedir(); + const claudeDir = getClaudeConfigDir(homeDir); + // Collect all MCP servers across scopes, then subtract disabled ones + const userMcpServers = new Set(); + const projectMcpServers = new Set(); + // === USER SCOPE === + // ~/.claude/CLAUDE.md + if (fs.existsSync(path.join(claudeDir, 'CLAUDE.md'))) { + claudeMdCount++; + } + // ~/.claude/rules/*.md + rulesCount += countRulesInDir(path.join(claudeDir, 'rules')); + // ~/.claude/settings.json (MCPs and hooks) + const userSettings = path.join(claudeDir, 'settings.json'); + for (const name of getMcpServerNames(userSettings)) { + userMcpServers.add(name); + } + hooksCount += countHooksInFile(userSettings); + outputStyle = readStringSetting(userSettings, 'outputStyle'); + const userLocalSettings = path.join(claudeDir, 'settings.local.json'); + outputStyle = readStringSetting(userLocalSettings, 'outputStyle') ?? outputStyle; + // {CLAUDE_CONFIG_DIR}.json (additional user-scope MCPs) + const userClaudeJson = getClaudeConfigJsonPath(homeDir); + for (const name of getMcpServerNames(userClaudeJson)) { + userMcpServers.add(name); + } + // Get disabled user-scope MCPs from ~/.claude.json + const disabledUserMcps = getDisabledMcpServers(userClaudeJson, 'disabledMcpServers'); + for (const name of disabledUserMcps) { + userMcpServers.delete(name); + } + // === PROJECT SCOPE === + // Avoid double-counting when project .claude directory is the same location as user scope. + const projectClaudeDir = cwd ? path.join(cwd, '.claude') : null; + const projectClaudeOverlapsUserScope = projectClaudeDir + ? pathsReferToSameLocation(projectClaudeDir, claudeDir) + : false; + if (cwd) { + // {cwd}/CLAUDE.md + if (fs.existsSync(path.join(cwd, 'CLAUDE.md'))) { + claudeMdCount++; + } + // {cwd}/CLAUDE.local.md + if (fs.existsSync(path.join(cwd, 'CLAUDE.local.md'))) { + claudeMdCount++; + } + // {cwd}/.claude/CLAUDE.md (alternative location, skip when it is user scope) + if (!projectClaudeOverlapsUserScope && fs.existsSync(path.join(cwd, '.claude', 'CLAUDE.md'))) { + claudeMdCount++; + } + // {cwd}/.claude/CLAUDE.local.md + if (fs.existsSync(path.join(cwd, '.claude', 'CLAUDE.local.md'))) { + claudeMdCount++; + } + // {cwd}/.claude/rules/*.md (recursive) + // Skip when it overlaps with user-scope rules. + if (!projectClaudeOverlapsUserScope) { + rulesCount += countRulesInDir(path.join(cwd, '.claude', 'rules')); + } + // {cwd}/.mcp.json (project MCP config) - tracked separately for disabled filtering + const mcpJsonServers = getMcpServerNames(path.join(cwd, '.mcp.json')); + // {cwd}/.claude/settings.json (project settings) + // Skip when it overlaps with user-scope settings. + const projectSettings = path.join(cwd, '.claude', 'settings.json'); + if (!projectClaudeOverlapsUserScope) { + for (const name of getMcpServerNames(projectSettings)) { + projectMcpServers.add(name); + } + hooksCount += countHooksInFile(projectSettings); + outputStyle = readStringSetting(projectSettings, 'outputStyle') ?? outputStyle; + } + // {cwd}/.claude/settings.local.json (local project settings) + const localSettings = path.join(cwd, '.claude', 'settings.local.json'); + for (const name of getMcpServerNames(localSettings)) { + projectMcpServers.add(name); + } + hooksCount += countHooksInFile(localSettings); + outputStyle = readStringSetting(localSettings, 'outputStyle') ?? outputStyle; + // Get disabled .mcp.json servers from settings.local.json + const disabledMcpJsonServers = getDisabledMcpServers(localSettings, 'disabledMcpjsonServers'); + for (const name of disabledMcpJsonServers) { + mcpJsonServers.delete(name); + } + // Add remaining .mcp.json servers to project set + for (const name of mcpJsonServers) { + projectMcpServers.add(name); + } + } + // Total MCP count = user servers + project servers + // Note: Deduplication only occurs within each scope, not across scopes. + // A server with the same name in both user and project scope counts as 2 (separate configs). + const mcpCount = userMcpServers.size + projectMcpServers.size; + return { claudeMdCount, rulesCount, mcpCount, hooksCount, outputStyle }; +} +export async function countConfigs(cwd) { + const homeDir = os.homedir(); + const claudeDir = getClaudeConfigDir(homeDir); + const claudeConfigJsonPath = getClaudeConfigJsonPath(homeDir); + const normalizedCwd = cwd ? path.resolve(cwd) : null; + const staticSentinelPaths = buildSentinelPaths(claudeDir, claudeConfigJsonPath, normalizedCwd); + const cached = readConfigCache({ cwd: normalizedCwd, claudeConfigDir: claudeDir }, homeDir); + const cacheValidationPaths = cached + ? Array.from(new Set([...staticSentinelPaths, ...Object.keys(cached.key.sentinels)])) + : staticSentinelPaths; + const currentSentinels = statSentinels(cacheValidationPaths); + if (cached && sentinelsMatch(cached.key.sentinels, currentSentinels)) { + return cached.data; + } + const result = computeConfigCountsFresh(cwd); + const ruleSentinelPaths = collectRuleDirectorySentinels(path.join(claudeDir, 'rules')); + const projectClaudeDir = normalizedCwd ? path.join(normalizedCwd, '.claude') : null; + const projectClaudeOverlapsUserScope = projectClaudeDir + ? pathsReferToSameLocation(projectClaudeDir, claudeDir) + : false; + if (normalizedCwd && !projectClaudeOverlapsUserScope) { + ruleSentinelPaths.push(...collectRuleDirectorySentinels(path.join(normalizedCwd, '.claude', 'rules'))); + } + const cacheSentinelPaths = Array.from(new Set([...staticSentinelPaths, ...ruleSentinelPaths])); + const cacheKey = { + cwd: normalizedCwd, + claudeConfigDir: claudeDir, + sentinels: statSentinels(cacheSentinelPaths), + }; + writeConfigCache(cacheKey, result, homeDir); + return result; +} +//# sourceMappingURL=config-reader.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/config-reader.js.map b/plugins/balance-hud/dist/config-reader.js.map new file mode 100644 index 0000000..6842e74 --- /dev/null +++ b/plugins/balance-hud/dist/config-reader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"config-reader.js","sourceRoot":"","sources":["../src/config-reader.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/config.d.ts b/plugins/balance-hud/dist/config.d.ts new file mode 100644 index 0000000..847a073 --- /dev/null +++ b/plugins/balance-hud/dist/config.d.ts @@ -0,0 +1,118 @@ +import type { Language } from './i18n/types.js'; +export type LineLayoutType = 'compact' | 'expanded'; +export type AutocompactBufferMode = 'enabled' | 'disabled'; +export type ContextValueMode = 'percent' | 'tokens' | 'remaining' | 'both'; +export type UsageValueMode = 'percent' | 'remaining'; +export type GitBranchOverflowMode = 'truncate' | 'wrap'; +/** + * Controls how the model name is displayed in the HUD badge. + * + * full: Show the raw display name as-is (e.g. "Opus 4.6 (1M context)") + * compact: Strip redundant context-window suffix (e.g. "Opus 4.6") + * short: Strip context suffix AND "Claude " prefix (e.g. "Opus 4.6") + */ +export type ModelFormatMode = 'full' | 'compact' | 'short'; +export type TimeFormatMode = 'relative' | 'absolute' | 'both' | 'elapsed' | 'elapsedAndAbsolute'; +export type CustomLinePosition = 'first' | 'last'; +export type HudElement = 'project' | 'addedDirs' | 'context' | 'usage' | 'promptCache' | 'memory' | 'environment' | 'tools' | 'skills' | 'mcp' | 'agents' | 'todos' | 'sessionTime'; +export type AddedDirsLayout = 'inline' | 'line'; +export type HudColorName = 'dim' | 'red' | 'green' | 'yellow' | 'magenta' | 'cyan' | 'brightBlue' | 'brightMagenta'; +/** A color value: named preset, 256-color index (0-255), or hex string (#rrggbb). */ +export type HudColorValue = HudColorName | number | string; +export interface HudColorOverrides { + context: HudColorValue; + usage: HudColorValue; + warning: HudColorValue; + usageWarning: HudColorValue; + critical: HudColorValue; + model: HudColorValue; + project: HudColorValue; + git: HudColorValue; + gitBranch: HudColorValue; + label: HudColorValue; + custom: HudColorValue; + barFilled: string; + barEmpty: string; +} +export declare const DEFAULT_ELEMENT_ORDER: HudElement[]; +export declare const DEFAULT_MERGE_GROUPS: HudElement[][]; +export interface HudConfig { + language: Language; + lineLayout: LineLayoutType; + showSeparators: boolean; + pathLevels: 1 | 2 | 3; + maxWidth: number | null; + forceMaxWidth: boolean; + elementOrder: HudElement[]; + gitStatus: { + enabled: boolean; + showDirty: boolean; + showAheadBehind: boolean; + showFileStats: boolean; + branchOverflow: GitBranchOverflowMode; + pushWarningThreshold: number; + pushCriticalThreshold: number; + }; + display: { + showModel: boolean; + showProject: boolean; + showAddedDirs: boolean; + addedDirsLayout: AddedDirsLayout; + showContextBar: boolean; + contextValue: ContextValueMode; + showConfigCounts: boolean; + showCost: boolean; + showDuration: boolean; + showSpeed: boolean; + showTokenBreakdown: boolean; + showUsage: boolean; + usageValue: UsageValueMode; + usageBarEnabled: boolean; + showResetLabel: boolean; + usageCompact: boolean; + showTools: boolean; + showSkills: boolean; + showMcp: boolean; + toolNameMaxLength: number; + toolsMaxVisible: number; + showAgents: boolean; + showTodos: boolean; + showSessionName: boolean; + showClaudeCodeVersion: boolean; + showEffortLevel: boolean; + showMemoryUsage: boolean; + showPromptCache: boolean; + promptCacheTtlSeconds: number; + showSessionTokens: boolean; + showOutputStyle: boolean; + showSessionStartDate: boolean; + showLastResponseAt: boolean; + showCompactions: boolean; + mergeGroups: HudElement[][]; + autocompactBuffer: AutocompactBufferMode; + contextWarningThreshold: number; + contextCriticalThreshold: number; + usageThreshold: number; + sevenDayThreshold: number; + environmentThreshold: number; + externalUsagePath: string; + externalUsageWritePath: string; + externalUsageFreshnessMs: number; + modelFormat: ModelFormatMode; + modelOverride: string; + showProvider: boolean; + providerName: string; + customLine: string; + customLinePosition: CustomLinePosition; + timeFormat: TimeFormatMode; + showAdvisor: boolean; + advisorOverride: string; + autoCompactWindow: number | null; + }; + colors: HudColorOverrides; +} +export declare const DEFAULT_CONFIG: HudConfig; +export declare function getConfigPath(): string; +export declare function mergeConfig(userConfig: Partial): HudConfig; +export declare function loadConfig(): Promise; +//# sourceMappingURL=config.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/config.d.ts.map b/plugins/balance-hud/dist/config.d.ts.map new file mode 100644 index 0000000..b4367e7 --- /dev/null +++ b/plugins/balance-hud/dist/config.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/config.js b/plugins/balance-hud/dist/config.js new file mode 100644 index 0000000..3982ebb --- /dev/null +++ b/plugins/balance-hud/dist/config.js @@ -0,0 +1,551 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { getHudPluginDir } from './claude-config-dir.js'; +import { createDebug } from './debug.js'; +const debug = createDebug('config'); +export const DEFAULT_ELEMENT_ORDER = [ + 'project', + 'addedDirs', + 'context', + 'usage', + 'promptCache', + 'memory', + 'environment', + 'tools', + 'skills', + 'mcp', + 'agents', + 'todos', + 'sessionTime', +]; +export const DEFAULT_MERGE_GROUPS = []; +const KNOWN_ELEMENTS = new Set(DEFAULT_ELEMENT_ORDER); +export const DEFAULT_CONFIG = { + language: 'en', + lineLayout: 'expanded', + showSeparators: false, + pathLevels: 1, + maxWidth: null, + forceMaxWidth: false, + elementOrder: [...DEFAULT_ELEMENT_ORDER], + gitStatus: { + enabled: true, + showDirty: true, + showAheadBehind: false, + showFileStats: false, + branchOverflow: 'truncate', + pushWarningThreshold: 0, + pushCriticalThreshold: 0, + }, + display: { + showModel: true, + showProject: true, + showAddedDirs: true, + addedDirsLayout: 'inline', + showContextBar: true, + contextValue: 'percent', + showConfigCounts: false, + showCost: false, + showDuration: false, + showSpeed: false, + showTokenBreakdown: true, + showUsage: true, + usageValue: 'percent', + usageBarEnabled: true, + showResetLabel: true, + usageCompact: false, + showTools: false, + showSkills: false, + showMcp: false, + toolNameMaxLength: 0, + toolsMaxVisible: 4, + showAgents: false, + showTodos: false, + showSessionName: false, + showClaudeCodeVersion: false, + showEffortLevel: false, + showMemoryUsage: false, + showPromptCache: false, + promptCacheTtlSeconds: 300, + showSessionTokens: false, + showOutputStyle: false, + showSessionStartDate: false, + showLastResponseAt: false, + showCompactions: false, + mergeGroups: DEFAULT_MERGE_GROUPS.map(group => [...group]), + autocompactBuffer: 'enabled', + contextWarningThreshold: 70, + contextCriticalThreshold: 85, + usageThreshold: 0, + sevenDayThreshold: 80, + environmentThreshold: 0, + externalUsagePath: '', + externalUsageWritePath: '', + externalUsageFreshnessMs: 300000, + modelFormat: 'full', + modelOverride: '', + showProvider: false, + providerName: '', + customLine: '', + customLinePosition: 'last', + timeFormat: 'relative', + showAdvisor: false, + advisorOverride: '', + autoCompactWindow: null, + }, + colors: { + context: 'brightGreen', + usage: 'brightBlue', + warning: 'yellow', + usageWarning: 'brightMagenta', + critical: 'red', + model: 'cyan', + project: 'yellow', + git: 'magenta', + gitBranch: 'cyan', + label: 'dim', + custom: 208, + barFilled: '█', + barEmpty: '░', + }, +}; +export function getConfigPath() { + const homeDir = os.homedir(); + return path.join(getHudPluginDir(homeDir), 'config.json'); +} +function validatePathLevels(value) { + return value === 1 || value === 2 || value === 3; +} +function validateLineLayout(value) { + return value === 'compact' || value === 'expanded'; +} +function validateAutocompactBuffer(value) { + return value === 'enabled' || value === 'disabled'; +} +function validateGitBranchOverflow(value) { + return value === 'truncate' || value === 'wrap'; +} +function validateContextValue(value) { + return value === 'percent' || value === 'tokens' || value === 'remaining' || value === 'both'; +} +function validateUsageValue(value) { + return value === 'percent' || value === 'remaining'; +} +function validateLanguage(value) { + return value === 'en' || value === 'zh' || value === 'zh-Hans'; +} +function validateModelFormat(value) { + return value === 'full' || value === 'compact' || value === 'short'; +} +function validateTimeFormat(value) { + return value === 'relative' + || value === 'absolute' + || value === 'both' + || value === 'elapsed' + || value === 'elapsedAndAbsolute'; +} +function validateCustomLinePosition(value) { + return value === 'first' || value === 'last'; +} +function validateColorName(value) { + return value === 'dim' + || value === 'red' + || value === 'green' + || value === 'yellow' + || value === 'magenta' + || value === 'cyan' + || value === 'brightBlue' + || value === 'brightGreen' + || value === 'brightMagenta'; +} +const UNSAFE_CODEPOINT = /[\p{Cc}\p{Cf}\p{Variation_Selector}\p{Zl}\p{Zp}\p{Cn}]/u; +function validateBarChar(value) { + if (typeof value !== 'string' || value.length === 0) + return false; + const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' }); + if (Array.from(segmenter.segment(value)).length !== 1) + return false; + for (const ch of value) { + if (UNSAFE_CODEPOINT.test(ch)) + return false; + } + return true; +} +const HEX_COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/; +function validateColorValue(value) { + if (validateColorName(value)) + return true; + if (typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 255) + return true; + if (typeof value === 'string' && HEX_COLOR_PATTERN.test(value)) + return true; + return false; +} +function validateElementOrder(value) { + if (!Array.isArray(value) || value.length === 0) { + return [...DEFAULT_ELEMENT_ORDER]; + } + const seen = new Set(); + const elementOrder = []; + for (const item of value) { + if (typeof item !== 'string' || !KNOWN_ELEMENTS.has(item)) { + continue; + } + const element = item; + if (seen.has(element)) { + continue; + } + seen.add(element); + elementOrder.push(element); + } + return elementOrder.length > 0 ? elementOrder : [...DEFAULT_ELEMENT_ORDER]; +} +function validateMergeGroups(value) { + if (!Array.isArray(value)) { + return DEFAULT_MERGE_GROUPS.map(group => [...group]); + } + if (value.length === 0) { + return []; + } + const usedElements = new Set(); + const mergeGroups = []; + for (const group of value) { + if (!Array.isArray(group)) { + continue; + } + const seenInGroup = new Set(); + const normalizedGroup = []; + const pendingElements = []; + for (const item of group) { + if (typeof item !== 'string' || !KNOWN_ELEMENTS.has(item)) { + continue; + } + const element = item; + if (seenInGroup.has(element) || usedElements.has(element)) { + continue; + } + seenInGroup.add(element); + normalizedGroup.push(element); + pendingElements.push(element); + } + if (normalizedGroup.length >= 2) { + for (const element of pendingElements) { + usedElements.add(element); + } + mergeGroups.push(normalizedGroup); + } + } + return mergeGroups.length > 0 + ? mergeGroups + : DEFAULT_MERGE_GROUPS.map(group => [...group]); +} +function migrateConfig(userConfig) { + const migrated = { ...userConfig }; + if ('layout' in userConfig && !('lineLayout' in userConfig)) { + if (typeof userConfig.layout === 'string') { + // Legacy string migration (v0.0.x → v0.1.x) + if (userConfig.layout === 'separators') { + migrated.lineLayout = 'compact'; + migrated.showSeparators = true; + } + else { + migrated.lineLayout = 'compact'; + migrated.showSeparators = false; + } + } + else if (typeof userConfig.layout === 'object' && userConfig.layout !== null) { + // Object layout written by third-party tools — extract nested fields + const obj = userConfig.layout; + if (typeof obj.lineLayout === 'string') + migrated.lineLayout = obj.lineLayout; + if (typeof obj.showSeparators === 'boolean') + migrated.showSeparators = obj.showSeparators; + if (typeof obj.pathLevels === 'number') + migrated.pathLevels = obj.pathLevels; + } + delete migrated.layout; + } + return migrated; +} +function validateThreshold(value, max = 100) { + if (typeof value !== 'number') + return 0; + return Math.max(0, Math.min(max, value)); +} +function validateContextThreshold(value, fallback) { + if (typeof value !== 'number' || !Number.isFinite(value)) + return fallback; + return Math.max(0, Math.min(100, value)); +} +function validateCountThreshold(value) { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return 0; + } + return Math.max(0, Math.floor(value)); +} +function validateDurationSeconds(value, fallback) { + if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) { + return fallback; + } + return Math.floor(value); +} +function validateNonNegativeInteger(value, fallback) { + if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) { + return fallback; + } + return value; +} +function validateAutoCompactWindow(value) { + if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) { + return null; + } + return value; +} +function validateOptionalPath(value) { + return typeof value === 'string' ? value.trim() : ''; +} +function validateFreshnessMs(value) { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return DEFAULT_CONFIG.display.externalUsageFreshnessMs; + } + return Math.max(0, Math.floor(value)); +} +export function mergeConfig(userConfig) { + const migrated = migrateConfig(userConfig); + const language = validateLanguage(migrated.language) + ? migrated.language + : DEFAULT_CONFIG.language; + const lineLayout = validateLineLayout(migrated.lineLayout) + ? migrated.lineLayout + : DEFAULT_CONFIG.lineLayout; + const showSeparators = typeof migrated.showSeparators === 'boolean' + ? migrated.showSeparators + : DEFAULT_CONFIG.showSeparators; + const pathLevels = validatePathLevels(migrated.pathLevels) + ? migrated.pathLevels + : DEFAULT_CONFIG.pathLevels; + const rawMaxWidth = migrated.maxWidth; + const maxWidth = (typeof rawMaxWidth === 'number' && Number.isFinite(rawMaxWidth) && rawMaxWidth > 0) + ? Math.floor(rawMaxWidth) + : null; + const elementOrder = validateElementOrder(migrated.elementOrder); + const forceMaxWidth = typeof migrated.forceMaxWidth === 'boolean' + ? migrated.forceMaxWidth + : DEFAULT_CONFIG.forceMaxWidth; + const gitStatus = { + enabled: typeof migrated.gitStatus?.enabled === 'boolean' + ? migrated.gitStatus.enabled + : DEFAULT_CONFIG.gitStatus.enabled, + showDirty: typeof migrated.gitStatus?.showDirty === 'boolean' + ? migrated.gitStatus.showDirty + : DEFAULT_CONFIG.gitStatus.showDirty, + showAheadBehind: typeof migrated.gitStatus?.showAheadBehind === 'boolean' + ? migrated.gitStatus.showAheadBehind + : DEFAULT_CONFIG.gitStatus.showAheadBehind, + showFileStats: typeof migrated.gitStatus?.showFileStats === 'boolean' + ? migrated.gitStatus.showFileStats + : DEFAULT_CONFIG.gitStatus.showFileStats, + branchOverflow: validateGitBranchOverflow(migrated.gitStatus?.branchOverflow) + ? migrated.gitStatus.branchOverflow + : DEFAULT_CONFIG.gitStatus.branchOverflow, + pushWarningThreshold: validateCountThreshold(migrated.gitStatus?.pushWarningThreshold), + pushCriticalThreshold: validateCountThreshold(migrated.gitStatus?.pushCriticalThreshold), + }; + const display = { + showModel: typeof migrated.display?.showModel === 'boolean' + ? migrated.display.showModel + : DEFAULT_CONFIG.display.showModel, + showProject: typeof migrated.display?.showProject === 'boolean' + ? migrated.display.showProject + : DEFAULT_CONFIG.display.showProject, + showAddedDirs: typeof migrated.display?.showAddedDirs === 'boolean' + ? migrated.display.showAddedDirs + : DEFAULT_CONFIG.display.showAddedDirs, + addedDirsLayout: (migrated.display?.addedDirsLayout === 'inline' || migrated.display?.addedDirsLayout === 'line') + ? migrated.display.addedDirsLayout + : DEFAULT_CONFIG.display.addedDirsLayout, + showContextBar: typeof migrated.display?.showContextBar === 'boolean' + ? migrated.display.showContextBar + : DEFAULT_CONFIG.display.showContextBar, + contextValue: validateContextValue(migrated.display?.contextValue) + ? migrated.display.contextValue + : DEFAULT_CONFIG.display.contextValue, + showConfigCounts: typeof migrated.display?.showConfigCounts === 'boolean' + ? migrated.display.showConfigCounts + : DEFAULT_CONFIG.display.showConfigCounts, + showCost: typeof migrated.display?.showCost === 'boolean' + ? migrated.display.showCost + : DEFAULT_CONFIG.display.showCost, + showDuration: typeof migrated.display?.showDuration === 'boolean' + ? migrated.display.showDuration + : DEFAULT_CONFIG.display.showDuration, + showSpeed: typeof migrated.display?.showSpeed === 'boolean' + ? migrated.display.showSpeed + : DEFAULT_CONFIG.display.showSpeed, + showTokenBreakdown: typeof migrated.display?.showTokenBreakdown === 'boolean' + ? migrated.display.showTokenBreakdown + : DEFAULT_CONFIG.display.showTokenBreakdown, + showUsage: typeof migrated.display?.showUsage === 'boolean' + ? migrated.display.showUsage + : DEFAULT_CONFIG.display.showUsage, + usageValue: validateUsageValue(migrated.display?.usageValue) + ? migrated.display.usageValue + : DEFAULT_CONFIG.display.usageValue, + usageBarEnabled: typeof migrated.display?.usageBarEnabled === 'boolean' + ? migrated.display.usageBarEnabled + : DEFAULT_CONFIG.display.usageBarEnabled, + showResetLabel: typeof migrated.display?.showResetLabel === 'boolean' + ? migrated.display.showResetLabel + : DEFAULT_CONFIG.display.showResetLabel, + usageCompact: typeof migrated.display?.usageCompact === 'boolean' + ? migrated.display.usageCompact + : DEFAULT_CONFIG.display.usageCompact, + showTools: typeof migrated.display?.showTools === 'boolean' + ? migrated.display.showTools + : DEFAULT_CONFIG.display.showTools, + showSkills: typeof migrated.display?.showSkills === 'boolean' + ? migrated.display.showSkills + : DEFAULT_CONFIG.display.showSkills, + showMcp: typeof migrated.display?.showMcp === 'boolean' + ? migrated.display.showMcp + : DEFAULT_CONFIG.display.showMcp, + toolNameMaxLength: validateNonNegativeInteger(migrated.display?.toolNameMaxLength, DEFAULT_CONFIG.display.toolNameMaxLength), + toolsMaxVisible: validateNonNegativeInteger(migrated.display?.toolsMaxVisible, DEFAULT_CONFIG.display.toolsMaxVisible), + showAgents: typeof migrated.display?.showAgents === 'boolean' + ? migrated.display.showAgents + : DEFAULT_CONFIG.display.showAgents, + showTodos: typeof migrated.display?.showTodos === 'boolean' + ? migrated.display.showTodos + : DEFAULT_CONFIG.display.showTodos, + showSessionName: typeof migrated.display?.showSessionName === 'boolean' + ? migrated.display.showSessionName + : DEFAULT_CONFIG.display.showSessionName, + showClaudeCodeVersion: typeof migrated.display?.showClaudeCodeVersion === 'boolean' + ? migrated.display.showClaudeCodeVersion + : DEFAULT_CONFIG.display.showClaudeCodeVersion, + showEffortLevel: typeof migrated.display?.showEffortLevel === 'boolean' + ? migrated.display.showEffortLevel + : DEFAULT_CONFIG.display.showEffortLevel, + showMemoryUsage: typeof migrated.display?.showMemoryUsage === 'boolean' + ? migrated.display.showMemoryUsage + : DEFAULT_CONFIG.display.showMemoryUsage, + showPromptCache: typeof migrated.display?.showPromptCache === 'boolean' + ? migrated.display.showPromptCache + : DEFAULT_CONFIG.display.showPromptCache, + promptCacheTtlSeconds: validateDurationSeconds(migrated.display?.promptCacheTtlSeconds, DEFAULT_CONFIG.display.promptCacheTtlSeconds), + showSessionTokens: typeof migrated.display?.showSessionTokens === 'boolean' + ? migrated.display.showSessionTokens + : DEFAULT_CONFIG.display.showSessionTokens, + showOutputStyle: typeof migrated.display?.showOutputStyle === 'boolean' + ? migrated.display.showOutputStyle + : DEFAULT_CONFIG.display.showOutputStyle, + showSessionStartDate: typeof migrated.display?.showSessionStartDate === 'boolean' + ? migrated.display.showSessionStartDate + : DEFAULT_CONFIG.display.showSessionStartDate, + showLastResponseAt: typeof migrated.display?.showLastResponseAt === 'boolean' + ? migrated.display.showLastResponseAt + : DEFAULT_CONFIG.display.showLastResponseAt, + showCompactions: typeof migrated.display?.showCompactions === 'boolean' + ? migrated.display.showCompactions + : DEFAULT_CONFIG.display.showCompactions, + mergeGroups: validateMergeGroups(migrated.display?.mergeGroups), + autocompactBuffer: validateAutocompactBuffer(migrated.display?.autocompactBuffer) + ? migrated.display.autocompactBuffer + : DEFAULT_CONFIG.display.autocompactBuffer, + contextWarningThreshold: validateContextThreshold(migrated.display?.contextWarningThreshold, DEFAULT_CONFIG.display.contextWarningThreshold), + contextCriticalThreshold: validateContextThreshold(migrated.display?.contextCriticalThreshold, DEFAULT_CONFIG.display.contextCriticalThreshold), + usageThreshold: validateThreshold(migrated.display?.usageThreshold, 100), + sevenDayThreshold: validateThreshold(migrated.display?.sevenDayThreshold, 100), + environmentThreshold: validateThreshold(migrated.display?.environmentThreshold, 100), + externalUsagePath: validateOptionalPath(migrated.display?.externalUsagePath), + externalUsageWritePath: validateOptionalPath(migrated.display?.externalUsageWritePath), + externalUsageFreshnessMs: validateFreshnessMs(migrated.display?.externalUsageFreshnessMs), + modelFormat: validateModelFormat(migrated.display?.modelFormat) + ? migrated.display.modelFormat + : DEFAULT_CONFIG.display.modelFormat, + modelOverride: typeof migrated.display?.modelOverride === 'string' + ? migrated.display.modelOverride.slice(0, 80) + : DEFAULT_CONFIG.display.modelOverride, + showProvider: typeof migrated.display?.showProvider === 'boolean' + ? migrated.display.showProvider + : DEFAULT_CONFIG.display.showProvider, + providerName: typeof migrated.display?.providerName === 'string' + ? migrated.display.providerName.slice(0, 40) + : DEFAULT_CONFIG.display.providerName, + customLine: typeof migrated.display?.customLine === 'string' + ? migrated.display.customLine.slice(0, 80) + : DEFAULT_CONFIG.display.customLine, + customLinePosition: validateCustomLinePosition(migrated.display?.customLinePosition) + ? migrated.display.customLinePosition + : DEFAULT_CONFIG.display.customLinePosition, + timeFormat: validateTimeFormat(migrated.display?.timeFormat) + ? migrated.display.timeFormat + : DEFAULT_CONFIG.display.timeFormat, + showAdvisor: typeof migrated.display?.showAdvisor === 'boolean' + ? migrated.display.showAdvisor + : DEFAULT_CONFIG.display.showAdvisor, + advisorOverride: typeof migrated.display?.advisorOverride === 'string' + ? migrated.display.advisorOverride.slice(0, 80) + : DEFAULT_CONFIG.display.advisorOverride, + autoCompactWindow: validateAutoCompactWindow(migrated.display?.autoCompactWindow), + }; + const colors = { + context: validateColorValue(migrated.colors?.context) + ? migrated.colors.context + : DEFAULT_CONFIG.colors.context, + usage: validateColorValue(migrated.colors?.usage) + ? migrated.colors.usage + : DEFAULT_CONFIG.colors.usage, + warning: validateColorValue(migrated.colors?.warning) + ? migrated.colors.warning + : DEFAULT_CONFIG.colors.warning, + usageWarning: validateColorValue(migrated.colors?.usageWarning) + ? migrated.colors.usageWarning + : DEFAULT_CONFIG.colors.usageWarning, + critical: validateColorValue(migrated.colors?.critical) + ? migrated.colors.critical + : DEFAULT_CONFIG.colors.critical, + model: validateColorValue(migrated.colors?.model) + ? migrated.colors.model + : DEFAULT_CONFIG.colors.model, + project: validateColorValue(migrated.colors?.project) + ? migrated.colors.project + : DEFAULT_CONFIG.colors.project, + git: validateColorValue(migrated.colors?.git) + ? migrated.colors.git + : DEFAULT_CONFIG.colors.git, + gitBranch: validateColorValue(migrated.colors?.gitBranch) + ? migrated.colors.gitBranch + : DEFAULT_CONFIG.colors.gitBranch, + label: validateColorValue(migrated.colors?.label) + ? migrated.colors.label + : DEFAULT_CONFIG.colors.label, + custom: validateColorValue(migrated.colors?.custom) + ? migrated.colors.custom + : DEFAULT_CONFIG.colors.custom, + barFilled: validateBarChar(migrated.colors?.barFilled) + ? migrated.colors.barFilled + : DEFAULT_CONFIG.colors.barFilled, + barEmpty: validateBarChar(migrated.colors?.barEmpty) + ? migrated.colors.barEmpty + : DEFAULT_CONFIG.colors.barEmpty, + }; + return { language, lineLayout, showSeparators, pathLevels, maxWidth, forceMaxWidth, elementOrder, gitStatus, display, colors }; +} +export async function loadConfig() { + const configPath = getConfigPath(); + try { + if (!fs.existsSync(configPath)) { + return mergeConfig({}); + } + const content = fs.readFileSync(configPath, 'utf-8'); + const userConfig = JSON.parse(content); + return mergeConfig(userConfig); + } + catch (err) { + debug('Failed to load config from %s, using defaults:', configPath, err instanceof Error ? err.message : err); + return mergeConfig({}); + } +} +//# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/config.js.map b/plugins/balance-hud/dist/config.js.map new file mode 100644 index 0000000..ed71993 --- /dev/null +++ b/plugins/balance-hud/dist/config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/constants.d.ts b/plugins/balance-hud/dist/constants.d.ts new file mode 100644 index 0000000..02a8411 --- /dev/null +++ b/plugins/balance-hud/dist/constants.d.ts @@ -0,0 +1,11 @@ +/** + * Autocompact buffer percentage. + * + * NOTE: This value is applied as a percentage of Claude Code's reported + * context window size. The `33k/200k` example is just the 200k-window case. + * It is empirically derived from current Claude Code `/context` output, is + * not officially documented by Anthropic, and may need adjustment if users + * report mismatches in future Claude Code versions. + */ +export declare const AUTOCOMPACT_BUFFER_PERCENT = 0.165; +//# sourceMappingURL=constants.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/constants.d.ts.map b/plugins/balance-hud/dist/constants.d.ts.map new file mode 100644 index 0000000..e46746f --- /dev/null +++ b/plugins/balance-hud/dist/constants.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,QAAQ,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/constants.js b/plugins/balance-hud/dist/constants.js new file mode 100644 index 0000000..6a4179b --- /dev/null +++ b/plugins/balance-hud/dist/constants.js @@ -0,0 +1,11 @@ +/** + * Autocompact buffer percentage. + * + * NOTE: This value is applied as a percentage of Claude Code's reported + * context window size. The `33k/200k` example is just the 200k-window case. + * It is empirically derived from current Claude Code `/context` output, is + * not officially documented by Anthropic, and may need adjustment if users + * report mismatches in future Claude Code versions. + */ +export const AUTOCOMPACT_BUFFER_PERCENT = 0.165; +//# sourceMappingURL=constants.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/constants.js.map b/plugins/balance-hud/dist/constants.js.map new file mode 100644 index 0000000..0814a4c --- /dev/null +++ b/plugins/balance-hud/dist/constants.js.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/context-cache.d.ts b/plugins/balance-hud/dist/context-cache.d.ts new file mode 100644 index 0000000..a410b5e --- /dev/null +++ b/plugins/balance-hud/dist/context-cache.d.ts @@ -0,0 +1,32 @@ +import type { StdinData } from "./types.js"; +export type ContextCacheDeps = { + homeDir: () => string; + now: () => number; + random: () => number; +}; +export type CompactHint = { + /** Timestamp of the most recent compact_boundary entry in the transcript. */ + lastCompactBoundaryAt?: Date; + /** Post-compact token count from compactMetadata, when Claude Code records it. */ + lastCompactPostTokens?: number; +}; +/** + * Apply context-window fallback in-place: + * - For suspicious zero frames, try restoring from the session-scoped cache. + * - For healthy frames, refresh the cache snapshot for this session + * (subject to TTL + value-change throttling to avoid hot-path writes). + * + * When `compactHint.lastCompactBoundaryAt` is newer than the cached snapshot's + * `saved_at`, the zero frame is treated as a legitimate post-/compact reset and + * the stale pre-compact snapshot is NOT restored. If `lastCompactPostTokens` + * is provided, it is used to synthesize an accurate transition-window percent. + * + * No-op when stdin has no transcript_path, since without a stable session key + * we cannot safely isolate cache entries across concurrent Claude Code sessions. + */ +export declare function applyContextWindowFallback(stdin: StdinData, overrides?: Partial, sessionName?: string, compactHint?: CompactHint): void; +/** + * Test-only entrypoint for deterministically exercising the sweep logic. + */ +export declare function _sweepCacheForTests(homeDir: string, now: number): void; +//# sourceMappingURL=context-cache.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/context-cache.d.ts.map b/plugins/balance-hud/dist/context-cache.d.ts.map new file mode 100644 index 0000000..b79cde2 --- /dev/null +++ b/plugins/balance-hud/dist/context-cache.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"context-cache.d.ts","sourceRoot":"","sources":["../src/context-cache.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoC5C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,MAAM,CAAC;CACtB,CAAC;AAgOF,MAAM,MAAM,WAAW,GAAG;IACxB,6EAA6E;IAC7E,qBAAqB,CAAC,EAAE,IAAI,CAAC;IAC7B,kFAAkF;IAClF,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,SAAS,EAChB,SAAS,GAAE,OAAO,CAAC,gBAAgB,CAAM,EACzC,WAAW,CAAC,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,WAAW,GACxB,IAAI,CA8CN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAEtE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/context-cache.js b/plugins/balance-hud/dist/context-cache.js new file mode 100644 index 0000000..77090b0 --- /dev/null +++ b/plugins/balance-hud/dist/context-cache.js @@ -0,0 +1,281 @@ +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { createHash } from "node:crypto"; +import { getHudPluginDir } from "./claude-config-dir.js"; +import { createDebug } from "./debug.js"; +const debug = createDebug('context-cache'); +const CACHE_DIRNAME = "context-cache"; +/** + * Minimum interval between cache rewrites for the same session. + * Status line runs every ~300ms so this keeps the steady-state write path cheap + * while still refreshing the fallback snapshot regularly. + */ +const WRITE_TTL_MS = 3_000; +/** + * Sweep parameters bounding long-term growth of the cache directory. + * A sweep is attempted probabilistically on cache writes to avoid paying + * directory-scan cost on every status line tick. + */ +const MAX_CACHE_AGE_MS = 7 * 24 * 60 * 60 * 1000; +const MAX_CACHE_ENTRIES = 100; +const SWEEP_SAMPLE_RATE = 0.01; +const defaultDeps = { + homeDir: () => os.homedir(), + now: () => Date.now(), + random: () => Math.random(), +}; +/** + * Resolve the session-scoped cache file used for context window fallback. + * Uses a sha256 of the transcript path so that concurrent Claude Code + * sessions never share or overwrite each other's cached snapshots. + */ +function getCachePath(homeDir, transcriptPath) { + const hash = createHash("sha256") + .update(path.resolve(transcriptPath)) + .digest("hex"); + return path.join(getHudPluginDir(homeDir), CACHE_DIRNAME, `${hash}.json`); +} +/** + * Resolve the cache directory that holds all session-scoped snapshots. + */ +function getCacheDir(homeDir) { + return path.join(getHudPluginDir(homeDir), CACHE_DIRNAME); +} +function ensurePrivateDir(dir) { + fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); + try { + fs.chmodSync(dir, 0o700); + } + catch { + // Best-effort: some filesystems do not support POSIX modes. + } +} +/** + * Read the last known good context snapshot from disk. + * Returns null when the cache is missing, malformed, or invalid. + */ +function readCache(homeDir, transcriptPath) { + try { + const cachePath = getCachePath(homeDir, transcriptPath); + if (!fs.existsSync(cachePath)) + return null; + const content = fs.readFileSync(cachePath, "utf8"); + const parsed = JSON.parse(content); + if (typeof parsed.used_percentage !== "number" || + !Number.isFinite(parsed.used_percentage)) { + return null; + } + return parsed; + } + catch (err) { + debug('Failed to read context cache:', err instanceof Error ? err.message : err); + return null; + } +} +/** + * Decide whether the current write can be skipped because the cached snapshot + * for this session was refreshed recently enough. + */ +function shouldSkipWrite(cachePath, now) { + try { + const stat = fs.statSync(cachePath); + return now - stat.mtimeMs < WRITE_TTL_MS; + } + catch (err) { + debug('Cache stat check failed (will write):', err instanceof Error ? err.message : err); + return false; + } +} +/** + * Persist a known-good context snapshot for future fallback use. + * Any write failure is intentionally ignored to keep rendering non-blocking. + */ +function writeCache(homeDir, transcriptPath, contextWindow, now, sessionName) { + try { + const cachePath = getCachePath(homeDir, transcriptPath); + if (shouldSkipWrite(cachePath, now)) { + return; + } + const cacheDir = path.dirname(cachePath); + ensurePrivateDir(cacheDir); + const payload = { + used_percentage: contextWindow.used_percentage ?? 0, + remaining_percentage: contextWindow.remaining_percentage ?? null, + current_usage: contextWindow.current_usage ?? null, + context_window_size: contextWindow.context_window_size ?? null, + saved_at: now, + session_name: sessionName ?? null, + }; + fs.writeFileSync(cachePath, JSON.stringify(payload), { + encoding: "utf8", + mode: 0o600, + }); + try { + fs.chmodSync(cachePath, 0o600); + } + catch { + // Best-effort: some filesystems do not support POSIX modes. + } + const timestampSeconds = now / 1000; + fs.utimesSync(cachePath, timestampSeconds, timestampSeconds); + } + catch (err) { + debug('Failed to write context cache:', err instanceof Error ? err.message : err); + } +} +/** + * Remove stale cache entries and enforce a hard cap on total file count. + * Safe to run opportunistically; every per-file failure is swallowed. + */ +function sweepCacheDir(cacheDir, now) { + try { + if (!fs.existsSync(cacheDir)) + return; + const entries = fs.readdirSync(cacheDir, { withFileTypes: true }); + const survivors = []; + for (const entry of entries) { + if (!entry.isFile() || !entry.name.endsWith(".json")) + continue; + const fullPath = path.join(cacheDir, entry.name); + try { + const stat = fs.statSync(fullPath); + if (now - stat.mtimeMs > MAX_CACHE_AGE_MS) { + fs.unlinkSync(fullPath); + continue; + } + survivors.push({ fullPath, mtimeMs: stat.mtimeMs }); + } + catch (err) { + debug('Sweep: failed to process %s:', fullPath, err instanceof Error ? err.message : err); + } + } + if (survivors.length > MAX_CACHE_ENTRIES) { + survivors.sort((a, b) => a.mtimeMs - b.mtimeMs); + const toDelete = survivors.length - MAX_CACHE_ENTRIES; + for (let i = 0; i < toDelete; i += 1) { + try { + fs.unlinkSync(survivors[i].fullPath); + } + catch (err) { + debug('Sweep: failed to unlink %s:', survivors[i].fullPath, err instanceof Error ? err.message : err); + } + } + } + } + catch (err) { + debug('Cache sweep failed:', err instanceof Error ? err.message : err); + } +} +/** + * Check whether all tracked token counters in current_usage are zero. + */ +function isAllUsageZero(usage) { + if (!usage) { + return true; + } + return ((usage.input_tokens ?? 0) === 0 && + (usage.output_tokens ?? 0) === 0 && + (usage.cache_creation_input_tokens ?? 0) === 0 && + (usage.cache_read_input_tokens ?? 0) === 0); +} +/** + * Returns true when context window data looks like a Claude Code reporting + * glitch rather than a genuine zero-usage state. + * + * We treat a zero-percent frame as suspicious when `current_usage` is empty. + * Fresh sessions are protected by a cache miss; post-compact resets are + * protected by the compact-boundary guard in applyContextWindowFallback. + */ +function isSuspiciousZero(contextWindow) { + const usedPercentage = contextWindow.used_percentage ?? 0; + if (usedPercentage !== 0) { + return false; + } + if (!isAllUsageZero(contextWindow.current_usage)) { + return false; + } + return true; +} +/** + * Determine whether the current frame contains a usable context snapshot. + */ +function hasGoodContext(contextWindow) { + return ((contextWindow.context_window_size ?? 0) > 0 && + typeof contextWindow.used_percentage === "number" && + contextWindow.used_percentage > 0); +} +/** + * Merge cached context fields into the current frame. + * Prefer the frame's context_window_size when already present. + */ +function applyCachedContext(contextWindow, cache) { + contextWindow.used_percentage = cache.used_percentage; + contextWindow.remaining_percentage = cache.remaining_percentage ?? null; + contextWindow.current_usage = cache.current_usage ?? null; + contextWindow.context_window_size = + contextWindow.context_window_size ?? cache.context_window_size ?? undefined; +} +/** + * Apply context-window fallback in-place: + * - For suspicious zero frames, try restoring from the session-scoped cache. + * - For healthy frames, refresh the cache snapshot for this session + * (subject to TTL + value-change throttling to avoid hot-path writes). + * + * When `compactHint.lastCompactBoundaryAt` is newer than the cached snapshot's + * `saved_at`, the zero frame is treated as a legitimate post-/compact reset and + * the stale pre-compact snapshot is NOT restored. If `lastCompactPostTokens` + * is provided, it is used to synthesize an accurate transition-window percent. + * + * No-op when stdin has no transcript_path, since without a stable session key + * we cannot safely isolate cache entries across concurrent Claude Code sessions. + */ +export function applyContextWindowFallback(stdin, overrides = {}, sessionName, compactHint) { + const contextWindow = stdin.context_window; + if (!contextWindow) { + return; + } + const transcriptPath = stdin.transcript_path?.trim(); + if (!transcriptPath) { + return; + } + const deps = { ...defaultDeps, ...overrides }; + const homeDir = deps.homeDir(); + const now = deps.now(); + if (isSuspiciousZero(contextWindow)) { + const cached = readCache(homeDir, transcriptPath); + const boundaryMs = compactHint?.lastCompactBoundaryAt?.getTime(); + const isPostCompactReset = typeof boundaryMs === "number" && + Number.isFinite(boundaryMs) && + (!cached?.saved_at || boundaryMs > cached.saved_at); + if (isPostCompactReset) { + // Legitimate /compact reset: keep the zero frame instead of restoring a + // stale pre-compact snapshot. Surface the compactMetadata.postTokens + // value (when available) so the bar shows the real post-compact + // percent during the transition before the next assistant response. + const postTokens = compactHint?.lastCompactPostTokens; + const size = contextWindow.context_window_size ?? 0; + if (typeof postTokens === "number" && postTokens > 0 && size > 0) { + const pct = Math.min(100, Math.max(0, Math.round((postTokens / size) * 100))); + contextWindow.used_percentage = pct; + contextWindow.remaining_percentage = 100 - pct; + } + } + else if (cached) { + applyCachedContext(contextWindow, cached); + } + } + if (hasGoodContext(contextWindow)) { + writeCache(homeDir, transcriptPath, contextWindow, now, sessionName); + if (deps.random() < SWEEP_SAMPLE_RATE) { + sweepCacheDir(getCacheDir(homeDir), now); + } + } +} +/** + * Test-only entrypoint for deterministically exercising the sweep logic. + */ +export function _sweepCacheForTests(homeDir, now) { + sweepCacheDir(getCacheDir(homeDir), now); +} +//# sourceMappingURL=context-cache.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/context-cache.js.map b/plugins/balance-hud/dist/context-cache.js.map new file mode 100644 index 0000000..2c82f16 --- /dev/null +++ b/plugins/balance-hud/dist/context-cache.js.map @@ -0,0 +1 @@ +{"version":3,"file":"context-cache.js","sourceRoot":"","sources":["../src/context-cache.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/cost.d.ts b/plugins/balance-hud/dist/cost.d.ts new file mode 100644 index 0000000..48822a4 --- /dev/null +++ b/plugins/balance-hud/dist/cost.d.ts @@ -0,0 +1,16 @@ +import type { SessionTokenUsage, StdinData } from './types.js'; +export interface SessionCostEstimate { + totalUsd: number; + inputUsd: number; + cacheCreationUsd: number; + cacheReadUsd: number; + outputUsd: number; +} +export interface SessionCostDisplay { + totalUsd: number; + source: 'native' | 'estimate'; +} +export declare function estimateSessionCost(stdin: StdinData, sessionTokens: SessionTokenUsage | undefined): SessionCostEstimate | null; +export declare function resolveSessionCost(stdin: StdinData, sessionTokens: SessionTokenUsage | undefined): SessionCostDisplay | null; +export declare function formatUsd(amount: number): string; +//# sourceMappingURL=cost.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/cost.d.ts.map b/plugins/balance-hud/dist/cost.d.ts.map new file mode 100644 index 0000000..07279a3 --- /dev/null +++ b/plugins/balance-hud/dist/cost.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ/D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;CAC/B;AAmED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,EAChB,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAC3C,mBAAmB,GAAG,IAAI,CAsC5B;AAmBD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAChB,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAC3C,kBAAkB,GAAG,IAAI,CAkB3B;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAQhD"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/cost.js b/plugins/balance-hud/dist/cost.js new file mode 100644 index 0000000..8266625 --- /dev/null +++ b/plugins/balance-hud/dist/cost.js @@ -0,0 +1,130 @@ +import { isBedrockModelId, isVertexModelId } from './stdin.js'; +const TOKENS_PER_MILLION = 1_000_000; +const CACHE_WRITE_MULTIPLIER = 1.25; +const CACHE_READ_MULTIPLIER = 0.1; +// Patterns are tried in order; the first match wins. Families with more specific +// model lines (Haiku 4.x differs from Haiku 3.5) must come before any broader +// fallback patterns to avoid silent under-pricing. +const ANTHROPIC_MODEL_PRICING = [ + { pattern: /\bopus 4 (?:[5-9]|\d{2,})\b/i, pricing: { inputUsdPerMillion: 5, outputUsdPerMillion: 25 } }, + { pattern: /\bopus 4(?: \d+)?\b/i, pricing: { inputUsdPerMillion: 15, outputUsdPerMillion: 75 } }, + { pattern: /\bsonnet 4(?: \d+)?\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } }, + { pattern: /\bsonnet 3 7\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } }, + { pattern: /\bsonnet 3 5\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } }, + { pattern: /\bhaiku 4(?: \d+)?\b/i, pricing: { inputUsdPerMillion: 1, outputUsdPerMillion: 5 } }, + { pattern: /\bhaiku 3 5\b/i, pricing: { inputUsdPerMillion: 0.8, outputUsdPerMillion: 4 } }, + // Enterprise plan aliases (e.g. opusplan, sonnetplan, haikuplan) + { pattern: /\bopusplan\b/i, pricing: { inputUsdPerMillion: 15, outputUsdPerMillion: 75 } }, + { pattern: /\bsonnetplan\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } }, + { pattern: /\bhaikuplan\b/i, pricing: { inputUsdPerMillion: 0.8, outputUsdPerMillion: 4 } }, +]; +function normalizeModelName(modelName) { + return modelName + .toLowerCase() + .replace(/^claude\s+/, '') + .replace(/\([^)]*\)/g, ' ') + .replace(/[._-]+/g, ' ') + .replace(/\s+/g, ' ') + .trim(); +} +function matchAnthropicPricing(modelName) { + const normalized = normalizeModelName(modelName); + for (const entry of ANTHROPIC_MODEL_PRICING) { + if (entry.pattern.test(normalized)) { + return entry.pricing; + } + } + return null; +} +function calculateUsd(tokens, usdPerMillion) { + return (tokens * usdPerMillion) / TOKENS_PER_MILLION; +} +function getAnthropicPricing(stdin) { + const candidates = [ + stdin.model?.display_name?.trim(), + stdin.model?.id?.trim(), + ]; + for (const candidate of candidates) { + if (!candidate) { + continue; + } + const pricing = matchAnthropicPricing(candidate); + if (pricing) { + return pricing; + } + } + return null; +} +export function estimateSessionCost(stdin, sessionTokens) { + if (!sessionTokens) { + return null; + } + if (isBedrockModelId(stdin.model?.id)) { + return null; + } + if (isVertexModelId(stdin.model?.id)) { + return null; + } + const pricing = getAnthropicPricing(stdin); + if (!pricing) { + return null; + } + const totalTokens = sessionTokens.inputTokens + + sessionTokens.cacheCreationTokens + + sessionTokens.cacheReadTokens + + sessionTokens.outputTokens; + if (totalTokens === 0) { + return null; + } + const inputUsd = calculateUsd(sessionTokens.inputTokens, pricing.inputUsdPerMillion); + const cacheCreationUsd = calculateUsd(sessionTokens.cacheCreationTokens, pricing.inputUsdPerMillion * CACHE_WRITE_MULTIPLIER); + const cacheReadUsd = calculateUsd(sessionTokens.cacheReadTokens, pricing.inputUsdPerMillion * CACHE_READ_MULTIPLIER); + const outputUsd = calculateUsd(sessionTokens.outputTokens, pricing.outputUsdPerMillion); + return { + totalUsd: inputUsd + cacheCreationUsd + cacheReadUsd + outputUsd, + inputUsd, + cacheCreationUsd, + cacheReadUsd, + outputUsd, + }; +} +function getNativeCostUsd(stdin) { + const nativeCost = stdin.cost?.total_cost_usd; + if (typeof nativeCost !== 'number' || !Number.isFinite(nativeCost)) { + return null; + } + if (isBedrockModelId(stdin.model?.id)) { + return null; + } + if (isVertexModelId(stdin.model?.id)) { + return null; + } + return nativeCost; +} +export function resolveSessionCost(stdin, sessionTokens) { + const nativeCostUsd = getNativeCostUsd(stdin); + if (nativeCostUsd !== null) { + return { + totalUsd: nativeCostUsd, + source: 'native', + }; + } + const estimate = estimateSessionCost(stdin, sessionTokens); + if (!estimate) { + return null; + } + return { + totalUsd: estimate.totalUsd, + source: 'estimate', + }; +} +export function formatUsd(amount) { + if (amount >= 1) { + return `$${amount.toFixed(2)}`; + } + if (amount >= 0.1) { + return `$${amount.toFixed(3)}`; + } + return `$${amount.toFixed(4)}`; +} +//# sourceMappingURL=cost.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/cost.js.map b/plugins/balance-hud/dist/cost.js.map new file mode 100644 index 0000000..8028262 --- /dev/null +++ b/plugins/balance-hud/dist/cost.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/debug.d.ts b/plugins/balance-hud/dist/debug.d.ts new file mode 100644 index 0000000..542ae24 --- /dev/null +++ b/plugins/balance-hud/dist/debug.d.ts @@ -0,0 +1,6 @@ +/** + * Create a namespaced debug logger + * @param namespace - Tag for log messages (e.g., 'config', 'usage') + */ +export declare function createDebug(namespace: string): (msg: string, ...args: unknown[]) => void; +//# sourceMappingURL=debug.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/debug.d.ts.map b/plugins/balance-hud/dist/debug.d.ts.map new file mode 100644 index 0000000..5e4d298 --- /dev/null +++ b/plugins/balance-hud/dist/debug.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IACrB,KAAK,MAAM,EAAE,GAAG,MAAM,OAAO,EAAE,KAAG,IAAI,CAK7D"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/debug.js b/plugins/balance-hud/dist/debug.js new file mode 100644 index 0000000..e331aa3 --- /dev/null +++ b/plugins/balance-hud/dist/debug.js @@ -0,0 +1,15 @@ +// Shared debug logging utility +// Enable via: DEBUG=balance-hud or DEBUG=* +const DEBUG = process.env.DEBUG?.includes('balance-hud') || process.env.DEBUG === '*'; +/** + * Create a namespaced debug logger + * @param namespace - Tag for log messages (e.g., 'config', 'usage') + */ +export function createDebug(namespace) { + return function debug(msg, ...args) { + if (DEBUG) { + console.error(`[balance-hud:${namespace}] ${msg}`, ...args); + } + }; +} +//# sourceMappingURL=debug.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/debug.js.map b/plugins/balance-hud/dist/debug.js.map new file mode 100644 index 0000000..6ebcc25 --- /dev/null +++ b/plugins/balance-hud/dist/debug.js.map @@ -0,0 +1 @@ +{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,0CAA0C;AAE1C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC;AAErF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO,SAAS,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;QACnD,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,eAAe,SAAS,KAAK,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/effort.d.ts b/plugins/balance-hud/dist/effort.d.ts new file mode 100644 index 0000000..2a20630 --- /dev/null +++ b/plugins/balance-hud/dist/effort.d.ts @@ -0,0 +1,32 @@ +export interface EffortInfo { + level: string; + symbol: string; +} +/** + * Shape of the effort field in Claude Code stdin JSON. + * + * Historically absent; Claude Code 2.1.115+ sends an object with a string + * `level` field (verified capture: `{ "level": "max" }`). The index signature + * keeps the type permissive so future additions (e.g., a budget field) do not + * require another breaking change here. + */ +export interface StdinEffort { + level?: string | null; + [key: string]: unknown; +} +export type StdinEffortInput = string | StdinEffort | null | undefined; +/** + * Resolve the current session's effort level. + * + * Resolution order (matches `extractEffortString` below): + * 1. stdin.effort as non-empty string — original PR #471 future-proofed path. + * 2. stdin.effort as object with string `level` — Claude Code 2.1.115+ schema + * (e.g., `{ "level": "max" }`). + * 3. Parent process CLI args — `--effort` flag captured from ppid. + * 4. null. + * + * Non-matching inputs (numbers, booleans, arrays, objects without a string + * `level`) fall through to step 3 rather than crashing. + */ +export declare function resolveEffortLevel(stdinEffort?: StdinEffortInput): EffortInfo | null; +//# sourceMappingURL=effort.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/effort.d.ts.map b/plugins/balance-hud/dist/effort.d.ts.map new file mode 100644 index 0000000..a78e1e8 --- /dev/null +++ b/plugins/balance-hud/dist/effort.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"effort.d.ts","sourceRoot":"","sources":["../src/effort.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAUD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,CAAC,EAAE,gBAAgB,GAC7B,UAAU,GAAG,IAAI,CAYnB"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/effort.js b/plugins/balance-hud/dist/effort.js new file mode 100644 index 0000000..143fcc4 --- /dev/null +++ b/plugins/balance-hud/dist/effort.js @@ -0,0 +1,74 @@ +import { execFileSync } from 'node:child_process'; +import { createDebug } from './debug.js'; +const debug = createDebug('effort'); +const KNOWN_SYMBOLS = { + low: '○', + medium: '◔', + high: '◑', + xhigh: '◕', + max: '●', +}; +/** + * Resolve the current session's effort level. + * + * Resolution order (matches `extractEffortString` below): + * 1. stdin.effort as non-empty string — original PR #471 future-proofed path. + * 2. stdin.effort as object with string `level` — Claude Code 2.1.115+ schema + * (e.g., `{ "level": "max" }`). + * 3. Parent process CLI args — `--effort` flag captured from ppid. + * 4. null. + * + * Non-matching inputs (numbers, booleans, arrays, objects without a string + * `level`) fall through to step 3 rather than crashing. + */ +export function resolveEffortLevel(stdinEffort) { + const fromStdin = extractEffortString(stdinEffort); + if (fromStdin) { + return formatEffort(fromStdin); + } + const cliEffort = readParentProcessEffort(); + if (cliEffort) { + return formatEffort(cliEffort); + } + return null; +} +function extractEffortString(value) { + if (typeof value === 'string') { + return value.length > 0 ? value : null; + } + if (value && typeof value === 'object' && !Array.isArray(value)) { + const level = value.level; + if (typeof level === 'string' && level.length > 0) { + return level; + } + } + return null; +} +function formatEffort(level) { + const normalized = level.toLowerCase().trim(); + const symbol = KNOWN_SYMBOLS[normalized] ?? ''; + return { level: normalized, symbol }; +} +function readParentProcessEffort() { + if (process.platform === 'win32') { + return null; + } + try { + const ppid = process.ppid; + if (!ppid || ppid <= 1) { + return null; + } + const output = execFileSync('ps', ['-o', 'args=', '-p', String(ppid)], { + encoding: 'utf8', + timeout: 500, + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(); + const match = output.match(/--effort[= ]+(\w+)/); + return match?.[1] ?? null; + } + catch (err) { + debug('Failed to read parent process effort:', err instanceof Error ? err.message : err); + return null; + } +} +//# sourceMappingURL=effort.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/effort.js.map b/plugins/balance-hud/dist/effort.js.map new file mode 100644 index 0000000..bc071b5 --- /dev/null +++ b/plugins/balance-hud/dist/effort.js.map @@ -0,0 +1 @@ +{"version":3,"file":"effort.js","sourceRoot":"","sources":["../src/effort.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAOpC,MAAM,aAAa,GAA2B;IAC5C,GAAG,EAAE,GAAG;IACR,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,GAAG;IACV,GAAG,EAAE,GAAG;CACT,CAAC;AAiBF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAChC,WAA8B;IAE9B,MAAM,SAAS,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,SAAS,GAAG,uBAAuB,EAAE,CAAC;IAC5C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,KAAK,GAAI,KAA6B,CAAC,KAAK,CAAC;QACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC/C,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,uBAAuB;IAC9B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC1B,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE;YACrE,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,GAAG;YACZ,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACjD,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,uCAAuC,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACzF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/external-usage.d.ts b/plugins/balance-hud/dist/external-usage.d.ts new file mode 100644 index 0000000..490e4b9 --- /dev/null +++ b/plugins/balance-hud/dist/external-usage.d.ts @@ -0,0 +1,17 @@ +import * as fs from 'node:fs'; +import type { HudConfig } from './config.js'; +import type { UsageData } from './types.js'; +export declare const EXTERNAL_USAGE_WRITE_THROTTLE_MS = 30000; +type FileSystemDeps = { + chmodSync: typeof fs.chmodSync; + existsSync: typeof fs.existsSync; + readFileSync: typeof fs.readFileSync; + renameSync: typeof fs.renameSync; + rmSync: typeof fs.rmSync; + statSync: typeof fs.statSync; + writeFileSync: typeof fs.writeFileSync; +}; +export declare function writeExternalUsageSnapshot(config: HudConfig, usage: UsageData | null, now?: number, deps?: FileSystemDeps): boolean; +export declare function getUsageFromExternalSnapshot(config: HudConfig, now?: number): UsageData | null; +export {}; +//# sourceMappingURL=external-usage.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/external-usage.d.ts.map b/plugins/balance-hud/dist/external-usage.d.ts.map new file mode 100644 index 0000000..c3c0052 --- /dev/null +++ b/plugins/balance-hud/dist/external-usage.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"external-usage.d.ts","sourceRoot":"","sources":["../src/external-usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAC;AAMnE,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAcvD,KAAK,cAAc,GAAG;IACpB,SAAS,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC;IAC/B,UAAU,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC;IACjC,YAAY,EAAE,OAAO,EAAE,CAAC,YAAY,CAAC;IACrC,UAAU,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC;IACjC,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC;IAC7B,aAAa,EAAE,OAAO,EAAE,CAAC,aAAa,CAAC;CACxC,CAAC;AAuLF,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,SAAS,GAAG,IAAI,EACvB,GAAG,SAAa,EAChB,IAAI,GAAE,cAAuB,GAC5B,OAAO,CAwCT;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,SAAS,EACjB,GAAG,SAAa,GACf,SAAS,GAAG,IAAI,CAkDlB"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/external-usage.js b/plugins/balance-hud/dist/external-usage.js new file mode 100644 index 0000000..76ce8a4 --- /dev/null +++ b/plugins/balance-hud/dist/external-usage.js @@ -0,0 +1,254 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { createDebug } from './debug.js'; +import { sanitizeDisplayText } from './utils/sanitize.js'; +const debug = createDebug('external-usage'); +const MAX_BALANCE_LABEL_LENGTH = 512; +export const EXTERNAL_USAGE_WRITE_THROTTLE_MS = 30_000; +const fsDeps = { + chmodSync: fs.chmodSync, + existsSync: fs.existsSync, + readFileSync: fs.readFileSync, + renameSync: fs.renameSync, + rmSync: fs.rmSync, + statSync: fs.statSync, + writeFileSync: fs.writeFileSync, +}; +function parseUsagePercent(value) { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return null; + } + return Math.round(Math.min(100, Math.max(0, value))); +} +function sanitizeBalanceLabel(value) { + if (typeof value !== 'string') { + return null; + } + // Balance labels from the auto_refresh daemon contain ANSI SGR color codes + // matching v1.1.3 hud_balance.mjs. The daemon is a trusted local process. + // Only strip OSC hyperlink sequences and C1 control chars — keep CSI/SGR colors. + const sanitized = value + .replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, '') // OSC sequences + .replace(/\x1B[@-Z\\-_]/g, '') // 7-bit C1 / ESC Fe (non-CSI) + .trim(); + if (!sanitized) { + return null; + } + if (sanitized.length <= MAX_BALANCE_LABEL_LENGTH) { + return sanitized; + } + return `${sanitized.slice(0, MAX_BALANCE_LABEL_LENGTH - 3)}...`; +} +function parseDateValue(value) { + if (typeof value === 'number') { + if (!Number.isFinite(value) || value <= 0) { + return null; + } + const millis = value > 1e12 ? value : value * 1000; + const date = new Date(millis); + return Number.isNaN(date.getTime()) ? null : date; + } + if (typeof value === 'string' && value.trim()) { + const date = new Date(value); + return Number.isNaN(date.getTime()) ? null : date; + } + return null; +} +function parseUpdatedAt(value) { + const date = parseDateValue(value); + return date ? date.getTime() : null; +} +function snapshotFromUsage(usage, now) { + return { + updated_at: new Date(now).toISOString(), + five_hour: { + used_percentage: usage.fiveHour, + resets_at: usage.fiveHourResetAt?.toISOString() ?? null, + }, + seven_day: { + used_percentage: usage.sevenDay, + resets_at: usage.sevenDayResetAt?.toISOString() ?? null, + }, + }; +} +function comparableSnapshot(snapshot) { + if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) { + return null; + } + const topLevelKeys = Object.keys(snapshot); + if (topLevelKeys.length !== 3 + || !topLevelKeys.includes('updated_at') + || !topLevelKeys.includes('five_hour') + || !topLevelKeys.includes('seven_day')) { + return null; + } + const value = snapshot; + if (parseUpdatedAt(value.updated_at) === null) { + return null; + } + const fiveHour = comparableWindow(value.five_hour); + const sevenDay = comparableWindow(value.seven_day); + if (fiveHour === null || sevenDay === null) { + return null; + } + return { + five_hour: fiveHour, + seven_day: sevenDay, + }; +} +function comparableWindow(value) { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + return null; + } + const keys = Object.keys(value); + if (keys.length !== 2 + || !keys.includes('used_percentage') + || !keys.includes('resets_at')) { + return null; + } + const window = value; + const usedPercentage = parseUsagePercent(window.used_percentage); + if (window.used_percentage !== null && usedPercentage === null) { + return null; + } + const resetAt = parseDateValue(window.resets_at); + if (window.resets_at !== null && resetAt === null) { + return null; + } + return { + used_percentage: usedPercentage, + resets_at: resetAt?.toISOString() ?? null, + }; +} +function shouldWriteSnapshot(snapshotPath, nextSnapshot, now, deps) { + try { + if (!deps.existsSync(snapshotPath)) { + return true; + } + const stats = deps.statSync(snapshotPath); + if (now - stats.mtimeMs > EXTERNAL_USAGE_WRITE_THROTTLE_MS) { + return true; + } + const current = JSON.parse(deps.readFileSync(snapshotPath, 'utf8')); + return JSON.stringify(comparableSnapshot(current)) !== JSON.stringify(comparableSnapshot(nextSnapshot)); + } + catch (err) { + debug('Failed to compare snapshot (will write):', err instanceof Error ? err.message : err); + return true; + } +} +function resolveSnapshotWritePath(snapshotPath) { + if (!path.isAbsolute(snapshotPath)) { + return null; + } + const parsed = path.parse(snapshotPath); + if (!parsed.base || parsed.ext.toLowerCase() !== '.json') { + return null; + } + return path.normalize(snapshotPath); +} +function directoryExists(dir, deps) { + try { + return deps.statSync(dir).isDirectory(); + } + catch (err) { + debug('Directory check failed for %s:', dir, err instanceof Error ? err.message : err); + return false; + } +} +export function writeExternalUsageSnapshot(config, usage, now = Date.now(), deps = fsDeps) { + const snapshotPath = resolveSnapshotWritePath(config.display.externalUsageWritePath); + if (!snapshotPath || !usage) { + return false; + } + const snapshot = snapshotFromUsage(usage, now); + const dir = path.dirname(snapshotPath); + const base = path.basename(snapshotPath); + const tmpPath = path.join(dir, `.${base}.${process.pid}.${now}.${Math.random().toString(36).slice(2)}.tmp`); + try { + if (!directoryExists(dir, deps)) { + return false; + } + if (!shouldWriteSnapshot(snapshotPath, snapshot, now, deps)) { + return false; + } + deps.writeFileSync(tmpPath, `${JSON.stringify(snapshot, null, 2)}\n`, { + encoding: 'utf8', + mode: 0o600, + flag: 'wx', + }); + deps.renameSync(tmpPath, snapshotPath); + deps.chmodSync(snapshotPath, 0o600); + return true; + } + catch (err) { + debug('Failed to write usage snapshot:', err instanceof Error ? err.message : err); + try { + deps.rmSync(tmpPath, { force: true }); + } + catch (cleanupErr) { + debug('Failed to clean up temp file:', cleanupErr instanceof Error ? cleanupErr.message : cleanupErr); + } + return false; + } +} +export function getUsageFromExternalSnapshot(config, now = Date.now()) { + const snapshotPath = config.display.externalUsagePath; + if (!snapshotPath || !path.isAbsolute(snapshotPath)) { + return null; + } + try { + const raw = fs.readFileSync(snapshotPath, 'utf8'); + const parsed = JSON.parse(raw); + const updatedAt = parseUpdatedAt(parsed.updated_at); + if (updatedAt === null) { + return null; + } + const freshnessMs = config.display.externalUsageFreshnessMs; + if (now - updatedAt > freshnessMs) { + return null; + } + const fiveHour = parseUsagePercent(parsed.five_hour?.used_percentage); + const sevenDay = parseUsagePercent(parsed.seven_day?.used_percentage); + const balanceLabel = sanitizeBalanceLabel(parsed.balance_label); + if (fiveHour === null && sevenDay === null && balanceLabel === null) { + return null; + } + const fiveHourResetAt = parseDateValue(parsed.five_hour?.resets_at); + const sevenDayResetAt = parseDateValue(parsed.seven_day?.resets_at); + if (parsed.five_hour && parsed.five_hour.resets_at != null && fiveHourResetAt === null) { + return null; + } + if (parsed.seven_day && parsed.seven_day.resets_at != null && sevenDayResetAt === null) { + return null; + } + const usage = { + fiveHour, + sevenDay, + fiveHourResetAt, + sevenDayResetAt, + }; + if (balanceLabel !== null) { + usage.balanceLabel = balanceLabel; + } + // Structured balance consumed fields (from auto_refresh daemon) + if (typeof parsed.balance_consumed === 'number' && Number.isFinite(parsed.balance_consumed)) { + usage.balanceConsumed = parsed.balance_consumed; + } + if (typeof parsed.balance_pct === 'number' && Number.isFinite(parsed.balance_pct)) { + usage.balancePct = parsed.balance_pct; + } + if (typeof parsed.balance_time === 'string' && parsed.balance_time.trim()) { + usage.balanceTime = parsed.balance_time.trim(); + } + if (parsed.balance_is_low === true) { + usage.balanceIsLow = true; + } + return usage; + } + catch (err) { + debug('Failed to read external usage snapshot:', err instanceof Error ? err.message : err); + return null; + } +} +//# sourceMappingURL=external-usage.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/external-usage.js.map b/plugins/balance-hud/dist/external-usage.js.map new file mode 100644 index 0000000..044c0f6 --- /dev/null +++ b/plugins/balance-hud/dist/external-usage.js.map @@ -0,0 +1 @@ +{"version":3,"file":"external-usage.js","sourceRoot":"","sources":["../src/external-usage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/extra-cmd.d.ts b/plugins/balance-hud/dist/extra-cmd.d.ts new file mode 100644 index 0000000..5de1f70 --- /dev/null +++ b/plugins/balance-hud/dist/extra-cmd.d.ts @@ -0,0 +1,24 @@ +export interface ExtraLabel { + label: string; +} +export declare function isExtraCmdAllowed(env?: NodeJS.ProcessEnv): boolean; +/** + * Sanitize output to prevent terminal escape injection. + * Strips ANSI escapes, OSC sequences, control characters, and bidi controls. + */ +export declare function sanitize(input: string): string; +/** + * Parse --extra-cmd argument from process.argv + * Supports both: --extra-cmd "command" and --extra-cmd="command" + */ +export declare function parseExtraCmdArg(argv?: string[], env?: NodeJS.ProcessEnv): string | null; +/** + * Execute a command and parse JSON output expecting { label: string } + * Returns null on any error (timeout, parse failure, missing label) + * + * SECURITY NOTE: The cmd parameter is sourced exclusively from CLI arguments + * (--extra-cmd) typed by the user. Since the user controls their own shell, + * shell injection is not a concern here - it's intentional user input. + */ +export declare function runExtraCmd(cmd: string, timeout?: number): Promise; +//# sourceMappingURL=extra-cmd.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/extra-cmd.d.ts.map b/plugins/balance-hud/dist/extra-cmd.d.ts.map new file mode 100644 index 0000000..8829661 --- /dev/null +++ b/plugins/balance-hud/dist/extra-cmd.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"extra-cmd.d.ts","sourceRoot":"","sources":["../src/extra-cmd.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAG/E;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,IAAI,CAsCf;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,MAAmB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoCnG"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/extra-cmd.js b/plugins/balance-hud/dist/extra-cmd.js new file mode 100644 index 0000000..712dd9e --- /dev/null +++ b/plugins/balance-hud/dist/extra-cmd.js @@ -0,0 +1,109 @@ +import { exec } from 'node:child_process'; +import { promisify } from 'node:util'; +import { createDebug } from './debug.js'; +import { sanitizeDisplayText } from './utils/sanitize.js'; +const execAsync = promisify(exec); +const MAX_BUFFER = 10 * 1024; // 10KB - plenty for a label +const MAX_LABEL_LENGTH = 50; +const TIMEOUT_MS = 3000; +const EXTRA_CMD_ENABLE_ENV = 'BALANCE_HUD_ALLOW_EXTRA_CMD'; +const debug = createDebug('extra-cmd'); +export function isExtraCmdAllowed(env = process.env) { + const value = env[EXTRA_CMD_ENABLE_ENV]?.trim().toLowerCase(); + return value === '1' || value === 'true' || value === 'yes' || value === 'on'; +} +/** + * Sanitize output to prevent terminal escape injection. + * Strips ANSI escapes, OSC sequences, control characters, and bidi controls. + */ +export function sanitize(input) { + return sanitizeDisplayText(input); +} +/** + * Parse --extra-cmd argument from process.argv + * Supports both: --extra-cmd "command" and --extra-cmd="command" + */ +export function parseExtraCmdArg(argv = process.argv, env = process.env) { + for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + // Handle --extra-cmd=value syntax + if (arg.startsWith('--extra-cmd=')) { + if (!isExtraCmdAllowed(env)) { + debug(`Warning: --extra-cmd ignored because ${EXTRA_CMD_ENABLE_ENV} is not enabled`); + return null; + } + const value = arg.slice('--extra-cmd='.length); + if (value === '') { + debug('Warning: --extra-cmd value is empty, ignoring'); + return null; + } + return value; + } + // Handle --extra-cmd value syntax + if (arg === '--extra-cmd') { + if (!isExtraCmdAllowed(env)) { + debug(`Warning: --extra-cmd ignored because ${EXTRA_CMD_ENABLE_ENV} is not enabled`); + return null; + } + if (i + 1 >= argv.length) { + debug('Warning: --extra-cmd specified but no value provided'); + return null; + } + const value = argv[i + 1]; + if (value === '') { + debug('Warning: --extra-cmd value is empty, ignoring'); + return null; + } + return value; + } + } + return null; +} +/** + * Execute a command and parse JSON output expecting { label: string } + * Returns null on any error (timeout, parse failure, missing label) + * + * SECURITY NOTE: The cmd parameter is sourced exclusively from CLI arguments + * (--extra-cmd) typed by the user. Since the user controls their own shell, + * shell injection is not a concern here - it's intentional user input. + */ +export async function runExtraCmd(cmd, timeout = TIMEOUT_MS) { + try { + const { stdout } = await execAsync(cmd, { + timeout, + maxBuffer: MAX_BUFFER, + windowsHide: true, + }); + const data = JSON.parse(stdout.trim()); + if (typeof data === 'object' && + data !== null && + 'label' in data && + typeof data.label === 'string') { + let label = sanitize(data.label); + if (label.length > MAX_LABEL_LENGTH) { + label = label.slice(0, MAX_LABEL_LENGTH - 1) + '…'; + } + return label; + } + debug(`Command output missing 'label' field or invalid type: ${JSON.stringify(data)}`); + return null; + } + catch (err) { + if (err instanceof Error) { + if (err.message.includes('TIMEOUT') || err.message.includes('killed')) { + debug(`Command timed out after ${timeout}ms: ${cmd}`); + } + else if (err instanceof SyntaxError) { + debug(`Failed to parse JSON output: ${err.message}`); + } + else { + debug(`Command failed: ${err.message}`); + } + } + else { + debug(`Command failed with unknown error`); + } + return null; + } +} +//# sourceMappingURL=extra-cmd.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/extra-cmd.js.map b/plugins/balance-hud/dist/extra-cmd.js.map new file mode 100644 index 0000000..d96a945 --- /dev/null +++ b/plugins/balance-hud/dist/extra-cmd.js.map @@ -0,0 +1 @@ +{"version":3,"file":"extra-cmd.js","sourceRoot":"","sources":["../src/extra-cmd.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/git.d.ts b/plugins/balance-hud/dist/git.d.ts new file mode 100644 index 0000000..f83fa5b --- /dev/null +++ b/plugins/balance-hud/dist/git.d.ts @@ -0,0 +1,29 @@ +export interface LineDiff { + added: number; + deleted: number; +} +export interface TrackedFile { + basename: string; + fullPath: string; + type: 'modified' | 'added' | 'deleted'; + lineDiff?: LineDiff; +} +export interface FileStats { + modified: number; + added: number; + deleted: number; + untracked: number; + trackedFiles: TrackedFile[]; +} +export interface GitStatus { + branch: string; + isDirty: boolean; + ahead: number; + behind: number; + fileStats?: FileStats; + lineDiff?: LineDiff; + branchUrl?: string; +} +export declare function getGitBranch(cwd?: string): Promise; +export declare function getGitStatus(cwd?: string): Promise; +//# sourceMappingURL=git.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/git.d.ts.map b/plugins/balance-hud/dist/git.d.ts.map new file mode 100644 index 0000000..611cf24 --- /dev/null +++ b/plugins/balance-hud/dist/git.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcvE;AAED,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA8F1E"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/git.js b/plugins/balance-hud/dist/git.js new file mode 100644 index 0000000..e56f36e --- /dev/null +++ b/plugins/balance-hud/dist/git.js @@ -0,0 +1,200 @@ +import { execFile } from 'node:child_process'; +import { promisify } from 'node:util'; +import { createDebug } from './debug.js'; +const debug = createDebug('git'); +const execFileAsync = promisify(execFile); +export async function getGitBranch(cwd) { + if (!cwd) + return null; + try { + const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, timeout: 1000, encoding: 'utf8', windowsHide: true }); + return stdout.trim() || null; + } + catch (err) { + debug('Failed to get git branch:', err instanceof Error ? err.message : err); + return null; + } +} +export async function getGitStatus(cwd) { + if (!cwd) + return null; + try { + // Get branch name + const { stdout: branchOut } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, timeout: 1000, encoding: 'utf8', windowsHide: true }); + const branch = branchOut.trim(); + if (!branch) + return null; + // Check for dirty state and parse file stats + let isDirty = false; + let fileStats; + let lineDiff; + try { + const { stdout: statusOut } = await execFileAsync('git', ['-c', 'core.quotePath=false', '--no-optional-locks', 'status', '--porcelain'], { cwd, timeout: 1000, encoding: 'utf8', windowsHide: true }); + const trimmed = statusOut.trim(); + isDirty = trimmed.length > 0; + if (isDirty) { + fileStats = parseFileStats(trimmed); + } + } + catch (err) { + debug('Failed to get git status:', err instanceof Error ? err.message : err); + } + // Get per-file and total line diffs + if (isDirty) { + try { + const { stdout: numstatOut } = await execFileAsync('git', ['-c', 'core.quotePath=false', 'diff', '--numstat', 'HEAD'], { cwd, timeout: 2000, encoding: 'utf8', windowsHide: true }); + const trackedPaths = new Set(fileStats?.trackedFiles.map((file) => file.fullPath) ?? []); + const { totalDiff, perFileDiff } = parseNumstat(numstatOut, trackedPaths); + lineDiff = totalDiff; + if (fileStats) { + applyLineDiffsToFiles(fileStats.trackedFiles, perFileDiff); + } + } + catch (err) { + debug('Failed to get line diff:', err instanceof Error ? err.message : err); + } + } + // Get ahead/behind counts + let ahead = 0; + let behind = 0; + try { + const { stdout: revOut } = await execFileAsync('git', ['rev-list', '--left-right', '--count', '@{upstream}...HEAD'], { cwd, timeout: 1000, encoding: 'utf8', windowsHide: true }); + const parts = revOut.trim().split(/\s+/); + if (parts.length === 2) { + behind = parseInt(parts[0], 10) || 0; + ahead = parseInt(parts[1], 10) || 0; + } + } + catch (err) { + debug('Failed to get ahead/behind (no upstream?):', err instanceof Error ? err.message : err); + } + // Build GitHub branch URL from remote + let branchUrl; + try { + const { stdout: remoteOut } = await execFileAsync('git', ['remote', 'get-url', 'origin'], { cwd, timeout: 1000, encoding: 'utf8', windowsHide: true }); + const remote = remoteOut.trim(); + const httpsBase = remote + .replace(/^git@github\.com:/, 'https://github.com/') + .replace(/^ssh:\/\/git@github\.com\//, 'https://github.com/') + .replace(/\.git$/, ''); + if (httpsBase.startsWith('https://github.com/')) { + branchUrl = `${httpsBase}/tree/${encodeURIComponent(branch)}`; + } + } + catch (err) { + debug('Failed to get remote URL:', err instanceof Error ? err.message : err); + } + return { branch, isDirty, ahead, behind, fileStats, lineDiff, branchUrl }; + } + catch (err) { + debug('getGitStatus failed:', err instanceof Error ? err.message : err); + return null; + } +} +/** + * Parse git status --porcelain output and count file stats (Starship-compatible format) + * Status codes: M=modified, A=added, D=deleted, ??=untracked + */ +function parseFileStats(porcelainOutput) { + const stats = { modified: 0, added: 0, deleted: 0, untracked: 0, trackedFiles: [] }; + const lines = porcelainOutput.split('\n').filter(Boolean); + for (const line of lines) { + if (line.length < 2) + continue; + const index = line[0]; // staged status + const worktree = line[1]; // unstaged status + if (line.startsWith('??')) { + stats.untracked++; + } + else if (index === 'A') { + stats.added++; + const fullPath = parsePorcelainPath(line.slice(2).trimStart()); + stats.trackedFiles.push({ basename: fullPath.split('/').pop() ?? fullPath, fullPath, type: 'added' }); + } + else if (index === 'D' || worktree === 'D') { + stats.deleted++; + const fullPath = parsePorcelainPath(line.slice(2).trimStart()); + stats.trackedFiles.push({ basename: fullPath.split('/').pop() ?? fullPath, fullPath, type: 'deleted' }); + } + else if (index === 'M' || worktree === 'M' || index === 'R' || index === 'C') { + // M=modified, R=renamed (counts as modified), C=copied (counts as modified) + stats.modified++; + // For renames, git porcelain shows "old -> new"; take the destination path + const fullPath = parsePorcelainPath(line.slice(2).trimStart().split(' -> ').pop() ?? line.slice(2).trimStart()); + stats.trackedFiles.push({ basename: fullPath.split('/').pop() ?? fullPath, fullPath, type: 'modified' }); + } + } + return stats; +} +function parsePorcelainPath(pathField) { + if (pathField.startsWith('"') && pathField.endsWith('"')) { + try { + return JSON.parse(pathField); + } + catch { + return pathField.slice(1, -1); + } + } + return pathField; +} +/** + * Extract the destination path from a numstat path field. + * + * For renames, `git diff --numstat` emits the path as `old => new` + * (sometimes with a shared directory prefix like `pkg/{old.ts => new.ts}`). + * `git status --porcelain` reports the renamed file under its destination + * only, so we key `perFileDiff` by the destination to make lookups match. + */ +function extractNumstatDestination(filePath) { + const braceMatch = filePath.match(/^(.*)\{(.*) => (.*)\}(.*)$/); + if (braceMatch) { + const [, prefix, , dest, suffix] = braceMatch; + return `${prefix}${dest}${suffix}`.replace(/\/{2,}/g, '/'); + } + const arrowIndex = filePath.indexOf(' => '); + if (arrowIndex !== -1) { + return filePath.slice(arrowIndex + 4); + } + return filePath; +} +function resolveNumstatPath(filePath, trackedPaths) { + if (trackedPaths.has(filePath)) { + return filePath; + } + const destinationPath = extractNumstatDestination(filePath); + if (destinationPath !== filePath && trackedPaths.has(destinationPath)) { + return destinationPath; + } + return filePath; +} +/** + * Parse `git diff --numstat HEAD` output. + * Returns total line diff and a map of fullPath -> LineDiff. + */ +function parseNumstat(numstatOutput, trackedPaths) { + const totalDiff = { added: 0, deleted: 0 }; + const perFileDiff = new Map(); + for (const line of numstatOutput.trim().split('\n').filter(Boolean)) { + const parts = line.split('\t'); + if (parts.length < 3) + continue; + const added = parseInt(parts[0], 10); + const deleted = parseInt(parts[1], 10); + const filePath = resolveNumstatPath(parts[2], trackedPaths); + if (Number.isNaN(added) || Number.isNaN(deleted)) + continue; // binary file + totalDiff.added += added; + totalDiff.deleted += deleted; + perFileDiff.set(filePath, { added, deleted }); + } + return { totalDiff, perFileDiff }; +} +function applyLineDiffsToFiles(files, perFileDiff) { + for (const file of files) { + const diff = perFileDiff.get(file.fullPath); + if (diff) { + file.lineDiff = diff; + } + } +} +//# sourceMappingURL=git.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/git.js.map b/plugins/balance-hud/dist/git.js.map new file mode 100644 index 0000000..b2d7bdb --- /dev/null +++ b/plugins/balance-hud/dist/git.js.map @@ -0,0 +1 @@ +{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/en.d.ts b/plugins/balance-hud/dist/i18n/en.d.ts new file mode 100644 index 0000000..b1d0edd --- /dev/null +++ b/plugins/balance-hud/dist/i18n/en.d.ts @@ -0,0 +1,3 @@ +import type { Messages } from "./types.js"; +export declare const en: Messages; +//# sourceMappingURL=en.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/en.d.ts.map b/plugins/balance-hud/dist/i18n/en.d.ts.map new file mode 100644 index 0000000..0b45ddf --- /dev/null +++ b/plugins/balance-hud/dist/i18n/en.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,EAAE,EAAE,QAsChB,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/en.js b/plugins/balance-hud/dist/i18n/en.js new file mode 100644 index 0000000..326bcf1 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/en.js @@ -0,0 +1,36 @@ +export const en = { + // Labels + "label.context": "Context", + "label.usage": "Usage", + "label.weekly": "Weekly", + "label.approxRam": "Approx RAM", + "label.promptCache": "Cache", + "label.rules": "rules", + "label.hooks": "hooks", + "label.estimatedCost": "Est.", + "label.cost": "Cost", + "label.tokens": "Tokens", + "label.sessionStarted": "Started", + "label.lastReply": "Last reply", + "label.advisor": "Advisor", + "label.compactions": "Compactions", + // Status + "status.limitReached": "Limit reached", + "status.allTodosComplete": "All todos complete", + "status.expired": "expired", + // Format + "format.resets": "resets", + "format.resetsIn": "resets in", + "format.at": "at", + "format.in": "in", + "format.cache": "cache", + "format.out": "out", + "format.tok": "tok", + "format.tokPerSec": "tok/s", + "format.justNow": "just now", + "format.ago": "ago", + // Init + "init.initializing": "[balance-hud] Initializing...", + "init.macosNote": "[balance-hud] Note: On macOS, you may need to restart Claude Code for the HUD to appear.", +}; +//# sourceMappingURL=en.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/en.js.map b/plugins/balance-hud/dist/i18n/en.js.map new file mode 100644 index 0000000..4f47b39 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/en.js.map @@ -0,0 +1 @@ +{"version":3,"file":"en.js","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,EAAE,GAAa;IAC1B,SAAS;IACT,eAAe,EAAE,SAAS;IAC1B,aAAa,EAAE,OAAO;IACtB,cAAc,EAAE,QAAQ;IACxB,iBAAiB,EAAE,YAAY;IAC/B,mBAAmB,EAAE,OAAO;IAC5B,aAAa,EAAE,OAAO;IACtB,aAAa,EAAE,OAAO;IACtB,qBAAqB,EAAE,MAAM;IAC7B,YAAY,EAAE,MAAM;IACpB,cAAc,EAAE,QAAQ;IACxB,sBAAsB,EAAE,SAAS;IACjC,iBAAiB,EAAE,YAAY;IAC/B,eAAe,EAAE,SAAS;IAC1B,mBAAmB,EAAE,aAAa;IAElC,SAAS;IACT,qBAAqB,EAAE,eAAe;IACtC,yBAAyB,EAAE,oBAAoB;IAC/C,gBAAgB,EAAE,SAAS;IAE3B,SAAS;IACT,eAAe,EAAE,QAAQ;IACzB,iBAAiB,EAAE,WAAW;IAC9B,WAAW,EAAE,IAAI;IACjB,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,OAAO;IACvB,YAAY,EAAE,KAAK;IACnB,YAAY,EAAE,KAAK;IACnB,kBAAkB,EAAE,OAAO;IAC3B,gBAAgB,EAAE,UAAU;IAC5B,YAAY,EAAE,KAAK;IAEnB,OAAO;IACP,mBAAmB,EAAE,8BAA8B;IACnD,gBAAgB,EACd,yFAAyF;CAC5F,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/index.d.ts b/plugins/balance-hud/dist/i18n/index.d.ts new file mode 100644 index 0000000..0c93c56 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/index.d.ts @@ -0,0 +1,9 @@ +import type { Language, MessageKey, Messages } from "./types.js"; +export type { Language, MessageKey, Messages }; +type CanonicalLanguage = "en" | "zh-Hans"; +export declare function setLanguage(lang: Language): void; +export declare function getLanguage(): Language; +export declare function getCanonicalLanguage(): CanonicalLanguage; +export declare function isCjkLanguage(): boolean; +export declare function t(key: MessageKey): string; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/index.d.ts.map b/plugins/balance-hud/dist/i18n/index.d.ts.map new file mode 100644 index 0000000..28acdb9 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIjE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAE/C,KAAK,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC;AAmB1C,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAEhD;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAEtC;AAGD,wBAAgB,oBAAoB,IAAI,iBAAiB,CAExD;AAGD,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED,wBAAgB,CAAC,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAGzC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/index.js b/plugins/balance-hud/dist/i18n/index.js new file mode 100644 index 0000000..fd4c1df --- /dev/null +++ b/plugins/balance-hud/dist/i18n/index.js @@ -0,0 +1,35 @@ +import { en } from "./en.js"; +import { zhHans } from "./zh-Hans.js"; +const locales = { + en, + zh: zhHans, + "zh-Hans": zhHans, +}; +// Resolve short language tags to canonical BCP 47 forms. +// Based on CLDR likely subtags: zh → zh-Hans-CN +// https://www.unicode.org/cldr/charts/latest/supplemental/likely_subtags.html +const CANONICAL = { + "en": "en", + "zh": "zh-Hans", + "zh-Hans": "zh-Hans", +}; +let currentLanguage = "en"; +export function setLanguage(lang) { + currentLanguage = lang; +} +export function getLanguage() { + return currentLanguage; +} +// https://www.rfc-editor.org/info/bcp47 +export function getCanonicalLanguage() { + return CANONICAL[currentLanguage] ?? "en"; +} +// https://www.unicode.org/reports/tr11/ +export function isCjkLanguage() { + return getCanonicalLanguage() === "zh-Hans"; +} +export function t(key) { + const canon = getCanonicalLanguage(); + return locales[canon]?.[key] ?? locales.en[key] ?? key; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/index.js.map b/plugins/balance-hud/dist/i18n/index.js.map new file mode 100644 index 0000000..a866b63 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAMtC,MAAM,OAAO,GAA+C;IAC1D,EAAE;IACF,EAAE,EAAE,MAAM;IACV,SAAS,EAAE,MAAM;CAClB,CAAC;AAEF,yDAAyD;AACzD,gDAAgD;AAChD,8EAA8E;AAC9E,MAAM,SAAS,GAAwC;IACrD,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,SAAS;CACrB,CAAC;AAEF,IAAI,eAAe,GAAa,IAAI,CAAC;AAErC,MAAM,UAAU,WAAW,CAAC,IAAc;IACxC,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,oBAAoB;IAClC,OAAO,SAAS,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC;AAC5C,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,aAAa;IAC3B,OAAO,oBAAoB,EAAE,KAAK,SAAS,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,CAAC,CAAC,GAAe;IAC/B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,OAAO,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;AACzD,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/types.d.ts b/plugins/balance-hud/dist/i18n/types.d.ts new file mode 100644 index 0000000..3175ff7 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/types.d.ts @@ -0,0 +1,4 @@ +export type MessageKey = "label.context" | "label.usage" | "label.weekly" | "label.approxRam" | "label.promptCache" | "label.rules" | "label.hooks" | "label.estimatedCost" | "label.cost" | "label.tokens" | "label.sessionStarted" | "label.lastReply" | "label.advisor" | "label.compactions" | "status.limitReached" | "status.allTodosComplete" | "status.expired" | "format.resets" | "format.resetsIn" | "format.at" | "format.in" | "format.cache" | "format.out" | "format.tok" | "format.tokPerSec" | "format.justNow" | "format.ago" | "init.initializing" | "init.macosNote"; +export type Messages = Record; +export type Language = "en" | "zh" | "zh-Hans"; +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/types.d.ts.map b/plugins/balance-hud/dist/i18n/types.d.ts.map new file mode 100644 index 0000000..ae7c99d --- /dev/null +++ b/plugins/balance-hud/dist/i18n/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/i18n/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAElB,eAAe,GACf,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,aAAa,GACb,aAAa,GACb,qBAAqB,GACrB,YAAY,GACZ,cAAc,GACd,sBAAsB,GACtB,iBAAiB,GACjB,eAAe,GACf,mBAAmB,GAEnB,qBAAqB,GACrB,yBAAyB,GACzB,gBAAgB,GAEhB,eAAe,GACf,iBAAiB,GACjB,WAAW,GACX,WAAW,GACX,cAAc,GACd,YAAY,GACZ,YAAY,GACZ,kBAAkB,GAClB,gBAAgB,GAChB,YAAY,GAEZ,mBAAmB,GACnB,gBAAgB,CAAC;AAErB,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAElD,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/types.js b/plugins/balance-hud/dist/i18n/types.js new file mode 100644 index 0000000..718fd38 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/types.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/types.js.map b/plugins/balance-hud/dist/i18n/types.js.map new file mode 100644 index 0000000..1355dee --- /dev/null +++ b/plugins/balance-hud/dist/i18n/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/i18n/types.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh-Hans.d.ts b/plugins/balance-hud/dist/i18n/zh-Hans.d.ts new file mode 100644 index 0000000..04f5a11 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh-Hans.d.ts @@ -0,0 +1,3 @@ +import type { Messages } from "./types.js"; +export declare const zhHans: Messages; +//# sourceMappingURL=zh-Hans.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh-Hans.d.ts.map b/plugins/balance-hud/dist/i18n/zh-Hans.d.ts.map new file mode 100644 index 0000000..16e7384 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh-Hans.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"zh-Hans.d.ts","sourceRoot":"","sources":["../../src/i18n/zh-Hans.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,MAAM,EAAE,QAsCpB,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh-Hans.js b/plugins/balance-hud/dist/i18n/zh-Hans.js new file mode 100644 index 0000000..8e70034 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh-Hans.js @@ -0,0 +1,36 @@ +export const zhHans = { + // Labels + "label.context": "上下文", + "label.usage": "用量", + "label.weekly": "本周", + "label.approxRam": "内存", + "label.promptCache": "缓存", + "label.rules": "规则", + "label.hooks": "钩子", + "label.estimatedCost": "估算", + "label.cost": "费用", + "label.tokens": "词元", + "label.sessionStarted": "开始", + "label.lastReply": "上次回复", + "label.advisor": "顾问", + "label.compactions": "压缩次数", + // Status + "status.limitReached": "已达上限", + "status.allTodosComplete": "全部完成", + "status.expired": "已过期", + // Format + "format.resets": "重置于", + "format.resetsIn": "重置剩余", + "format.at": "", + "format.in": "输入", + "format.cache": "缓存", + "format.out": "输出", + "format.tok": "词元", + "format.tokPerSec": "tok/s", + "format.justNow": "刚刚", + "format.ago": "前", + // Init + "init.initializing": "[balance-hud] 正在初始化...", + "init.macosNote": "[balance-hud] 注意:在 macOS 上,您可能需要重启 Claude Code 才能显示 HUD。", +}; +//# sourceMappingURL=zh-Hans.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh-Hans.js.map b/plugins/balance-hud/dist/i18n/zh-Hans.js.map new file mode 100644 index 0000000..9855178 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh-Hans.js.map @@ -0,0 +1 @@ +{"version":3,"file":"zh-Hans.js","sourceRoot":"","sources":["../../src/i18n/zh-Hans.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,MAAM,GAAa;IAC9B,SAAS;IACT,eAAe,EAAE,KAAK;IACtB,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,IAAI;IACpB,iBAAiB,EAAE,IAAI;IACvB,mBAAmB,EAAE,IAAI;IACzB,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,IAAI;IACnB,qBAAqB,EAAE,IAAI;IAC3B,YAAY,EAAE,IAAI;IAClB,cAAc,EAAE,IAAI;IACpB,sBAAsB,EAAE,IAAI;IAC5B,iBAAiB,EAAE,MAAM;IACzB,eAAe,EAAE,IAAI;IACrB,mBAAmB,EAAE,MAAM;IAE3B,SAAS;IACT,qBAAqB,EAAE,MAAM;IAC7B,yBAAyB,EAAE,MAAM;IACjC,gBAAgB,EAAE,KAAK;IAEvB,SAAS;IACT,eAAe,EAAE,KAAK;IACtB,iBAAiB,EAAE,MAAM;IACzB,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,IAAI;IAClB,kBAAkB,EAAE,OAAO;IAC3B,gBAAgB,EAAE,IAAI;IACtB,YAAY,EAAE,GAAG;IAEjB,OAAO;IACP,mBAAmB,EAAE,uBAAuB;IAC5C,gBAAgB,EACd,yDAAyD;CAC5D,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh.d.ts b/plugins/balance-hud/dist/i18n/zh.d.ts new file mode 100644 index 0000000..ac28de3 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh.d.ts @@ -0,0 +1,3 @@ +import type { Messages } from "./types.js"; +export declare const zh: Messages; +//# sourceMappingURL=zh.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh.d.ts.map b/plugins/balance-hud/dist/i18n/zh.d.ts.map new file mode 100644 index 0000000..f916d3e --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"zh.d.ts","sourceRoot":"","sources":["../../src/i18n/zh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,EAAE,EAAE,QAoChB,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh.js b/plugins/balance-hud/dist/i18n/zh.js new file mode 100644 index 0000000..dd46f94 --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh.js @@ -0,0 +1,34 @@ +export const zh = { + // Labels + "label.context": "上下文", + "label.usage": "用量", + "label.weekly": "本周", + "label.approxRam": "内存", + "label.promptCache": "缓存", + "label.rules": "规则", + "label.hooks": "钩子", + "label.estimatedCost": "估算", + "label.cost": "费用", + "label.tokens": "令牌", + "label.sessionStarted": "开始", + "label.lastReply": "上次回复", + // Status + "status.limitReached": "已达上限", + "status.allTodosComplete": "全部完成", + "status.expired": "已过期", + // Format + "format.resets": "重置于", + "format.resetsIn": "重置剩余", + "format.at": "", + "format.in": "输入", + "format.cache": "缓存", + "format.out": "输出", + "format.tok": "令牌", + "format.tokPerSec": "tok/s", + "format.justNow": "刚刚", + "format.ago": "前", + // Init + "init.initializing": "[balance-hud] 正在初始化...", + "init.macosNote": "[balance-hud] 注意:在 macOS 上,您可能需要重启 Claude Code 才能显示 HUD。", +}; +//# sourceMappingURL=zh.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/i18n/zh.js.map b/plugins/balance-hud/dist/i18n/zh.js.map new file mode 100644 index 0000000..e5ddc0d --- /dev/null +++ b/plugins/balance-hud/dist/i18n/zh.js.map @@ -0,0 +1 @@ +{"version":3,"file":"zh.js","sourceRoot":"","sources":["../../src/i18n/zh.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,EAAE,GAAa;IAC1B,SAAS;IACT,eAAe,EAAE,KAAK;IACtB,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,IAAI;IACpB,iBAAiB,EAAE,IAAI;IACvB,mBAAmB,EAAE,IAAI;IACzB,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,IAAI;IACnB,qBAAqB,EAAE,IAAI;IAC3B,YAAY,EAAE,IAAI;IAClB,cAAc,EAAE,IAAI;IACpB,sBAAsB,EAAE,IAAI;IAC5B,iBAAiB,EAAE,MAAM;IAEzB,SAAS;IACT,qBAAqB,EAAE,MAAM;IAC7B,yBAAyB,EAAE,MAAM;IACjC,gBAAgB,EAAE,KAAK;IAEvB,SAAS;IACT,eAAe,EAAE,KAAK;IACtB,iBAAiB,EAAE,MAAM;IACzB,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,IAAI;IAClB,kBAAkB,EAAE,OAAO;IAC3B,gBAAgB,EAAE,IAAI;IACtB,YAAY,EAAE,GAAG;IAEjB,OAAO;IACP,mBAAmB,EAAE,uBAAuB;IAC5C,gBAAgB,EACd,yDAAyD;CAC5D,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/index.d.ts b/plugins/balance-hud/dist/index.d.ts new file mode 100644 index 0000000..5b796fe --- /dev/null +++ b/plugins/balance-hud/dist/index.d.ts @@ -0,0 +1,41 @@ +import { readStdin, getUsageFromStdin } from "./stdin.js"; +import { parseTranscript } from "./transcript.js"; +import { render } from "./render/index.js"; +import { countConfigs } from "./config-reader.js"; +import { getGitStatus } from "./git.js"; +import { loadConfig } from "./config.js"; +import { parseExtraCmdArg, runExtraCmd } from "./extra-cmd.js"; +import { getClaudeCodeVersion } from "./version.js"; +import { getMemoryUsage } from "./memory.js"; +import { applyContextWindowFallback } from "./context-cache.js"; +import { getUsageFromExternalSnapshot, writeExternalUsageSnapshot } from "./external-usage.js"; +export { getUsageFromExternalSnapshot, writeExternalUsageSnapshot } from "./external-usage.js"; +export type MainDeps = { + readStdin: typeof readStdin; + getUsageFromStdin: typeof getUsageFromStdin; + getUsageFromExternalSnapshot: typeof getUsageFromExternalSnapshot; + writeExternalUsageSnapshot: typeof writeExternalUsageSnapshot; + parseTranscript: typeof parseTranscript; + countConfigs: typeof countConfigs; + getGitStatus: typeof getGitStatus; + loadConfig: typeof loadConfig; + parseExtraCmdArg: typeof parseExtraCmdArg; + runExtraCmd: typeof runExtraCmd; + getClaudeCodeVersion: typeof getClaudeCodeVersion; + getMemoryUsage: typeof getMemoryUsage; + applyContextWindowFallback: typeof applyContextWindowFallback; + render: typeof render; + now: () => number; + log: (...args: unknown[]) => void; +}; +/** + * Returns true when the HUD is disabled for this invocation via the + * CLAUDE_HUD_DISABLE environment variable. Any non-blank value other than an + * explicit negative (`0`, `false`, `off`, `no`, case-insensitive) disables the + * HUD, so users can launch sessions without it (`CLAUDE_HUD_DISABLE=1 claude`) + * while keeping the statusLine entry in settings.json intact. + */ +export declare function isHudDisabled(env?: NodeJS.ProcessEnv): boolean; +export declare function main(overrides?: Partial): Promise; +export declare function formatSessionDuration(sessionStart?: Date, now?: () => number): string; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/index.d.ts.map b/plugins/balance-hud/dist/index.d.ts.map new file mode 100644 index 0000000..09add6f --- /dev/null +++ b/plugins/balance-hud/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAI/F,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAI/F,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,4BAA4B,EAAE,OAAO,4BAA4B,CAAC;IAClE,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAClD,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAM3E;AAED,wBAAsB,IAAI,CAAC,SAAS,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwI3E;AAED,wBAAgB,qBAAqB,CACnC,YAAY,CAAC,EAAE,IAAI,EACnB,GAAG,GAAE,MAAM,MAAyB,GACnC,MAAM,CAcR"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/index.js b/plugins/balance-hud/dist/index.js new file mode 100644 index 0000000..17f3ff6 --- /dev/null +++ b/plugins/balance-hud/dist/index.js @@ -0,0 +1,221 @@ +import { readStdin, getUsageFromStdin } from "./stdin.js"; +import { parseTranscript } from "./transcript.js"; +import { render } from "./render/index.js"; +import { countConfigs } from "./config-reader.js"; +import { getGitStatus } from "./git.js"; +import { loadConfig } from "./config.js"; +import { parseExtraCmdArg, runExtraCmd } from "./extra-cmd.js"; +import { getClaudeCodeVersion } from "./version.js"; +import { getMemoryUsage } from "./memory.js"; +import { resolveEffortLevel } from "./effort.js"; +import { applyContextWindowFallback } from "./context-cache.js"; +import { getUsageFromExternalSnapshot, writeExternalUsageSnapshot } from "./external-usage.js"; +import { setLanguage, t } from "./i18n/index.js"; +export { getUsageFromExternalSnapshot, writeExternalUsageSnapshot } from "./external-usage.js"; +import { fileURLToPath } from "node:url"; +import { realpathSync, existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +/** + * Returns true when the HUD is disabled for this invocation via the + * CLAUDE_HUD_DISABLE environment variable. Any non-blank value other than an + * explicit negative (`0`, `false`, `off`, `no`, case-insensitive) disables the + * HUD, so users can launch sessions without it (`CLAUDE_HUD_DISABLE=1 claude`) + * while keeping the statusLine entry in settings.json intact. + */ +export function isHudDisabled(env = process.env) { + const value = (env.BALANCE_HUD_DISABLE || env.CLAUDE_HUD_DISABLE)?.trim().toLowerCase(); + if (value === undefined || value === "") { + return false; + } + return value !== "0" && value !== "false" && value !== "off" && value !== "no"; +} +export async function main(overrides = {}) { + if (isHudDisabled()) { + // Print nothing so Claude Code renders an empty statusline, and skip all + // work (stdin parse, transcript scan, git) for the ~300ms polling loop. + return; + } + const deps = { + readStdin, + getUsageFromStdin, + getUsageFromExternalSnapshot, + writeExternalUsageSnapshot, + parseTranscript, + countConfigs, + getGitStatus, + loadConfig, + parseExtraCmdArg, + runExtraCmd, + getClaudeCodeVersion, + getMemoryUsage, + applyContextWindowFallback, + render, + now: () => Date.now(), + log: console.log, + ...overrides, + }; + try { + const stdin = await deps.readStdin(); + if (!stdin) { + // Running without stdin - this happens during setup verification + const config = await deps.loadConfig(); + setLanguage(config.language); + const isMacOS = process.platform === "darwin"; + deps.log(t("init.initializing")); + if (isMacOS) { + deps.log(t("init.macosNote")); + } + return; + } + const transcriptPath = stdin.transcript_path ?? ""; + const transcript = await deps.parseTranscript(transcriptPath); + deps.applyContextWindowFallback(stdin, {}, transcript.sessionName, { + lastCompactBoundaryAt: transcript.lastCompactBoundaryAt, + lastCompactPostTokens: transcript.lastCompactPostTokens, + }); + const { claudeMdCount, rulesCount, mcpCount, hooksCount, outputStyle } = await deps.countConfigs(stdin.cwd); + const config = await deps.loadConfig(); + setLanguage(config.language); + const gitStatus = config.gitStatus.enabled + ? await deps.getGitStatus(stdin.cwd) + : null; + let usageData = null; + const shouldReadUsage = config.display.showUsage !== false; + const shouldWriteUsage = Boolean(config.display.externalUsageWritePath); + const stdinUsage = shouldReadUsage || shouldWriteUsage + ? deps.getUsageFromStdin(stdin) + : null; + if (shouldWriteUsage && stdinUsage) { + deps.writeExternalUsageSnapshot(config, stdinUsage, deps.now()); + } + // ── Auto-detect balance snapshot from plugin directory ──── + // This allows balance-hud to show API balance AND usage quotas without + // manual externalUsagePath configuration. The auto_refresh daemon writes + // balance_usage.json next to the plugin root. + const autoUsage = tryLoadAutoSnapshot(config, deps); + + if (shouldReadUsage) { + usageData = stdinUsage; + if (!usageData) { + usageData = deps.getUsageFromExternalSnapshot(config, deps.now()); + } + else if (config.display.externalUsagePath) { + const ext = deps.getUsageFromExternalSnapshot(config, deps.now()); + if (ext != null) { + usageData = { + ...usageData, + ...(ext.balanceLabel != null && { balanceLabel: ext.balanceLabel }), + // If stdin did not provide sevenDay (e.g. third-party clients like the + // Claudian Obsidian plugin that only surface five_hour), fall back to the + // external snapshot so the weekly limit still shows in the HUD. + ...(usageData.sevenDay == null && ext.sevenDay != null && { + sevenDay: ext.sevenDay, + sevenDayResetAt: ext.sevenDayResetAt ?? null, + }), + }; + } + } + // ── Auto-detect balance snapshot (no externalUsagePath configured) ── + if (autoUsage) { + if (usageData) { + if (usageData.balanceLabel == null && autoUsage.balanceLabel != null) { + usageData = { ...usageData, balanceLabel: autoUsage.balanceLabel }; + } + if (usageData.fiveHour == null && autoUsage.fiveHour != null) { + usageData = { ...usageData, fiveHour: autoUsage.fiveHour, fiveHourResetAt: autoUsage.fiveHourResetAt ?? null }; + } + if (usageData.sevenDay == null && autoUsage.sevenDay != null) { + usageData = { ...usageData, sevenDay: autoUsage.sevenDay, sevenDayResetAt: autoUsage.sevenDayResetAt ?? null }; + } + } + else { + usageData = autoUsage; + } + } + } + const extraCmd = deps.parseExtraCmdArg(); + const extraLabel = extraCmd ? await deps.runExtraCmd(extraCmd) : null; + const sessionDuration = formatSessionDuration(transcript.sessionStart, deps.now); + const claudeCodeVersion = config.display.showClaudeCodeVersion + ? await deps.getClaudeCodeVersion() + : undefined; + const effortInfo = config.display.showEffortLevel + ? resolveEffortLevel(stdin.effort) + : null; + const memoryUsage = config.display.showMemoryUsage && config.lineLayout === "expanded" + ? await deps.getMemoryUsage() + : null; + const ctx = { + stdin, + transcript, + claudeMdCount, + rulesCount, + mcpCount, + hooksCount, + sessionDuration, + gitStatus, + usageData, + memoryUsage, + config, + extraLabel, + outputStyle, + claudeCodeVersion, + effortLevel: effortInfo?.level, + effortSymbol: effortInfo?.symbol, + }; + deps.render(ctx); + } + catch (error) { + deps.log("[balance-hud] Error:", error instanceof Error ? error.message : "Unknown error"); + } +} +/** + * Try to load the full usage snapshot from the plugin's balance_usage.json. + * The auto_refresh daemon writes this file after each DeepSeek API poll. + * Returns a usage object { fiveHour, sevenDay, fiveHourResetAt, sevenDayResetAt, balanceLabel } + * or null if not available / stale. + */ +function tryLoadAutoSnapshot(config, deps) { + try { + // If externalUsagePath is explicitly set, skip auto-detection + if (config.display.externalUsagePath) return null; + // Resolve plugin root: dist/index.js → ../balance_usage.json + const distDir = dirname(fileURLToPath(import.meta.url)); + const pluginRoot = join(distDir, '..'); + return deps.getUsageFromExternalSnapshot( + { ...config, display: { ...config.display, externalUsagePath: join(pluginRoot, 'balance_usage.json') } }, + deps.now() + ); + } catch { + return null; + } +} + +export function formatSessionDuration(sessionStart, now = () => Date.now()) { + if (!sessionStart) { + return ""; + } + const ms = now() - sessionStart.getTime(); + const mins = Math.floor(ms / 60000); + if (mins < 1) + return "<1m"; + if (mins < 60) + return `${mins}m`; + const hours = Math.floor(mins / 60); + const remainingMins = mins % 60; + return `${hours}h ${remainingMins}m`; +} +const scriptPath = fileURLToPath(import.meta.url); +const argvPath = process.argv[1]; +const isSamePath = (a, b) => { + try { + return realpathSync(a) === realpathSync(b); + } + catch { + return a === b; + } +}; +if (argvPath && isSamePath(argvPath, scriptPath)) { + void main(); +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/index.js.map b/plugins/balance-hud/dist/index.js.map new file mode 100644 index 0000000..79cb54e --- /dev/null +++ b/plugins/balance-hud/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/memory.d.ts b/plugins/balance-hud/dist/memory.d.ts new file mode 100644 index 0000000..1813668 --- /dev/null +++ b/plugins/balance-hud/dist/memory.d.ts @@ -0,0 +1,19 @@ +import type { MemoryInfo } from './types.js'; +type MemoryReader = () => { + totalBytes: number; + freeBytes: number; +}; +export declare function parseVmStat(output: string): { + pageSize: number; + active: number; + wired: number; +} | null; +export declare function parseLinuxMeminfo(output: string): { + totalBytes: number; + freeBytes: number; +} | null; +export declare function getMemoryUsage(): Promise; +export declare function formatBytes(bytes: number): string; +export declare function _setMemoryReaderForTests(reader: MemoryReader | null): void; +export {}; +//# sourceMappingURL=memory.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/memory.d.ts.map b/plugins/balance-hud/dist/memory.d.ts.map new file mode 100644 index 0000000..f64f916 --- /dev/null +++ b/plugins/balance-hud/dist/memory.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,KAAK,YAAY,GAAG,MAAM;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,GACb;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAa5D;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GACb;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYlD;AAuCD,wBAAsB,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAuBjE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgBjD;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAM1E"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/memory.js b/plugins/balance-hud/dist/memory.js new file mode 100644 index 0000000..f163038 --- /dev/null +++ b/plugins/balance-hud/dist/memory.js @@ -0,0 +1,109 @@ +import os from 'node:os'; +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { createDebug } from './debug.js'; +const debug = createDebug('memory'); +export function parseVmStat(output) { + const pageSizeMatch = output.match(/page size of (\d+) bytes/); + if (!pageSizeMatch) + return null; + const activeMatch = output.match(/Pages active:\s+(\d+)/); + const wiredMatch = output.match(/Pages wired down:\s+(\d+)/); + if (!activeMatch || !wiredMatch) + return null; + return { + pageSize: Number(pageSizeMatch[1]), + active: Number(activeMatch[1]), + wired: Number(wiredMatch[1]), + }; +} +export function parseLinuxMeminfo(output) { + const totalMatch = output.match(/^MemTotal:\s+(\d+)\s+kB/m); + const availMatch = output.match(/^MemAvailable:\s+(\d+)\s+kB/m); + if (!totalMatch || !availMatch) + return null; + const totalBytes = Number(totalMatch[1]) * 1024; + const freeBytes = Number(availMatch[1]) * 1024; + if (!Number.isFinite(totalBytes) || !Number.isFinite(freeBytes)) { + return null; + } + return { totalBytes, freeBytes }; +} +const readDefaultMemory = () => ({ + totalBytes: os.totalmem(), + freeBytes: os.freemem(), +}); +const readLinuxMemory = () => { + try { + const content = readFileSync('/proc/meminfo', 'utf8'); + return parseLinuxMeminfo(content) ?? readDefaultMemory(); + } + catch (err) { + debug('Failed to read /proc/meminfo:', err instanceof Error ? err.message : err); + return readDefaultMemory(); + } +}; +const readMacOSMemory = () => { + try { + const output = execFileSync('/usr/bin/vm_stat', { + encoding: 'utf8', + timeout: 5000, + }); + const parsed = parseVmStat(output); + if (!parsed) + return readDefaultMemory(); + const totalBytes = os.totalmem(); + const usedBytes = (parsed.active + parsed.wired) * parsed.pageSize; + return { totalBytes, freeBytes: totalBytes - usedBytes }; + } + catch (err) { + debug('Failed to run vm_stat:', err instanceof Error ? err.message : err); + return readDefaultMemory(); + } +}; +let readMemory = process.platform === 'darwin' ? readMacOSMemory : + process.platform === 'linux' ? readLinuxMemory : + readDefaultMemory; +export async function getMemoryUsage() { + try { + const { totalBytes, freeBytes } = readMemory(); + if (!Number.isFinite(totalBytes) || totalBytes <= 0) { + return null; + } + const safeFreeBytes = Number.isFinite(freeBytes) + ? Math.min(Math.max(freeBytes, 0), totalBytes) + : 0; + const usedBytes = totalBytes - safeFreeBytes; + const usedPercent = Math.round((usedBytes / totalBytes) * 100); + return { + totalBytes, + usedBytes, + freeBytes: safeFreeBytes, + usedPercent: Math.min(Math.max(usedPercent, 0), 100), + }; + } + catch (err) { + debug('Failed to get memory usage:', err instanceof Error ? err.message : err); + return null; + } +} +export function formatBytes(bytes) { + if (!Number.isFinite(bytes) || bytes <= 0) { + return '0 B'; + } + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let value = bytes; + let unitIndex = 0; + while (value >= 1024 && unitIndex < units.length - 1) { + value /= 1024; + unitIndex += 1; + } + const fractionDigits = value >= 10 || unitIndex === 0 ? 0 : 1; + return `${value.toFixed(fractionDigits)} ${units[unitIndex]}`; +} +export function _setMemoryReaderForTests(reader) { + readMemory = reader ?? (process.platform === 'darwin' ? readMacOSMemory : + process.platform === 'linux' ? readLinuxMemory : + readDefaultMemory); +} +//# sourceMappingURL=memory.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/memory.js.map b/plugins/balance-hud/dist/memory.js.map new file mode 100644 index 0000000..f8f4df1 --- /dev/null +++ b/plugins/balance-hud/dist/memory.js.map @@ -0,0 +1 @@ +{"version":3,"file":"memory.js","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/agents-line.d.ts b/plugins/balance-hud/dist/render/agents-line.d.ts new file mode 100644 index 0000000..a9e0584 --- /dev/null +++ b/plugins/balance-hud/dist/render/agents-line.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from '../types.js'; +export declare function renderAgentsLine(ctx: RenderContext): string | null; +//# sourceMappingURL=agents-line.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/agents-line.d.ts.map b/plugins/balance-hud/dist/render/agents-line.d.ts.map new file mode 100644 index 0000000..0febea2 --- /dev/null +++ b/plugins/balance-hud/dist/render/agents-line.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"agents-line.d.ts","sourceRoot":"","sources":["../../src/render/agents-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,aAAa,CAAC;AAO7D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA2BlE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/agents-line.js b/plugins/balance-hud/dist/render/agents-line.js new file mode 100644 index 0000000..de63c38 --- /dev/null +++ b/plugins/balance-hud/dist/render/agents-line.js @@ -0,0 +1,67 @@ +import { yellow, green, magenta, label } from './colors.js'; +import { truncateString } from '../utils/truncate.js'; +const MAX_RECENT_COMPLETED = 2; +const MAX_AGENTS_SHOWN = 3; +export function renderAgentsLine(ctx) { + const { agents } = ctx.transcript; + const colors = ctx.config?.colors; + const runningAgents = agents.filter((a) => a.status === 'running'); + const recentCompleted = agents + .filter((a) => a.status === 'completed') + .slice(-MAX_RECENT_COMPLETED); + const seen = new Set(); + const toShow = [...runningAgents, ...recentCompleted] + .filter((a) => { + if (seen.has(a.id)) + return false; + seen.add(a.id); + return true; + }) + .slice(-MAX_AGENTS_SHOWN); + if (toShow.length === 0) { + return null; + } + const lines = []; + for (const agent of toShow) { + lines.push(formatAgent(agent, colors)); + } + return lines.join('\n'); +} +function getStatusIcon(status) { + switch (status) { + case 'running': + return yellow('◐'); + case 'completed': + default: + return green('✓'); + } +} +function formatAgent(agent, colors) { + const statusIcon = getStatusIcon(agent.status); + const type = magenta(agent.type); + const model = agent.model ? label(`[${agent.model}]`, colors) : ''; + const desc = agent.description + ? label(`: ${truncateString(agent.description, 40)}`, colors) + : ''; + const elapsed = formatElapsed(agent); + return `${statusIcon} ${type}${model ? ` ${model}` : ''}${desc} ${label(`(${elapsed})`, colors)}`; +} +function formatElapsed(agent) { + const now = Date.now(); + const start = agent.startTime.getTime(); + const end = agent.endTime?.getTime() ?? now; + const ms = Math.max(0, end - start); + if (ms < 1000) + return '<1s'; + if (ms < 60_000) + return `${Math.round(ms / 1000)}s`; + const totalSecs = Math.floor(ms / 1000); + const mins = Math.floor(totalSecs / 60); + const secs = totalSecs % 60; + if (mins < 60) + return `${mins}m ${secs}s`; + const hours = Math.floor(mins / 60); + const remainingMins = mins % 60; + return `${hours}h ${remainingMins}m`; +} +//# sourceMappingURL=agents-line.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/agents-line.js.map b/plugins/balance-hud/dist/render/agents-line.js.map new file mode 100644 index 0000000..8959c38 --- /dev/null +++ b/plugins/balance-hud/dist/render/agents-line.js.map @@ -0,0 +1 @@ +{"version":3,"file":"agents-line.js","sourceRoot":"","sources":["../../src/render/agents-line.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/colors.d.ts b/plugins/balance-hud/dist/render/colors.d.ts new file mode 100644 index 0000000..8eb7fa3 --- /dev/null +++ b/plugins/balance-hud/dist/render/colors.d.ts @@ -0,0 +1,26 @@ +import type { HudColorOverrides } from '../config.js'; +export declare const RESET = "\u001B[0m"; +export declare function green(text: string): string; +export declare function yellow(text: string): string; +export declare function red(text: string): string; +export declare function cyan(text: string): string; +export declare function magenta(text: string): string; +export declare function dim(text: string): string; +export declare function claudeOrange(text: string): string; +export declare function model(text: string, colors?: Partial): string; +export declare function project(text: string, colors?: Partial): string; +export declare function git(text: string, colors?: Partial): string; +export declare function gitBranch(text: string, colors?: Partial): string; +export declare function label(text: string, colors?: Partial): string; +export declare function custom(text: string, colors?: Partial): string; +export declare function warning(text: string, colors?: Partial): string; +export declare function critical(text: string, colors?: Partial): string; +export interface ContextThresholds { + warning?: number; + critical?: number; +} +export declare function getContextColor(percent: number, colors?: Partial, thresholds?: ContextThresholds): string; +export declare function getQuotaColor(percent: number, colors?: Partial): string; +export declare function quotaBar(percent: number, width?: number, colors?: Partial): string; +export declare function coloredBar(percent: number, width?: number, colors?: Partial, thresholds?: ContextThresholds): string; +//# sourceMappingURL=colors.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/colors.d.ts.map b/plugins/balance-hud/dist/render/colors.d.ts.map new file mode 100644 index 0000000..672f653 --- /dev/null +++ b/plugins/balance-hud/dist/render/colors.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/render/colors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEnF,eAAO,MAAM,KAAK,cAAY,CAAC;AAwD/B,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAE/E;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAEjF;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAE7E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAEnF;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAE/E;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAEhF;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAEjF;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAElF;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,UAAU,CAAC,EAAE,iBAAiB,GAC7B,MAAM,CAMR;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAI1F;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CASzG;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAW,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,UAAU,CAAC,EAAE,iBAAiB,GAC7B,MAAM,CASR"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/colors.js b/plugins/balance-hud/dist/render/colors.js new file mode 100644 index 0000000..c800eca --- /dev/null +++ b/plugins/balance-hud/dist/render/colors.js @@ -0,0 +1,136 @@ +export const RESET = '\x1b[0m'; +const DIM = '\x1b[2m'; +const RED = '\x1b[31m'; +const GREEN = '\x1b[32m'; +const YELLOW = '\x1b[33m'; +const MAGENTA = '\x1b[35m'; +const CYAN = '\x1b[36m'; +export const BRIGHT_BLUE = '\x1b[94m'; +export const BRIGHT_GREEN = '\x1b[92m'; +const BRIGHT_MAGENTA = '\x1b[95m'; +const CLAUDE_ORANGE = '\x1b[38;5;208m'; +const ANSI_BY_NAME = { + dim: DIM, + red: RED, + green: GREEN, + yellow: YELLOW, + magenta: MAGENTA, + cyan: CYAN, + brightBlue: BRIGHT_BLUE, + brightGreen: BRIGHT_GREEN, + brightMagenta: BRIGHT_MAGENTA, +}; +/** Convert a hex color string (#rrggbb) to a truecolor ANSI escape sequence. */ +function hexToAnsi(hex) { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + return `\x1b[38;2;${r};${g};${b}m`; +} +/** + * Resolve a color value to an ANSI escape sequence. + * Accepts named presets, 256-color indices (0-255), or hex strings (#rrggbb). + */ +function resolveAnsi(value, fallback) { + if (value === undefined || value === null) { + return fallback; + } + if (typeof value === 'number') { + return `\x1b[38;5;${value}m`; + } + if (typeof value === 'string' && value.startsWith('#') && value.length === 7) { + return hexToAnsi(value); + } + return ANSI_BY_NAME[value] ?? fallback; +} +function colorize(text, color) { + return `${color}${text}${RESET}`; +} +function withOverride(text, value, fallback) { + return colorize(text, resolveAnsi(value, fallback)); +} +export function green(text) { + return colorize(text, GREEN); +} +export function yellow(text) { + return colorize(text, YELLOW); +} +export function red(text) { + return colorize(text, RED); +} +export function cyan(text) { + return colorize(text, CYAN); +} +export function magenta(text) { + return colorize(text, MAGENTA); +} +export function dim(text) { + return colorize(text, DIM); +} +export function brightGreen(text) { + return colorize(text, BRIGHT_GREEN); +} +export function claudeOrange(text) { + return colorize(text, CLAUDE_ORANGE); +} +export function model(text, colors) { + return withOverride(text, colors?.model, CYAN); +} +export function project(text, colors) { + return withOverride(text, colors?.project, YELLOW); +} +export function git(text, colors) { + return withOverride(text, colors?.git, MAGENTA); +} +export function gitBranch(text, colors) { + return withOverride(text, colors?.gitBranch, CYAN); +} +export function label(text, colors) { + return withOverride(text, colors?.label, DIM); +} +export function custom(text, colors) { + return withOverride(text, colors?.custom, CLAUDE_ORANGE); +} +export function warning(text, colors) { + return colorize(text, resolveAnsi(colors?.warning, YELLOW)); +} +export function critical(text, colors) { + return colorize(text, resolveAnsi(colors?.critical, RED)); +} +export function getContextColor(percent, colors, thresholds) { + const critical = thresholds?.critical ?? 85; + const warning = thresholds?.warning ?? 70; + if (percent >= critical) + return resolveAnsi(colors?.critical, RED); + if (percent >= warning) + return resolveAnsi(colors?.warning, YELLOW); + return resolveAnsi(colors?.context, BRIGHT_GREEN); +} +export function getQuotaColor(percent, colors) { + if (percent >= 90) + return resolveAnsi(colors?.critical, RED); + if (percent >= 75) + return resolveAnsi(colors?.usageWarning, BRIGHT_MAGENTA); + return resolveAnsi(colors?.usage, BRIGHT_BLUE); +} +export function quotaBar(percent, width = 10, colors) { + const safeWidth = Number.isFinite(width) ? Math.max(0, Math.round(width)) : 0; + const safePercent = Number.isFinite(percent) ? Math.min(100, Math.max(0, percent)) : 0; + const filled = Math.round((safePercent / 100) * safeWidth); + const empty = safeWidth - filled; + const color = getQuotaColor(safePercent, colors); + const filledChar = colors?.barFilled ?? '█'; + const emptyChar = colors?.barEmpty ?? '░'; + return `${color}${filledChar.repeat(filled)}${DIM}${emptyChar.repeat(empty)}${RESET}`; +} +export function coloredBar(percent, width = 10, colors, thresholds) { + const safeWidth = Number.isFinite(width) ? Math.max(0, Math.round(width)) : 0; + const safePercent = Number.isFinite(percent) ? Math.min(100, Math.max(0, percent)) : 0; + const filled = Math.round((safePercent / 100) * safeWidth); + const empty = safeWidth - filled; + const color = getContextColor(safePercent, colors, thresholds); + const filledChar = colors?.barFilled ?? '█'; + const emptyChar = colors?.barEmpty ?? '░'; + return `${color}${filledChar.repeat(filled)}${DIM}${emptyChar.repeat(empty)}${RESET}`; +} +//# sourceMappingURL=colors.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/colors.js.map b/plugins/balance-hud/dist/render/colors.js.map new file mode 100644 index 0000000..9ff0e43 --- /dev/null +++ b/plugins/balance-hud/dist/render/colors.js.map @@ -0,0 +1 @@ +{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/render/colors.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/format-reset-time.d.ts b/plugins/balance-hud/dist/render/format-reset-time.d.ts new file mode 100644 index 0000000..fd64904 --- /dev/null +++ b/plugins/balance-hud/dist/render/format-reset-time.d.ts @@ -0,0 +1,14 @@ +import type { TimeFormatMode } from '../config.js'; +/** + * Formats a usage-window reset timestamp for display in the HUD. + * + * @param resetAt - The reset timestamp, or null if unknown. + * @param mode - How to express the time: + * - `'relative'` (default) — duration until reset, e.g. `2h 30m` + * - `'absolute'` — wall-clock time, e.g. `at 14:30` (locale-aware) + * - `'both'` — both combined, e.g. `2h 30m, at 14:30` (locale-aware) + * @returns A formatted string, or an empty string when the reset is in the past + * or the date is unknown. + */ +export declare function formatResetTime(resetAt: Date | null, mode?: TimeFormatMode): string; +//# sourceMappingURL=format-reset-time.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/format-reset-time.d.ts.map b/plugins/balance-hud/dist/render/format-reset-time.d.ts.map new file mode 100644 index 0000000..a991cc6 --- /dev/null +++ b/plugins/balance-hud/dist/render/format-reset-time.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"format-reset-time.d.ts","sourceRoot":"","sources":["../../src/render/format-reset-time.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,GAAE,cAA2B,GAAG,MAAM,CAoB/F"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/format-reset-time.js b/plugins/balance-hud/dist/render/format-reset-time.js new file mode 100644 index 0000000..ec94549 --- /dev/null +++ b/plugins/balance-hud/dist/render/format-reset-time.js @@ -0,0 +1,59 @@ +import { t } from '../i18n/index.js'; +/** + * Formats a usage-window reset timestamp for display in the HUD. + * + * @param resetAt - The reset timestamp, or null if unknown. + * @param mode - How to express the time: + * - `'relative'` (default) — duration until reset, e.g. `2h 30m` + * - `'absolute'` — wall-clock time, e.g. `at 14:30` (locale-aware) + * - `'both'` — both combined, e.g. `2h 30m, at 14:30` (locale-aware) + * @returns A formatted string, or an empty string when the reset is in the past + * or the date is unknown. + */ +export function formatResetTime(resetAt, mode = 'relative') { + if (!resetAt) + return ''; + const now = new Date(); + const diffMs = resetAt.getTime() - now.getTime(); + if (diffMs <= 0) + return ''; + if (mode === 'relative') { + return formatRelative(diffMs); + } + const absolute = formatAbsolute(resetAt, now); + if (mode === 'absolute') { + return absolute; + } + // 'both' — comma separator avoids nested parentheses when the caller + // wraps the result in its own (...) parenthetical + return `${formatRelative(diffMs)}, ${absolute}`; +} +function formatRelative(diffMs) { + const diffMins = Math.ceil(diffMs / 60000); + if (diffMins < 60) { + return `${diffMins}m`; + } + const hours = Math.floor(diffMins / 60); + const mins = diffMins % 60; + if (hours >= 24) { + const days = Math.floor(hours / 24); + const remHours = hours % 24; + return remHours > 0 ? `${days}d ${remHours}h` : `${days}d`; + } + return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`; +} +function formatAbsolute(resetAt, now) { + // The "at" prefix is i18n-aware. Locales that bake the preposition into + // "format.resets" (e.g. zh: "重置于") set "format.at" to "" so the time + // is returned bare ("14:30") and the preposition is supplied by the caller. + const at = t('format.at'); + const prefix = at ? `${at} ` : ''; + const timeStr = resetAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + // Show the date only when the reset falls on a different calendar day + if (resetAt.toDateString() === now.toDateString()) { + return `${prefix}${timeStr}`; + } + const dateStr = resetAt.toLocaleDateString([], { month: 'short', day: 'numeric' }); + return `${prefix}${dateStr} ${timeStr}`; +} +//# sourceMappingURL=format-reset-time.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/format-reset-time.js.map b/plugins/balance-hud/dist/render/format-reset-time.js.map new file mode 100644 index 0000000..4b1d531 --- /dev/null +++ b/plugins/balance-hud/dist/render/format-reset-time.js.map @@ -0,0 +1 @@ +{"version":3,"file":"format-reset-time.js","sourceRoot":"","sources":["../../src/render/format-reset-time.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,kBAAkB,CAAC;AAErC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,OAAoB,EAAE,OAAuB,UAAU;IACrF,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IACjD,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAE3B,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAE9C,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,qEAAqE;IACrE,kDAAkD;IAClD,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,KAAK,QAAQ,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;IAE3C,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC;QAClB,OAAO,GAAG,QAAQ,GAAG,CAAC;IACxB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,GAAG,EAAE,CAAC;IAE3B,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,QAAQ,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;IAC7D,CAAC;IAED,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC;AACvD,CAAC;AAED,SAAS,cAAc,CAAC,OAAa,EAAE,GAAS;IAC9C,wEAAwE;IACxE,qEAAqE;IACrE,4EAA4E;IAC5E,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;IAC1B,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAEvF,sEAAsE;IACtE,IAAI,OAAO,CAAC,YAAY,EAAE,KAAK,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC;QAClD,OAAO,GAAG,MAAM,GAAG,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IACnF,OAAO,GAAG,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;AAC1C,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/index.d.ts b/plugins/balance-hud/dist/render/index.d.ts new file mode 100644 index 0000000..8d14d5c --- /dev/null +++ b/plugins/balance-hud/dist/render/index.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from '../types.js'; +export declare function render(ctx: RenderContext): void; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/index.d.ts.map b/plugins/balance-hud/dist/render/index.d.ts.map new file mode 100644 index 0000000..38d2599 --- /dev/null +++ b/plugins/balance-hud/dist/render/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA2gBjD,wBAAgB,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAuE/C"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/index.js b/plugins/balance-hud/dist/render/index.js new file mode 100644 index 0000000..bfed496 --- /dev/null +++ b/plugins/balance-hud/dist/render/index.js @@ -0,0 +1,498 @@ +import { DEFAULT_ELEMENT_ORDER, DEFAULT_MERGE_GROUPS } from '../config.js'; +import { renderSessionLine } from './session-line.js'; +import { renderToolsLine } from './tools-line.js'; +import { renderSkillsLine, renderMcpLine } from './skills-mcp-line.js'; +import { renderAgentsLine } from './agents-line.js'; +import { renderTodosLine } from './todos-line.js'; +import { renderIdentityLine, renderProjectLine, renderAddedDirsLine, renderGitFilesLine, renderEnvironmentLine, renderPromptCacheLine, renderUsageLine, renderBalanceLine, renderMemoryLine, renderSessionTokensLine, renderCompactionsLine, renderSessionTimeLine, } from './lines/index.js'; +import { dim, RESET } from './colors.js'; +import { getTerminalWidth, UNKNOWN_TERMINAL_WIDTH } from '../utils/terminal.js'; +import { codePointCellWidth, isCjkAmbiguousWide } from './width.js'; +// eslint-disable-next-line no-control-regex +const ANSI_ESCAPE_PATTERN = /^(?:\x1b\[[0-9;]*m|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\))/; +// eslint-disable-next-line no-control-regex +const ANSI_ESCAPE_GLOBAL = /(?:\x1b\[[0-9;]*m|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\))/g; +const GRAPHEME_SEGMENTER = typeof Intl.Segmenter === 'function' + ? new Intl.Segmenter(undefined, { granularity: 'grapheme' }) + : null; +function stripAnsi(str) { + return str.replace(ANSI_ESCAPE_GLOBAL, ''); +} +function splitAnsiTokens(str) { + const tokens = []; + let i = 0; + while (i < str.length) { + const ansiMatch = ANSI_ESCAPE_PATTERN.exec(str.slice(i)); + if (ansiMatch) { + tokens.push({ type: 'ansi', value: ansiMatch[0] }); + i += ansiMatch[0].length; + continue; + } + let j = i; + while (j < str.length) { + const nextAnsi = ANSI_ESCAPE_PATTERN.exec(str.slice(j)); + if (nextAnsi) { + break; + } + j += 1; + } + tokens.push({ type: 'text', value: str.slice(i, j) }); + i = j; + } + return tokens; +} +function segmentGraphemes(text) { + if (!text) { + return []; + } + if (!GRAPHEME_SEGMENTER) { + return Array.from(text); + } + return Array.from(GRAPHEME_SEGMENTER.segment(text), segment => segment.segment); +} +function graphemeWidth(grapheme, ambiguousWide) { + if (!grapheme || /^\p{Control}$/u.test(grapheme)) { + return 0; + } + // Emoji glyphs and ZWJ sequences generally render as double-width. + if (/\p{Extended_Pictographic}/u.test(grapheme)) { + return 2; + } + let hasVisibleBase = false; + let width = 0; + for (const char of Array.from(grapheme)) { + if (/^\p{Mark}$/u.test(char) || char === '\u200D' || char === '\uFE0F') { + continue; + } + hasVisibleBase = true; + const codePoint = char.codePointAt(0); + if (codePoint !== undefined) { + width = Math.max(width, codePointCellWidth(codePoint, ambiguousWide)); + } + else { + width = Math.max(width, 1); + } + } + return hasVisibleBase ? width : 0; +} +function visualLength(str) { + const ambiguousWide = isCjkAmbiguousWide(); + let width = 0; + for (const token of splitAnsiTokens(str)) { + if (token.type === 'ansi') { + continue; + } + for (const grapheme of segmentGraphemes(token.value)) { + width += graphemeWidth(grapheme, ambiguousWide); + } + } + return width; +} +function sliceVisible(str, maxVisible) { + if (maxVisible <= 0) { + return ''; + } + const ambiguousWide = isCjkAmbiguousWide(); + let result = ''; + let visibleWidth = 0; + let done = false; + let i = 0; + while (i < str.length && !done) { + const ansiMatch = ANSI_ESCAPE_PATTERN.exec(str.slice(i)); + if (ansiMatch) { + result += ansiMatch[0]; + i += ansiMatch[0].length; + continue; + } + let j = i; + while (j < str.length) { + const nextAnsi = ANSI_ESCAPE_PATTERN.exec(str.slice(j)); + if (nextAnsi) { + break; + } + j += 1; + } + const plainChunk = str.slice(i, j); + for (const grapheme of segmentGraphemes(plainChunk)) { + const graphemeCellWidth = graphemeWidth(grapheme, ambiguousWide); + if (visibleWidth + graphemeCellWidth > maxVisible) { + done = true; + break; + } + result += grapheme; + visibleWidth += graphemeCellWidth; + } + i = j; + } + return result; +} +// OSC 8 close sequence (`\x1b]8;;\x1b\\`) terminates the current hyperlink. +// If truncation cuts inside an open OSC 8 hyperlink, emitting only an SGR +// reset (`\x1b[0m`) is not enough — the terminal keeps treating subsequent +// output as part of the link and renders its underline across the rest of +// the line. This helper returns the close sequence iff the last OSC 8 in +// `str` opened a hyperlink (non-empty URL) without being followed by a +// closer (empty URL). +const OSC8_OPEN_OR_CLOSE = /\x1b\]8;;([^\x07\x1b]*)(?:\x07|\x1b\\)/g; +const OSC8_CLOSE = '\x1b]8;;\x1b\\'; +function closeOpenHyperlink(str) { + let last = null; + let match; + OSC8_OPEN_OR_CLOSE.lastIndex = 0; + while ((match = OSC8_OPEN_OR_CLOSE.exec(str)) !== null) { + last = match; + } + return last && last[1].length > 0 ? OSC8_CLOSE : ''; +} +function truncateToWidth(str, maxWidth) { + if (maxWidth <= 0 || visualLength(str) <= maxWidth) { + return str; + } + const suffix = maxWidth >= 3 ? '...' : '.'.repeat(maxWidth); + const keep = Math.max(0, maxWidth - suffix.length); + const sliced = sliceVisible(str, keep); + // Close the hyperlink (if any) before the ellipsis so the suffix renders + // as plain text rather than as part of the truncated link. + return `${sliced}${closeOpenHyperlink(sliced)}${suffix}${RESET}`; +} +function splitLineBySeparators(line) { + const segments = []; + const separators = []; + let currentStart = 0; + let i = 0; + while (i < line.length) { + const ansiMatch = ANSI_ESCAPE_PATTERN.exec(line.slice(i)); + if (ansiMatch) { + i += ansiMatch[0].length; + continue; + } + const separator = line.startsWith(' | ', i) + ? ' | ' + : (line.startsWith(' │ ', i) ? ' │ ' : null); + if (separator) { + segments.push(line.slice(currentStart, i)); + separators.push(separator); + i += separator.length; + currentStart = i; + continue; + } + i += 1; + } + segments.push(line.slice(currentStart)); + return { segments, separators }; +} +function splitWrapParts(line) { + const { segments, separators } = splitLineBySeparators(line); + if (segments.length === 0) { + return []; + } + let parts = [{ + separator: '', + segment: segments[0], + }]; + for (let segmentIndex = 1; segmentIndex < segments.length; segmentIndex += 1) { + parts.push({ + separator: separators[segmentIndex - 1] ?? ' | ', + segment: segments[segmentIndex], + }); + } + // Keep the leading [model | provider] block together. + // This avoids splitting inside the model badge while still splitting + // separators elsewhere in the line. + const firstVisible = stripAnsi(parts[0].segment).trimStart(); + const firstHasOpeningBracket = firstVisible.startsWith('['); + const firstHasClosingBracket = stripAnsi(parts[0].segment).includes(']'); + if (firstHasOpeningBracket && !firstHasClosingBracket && parts.length > 1) { + let mergedSegment = parts[0].segment; + let consumeIndex = 1; + while (consumeIndex < parts.length) { + const nextPart = parts[consumeIndex]; + mergedSegment += `${nextPart.separator}${nextPart.segment}`; + consumeIndex += 1; + if (stripAnsi(nextPart.segment).includes(']')) { + break; + } + } + parts = [ + { separator: '', segment: mergedSegment }, + ...parts.slice(consumeIndex), + ]; + } + return parts; +} +function wrapLineToWidth(line, maxWidth) { + if (maxWidth <= 0 || visualLength(line) <= maxWidth) { + return [line]; + } + const parts = splitWrapParts(line); + if (parts.length <= 1) { + return [truncateToWidth(line, maxWidth)]; + } + const wrapped = []; + let current = parts[0].segment; + for (const part of parts.slice(1)) { + const candidate = `${current}${part.separator}${part.segment}`; + if (visualLength(candidate) <= maxWidth) { + current = candidate; + continue; + } + wrapped.push(truncateToWidth(current, maxWidth)); + current = part.segment; + } + if (current) { + wrapped.push(truncateToWidth(current, maxWidth)); + } + return wrapped; +} +// `length` is a target visual width in cells. +// `─` (U+2500) is East Asian Ambiguous-width: rendered as 2 cells in CJK +// terminals and 1 cell elsewhere. Repeating it `length` times in CJK mode +// would double the visual width and force the terminal to wrap. +function makeSeparator(length) { + const cellsPerDash = isCjkAmbiguousWide() ? 2 : 1; + const repeats = Math.max(1, Math.floor(length / cellsPerDash)); + return dim('─'.repeat(repeats)); +} +const ACTIVITY_ELEMENTS = new Set(['tools', 'skills', 'mcp', 'agents', 'todos']); +function buildMergeGroupLookup(mergeGroups) { + const lookup = new Map(); + for (const group of mergeGroups) { + const groupSet = new Set(group); + for (const element of group) { + if (!lookup.has(element)) { + lookup.set(element, groupSet); + } + } + } + return lookup; +} +function collectMergeSequence(elementOrder, startIndex, seen, group) { + const sequence = []; + for (let index = startIndex; index < elementOrder.length; index += 1) { + const element = elementOrder[index]; + if (seen.has(element) || !group.has(element)) { + break; + } + sequence.push(element); + } + return sequence; +} +function collectActivityLines(ctx) { + const activityLines = []; + const display = ctx.config?.display; + if (display?.showTools !== false) { + const toolsLine = renderToolsLine(ctx); + if (toolsLine) { + activityLines.push(toolsLine); + } + } + if (display?.showSkills === true) { + const skillsLine = renderSkillsLine(ctx); + if (skillsLine) { + activityLines.push(skillsLine); + } + } + if (display?.showMcp === true) { + const mcpLine = renderMcpLine(ctx); + if (mcpLine) { + activityLines.push(mcpLine); + } + } + if (display?.showAgents !== false) { + const agentsLine = renderAgentsLine(ctx); + if (agentsLine) { + activityLines.push(agentsLine); + } + } + if (display?.showTodos !== false) { + const todosLine = renderTodosLine(ctx); + if (todosLine) { + activityLines.push(todosLine); + } + } + return activityLines; +} +function renderElementLine(ctx, element, options) { + const display = ctx.config?.display; + const alignProgressLabels = options?.alignProgressLabels ?? false; + switch (element) { + case 'project': + return renderProjectLine(ctx); + case 'addedDirs': + return renderAddedDirsLine(ctx); + case 'context': + return renderIdentityLine(ctx, alignProgressLabels); + case 'usage': + return null; // usage is rendered after balance line, not in element loop + case 'promptCache': + return renderPromptCacheLine(ctx); + case 'memory': + return renderMemoryLine(ctx); + case 'environment': + return renderEnvironmentLine(ctx); + case 'tools': + return display?.showTools === false ? null : renderToolsLine(ctx); + case 'skills': + return display?.showSkills === true ? renderSkillsLine(ctx) : null; + case 'mcp': + return display?.showMcp === true ? renderMcpLine(ctx) : null; + case 'agents': + return display?.showAgents === false ? null : renderAgentsLine(ctx); + case 'todos': + return display?.showTodos === false ? null : renderTodosLine(ctx); + case 'sessionTime': + return renderSessionTimeLine(ctx); + } +} +function renderCompact(ctx) { + const lines = []; + const sessionLine = renderSessionLine(ctx); + if (sessionLine) { + lines.push(sessionLine); + } + return lines; +} +function renderExpanded(ctx, terminalWidth = null) { + const elementOrder = ctx.config?.elementOrder ?? DEFAULT_ELEMENT_ORDER; + const mergeGroups = ctx.config?.display?.mergeGroups ?? DEFAULT_MERGE_GROUPS; + const mergeGroupLookup = buildMergeGroupLookup(mergeGroups); + const seen = new Set(); + const lines = []; + for (let index = 0; index < elementOrder.length; index += 1) { + const element = elementOrder[index]; + if (seen.has(element)) { + continue; + } + const mergeGroup = mergeGroupLookup.get(element); + if (mergeGroup) { + const mergeSequence = collectMergeSequence(elementOrder, index, seen, mergeGroup); + if (mergeSequence.length > 1) { + index += mergeSequence.length - 1; + for (const groupedElement of mergeSequence) { + seen.add(groupedElement); + } + const renderedGroupLines = mergeSequence + .map(groupedElement => ({ + element: groupedElement, + line: renderElementLine(ctx, groupedElement), + })) + .filter((entry) => typeof entry.line === 'string' && entry.line.length > 0); + if (renderedGroupLines.length > 1) { + const combinedLine = renderedGroupLines.map(({ line }) => line).join(' │ '); + const widthIsReal = terminalWidth !== UNKNOWN_TERMINAL_WIDTH; + const canCombine = !widthIsReal || visualLength(combinedLine) <= terminalWidth; + if (canCombine) { + lines.push({ + line: combinedLine, + isActivity: renderedGroupLines.some(({ element: groupedElement }) => ACTIVITY_ELEMENTS.has(groupedElement)), + }); + } + else { + for (const { element: groupedElement, line } of renderedGroupLines) { + const stackedLine = renderElementLine(ctx, groupedElement, { + alignProgressLabels: true, + }) ?? line; + lines.push({ + line: stackedLine, + isActivity: ACTIVITY_ELEMENTS.has(groupedElement), + }); + } + } + } + else if (renderedGroupLines.length === 1) { + const [{ element: groupedElement, line }] = renderedGroupLines; + lines.push({ + line, + isActivity: ACTIVITY_ELEMENTS.has(groupedElement), + }); + } + continue; + } + } + seen.add(element); + const line = renderElementLine(ctx, element); + if (!line) { + continue; + } + lines.push({ + line, + isActivity: ACTIVITY_ELEMENTS.has(element), + }); + } + // Git files line always goes last (pass width so it can hide itself if too narrow) + const gitFilesLine = renderGitFilesLine(ctx, terminalWidth); + if (gitFilesLine) { + lines.push({ line: gitFilesLine, isActivity: false }); + } + return lines; +} +export function render(ctx) { + const lineLayout = ctx.config?.lineLayout ?? 'expanded'; + const showSeparators = ctx.config?.showSeparators ?? false; + const detectedWidth = getTerminalWidth({ preferEnv: true, fallback: UNKNOWN_TERMINAL_WIDTH }); + const configuredMaxWidth = ctx.config?.maxWidth ?? UNKNOWN_TERMINAL_WIDTH; + const terminalWidth = ctx.config?.forceMaxWidth && configuredMaxWidth !== UNKNOWN_TERMINAL_WIDTH + ? configuredMaxWidth + : (detectedWidth ?? configuredMaxWidth ?? UNKNOWN_TERMINAL_WIDTH); + let lines; + if (lineLayout === 'expanded') { + const renderedLines = renderExpanded(ctx, terminalWidth); + lines = renderedLines.map(({ line }) => line); + // ── Balance + Usage (separate lines) ──── + const balanceLine = renderBalanceLine(ctx); + if (balanceLine) { + lines.push(balanceLine); + } + const usageLine = renderUsageLine(ctx); + if (usageLine) { + lines.push(usageLine); + } + // Session token usage (cumulative) + if (ctx.config?.display?.showSessionTokens) { + const sessionTokensLine = renderSessionTokensLine(ctx); + if (sessionTokensLine) { + lines.push(sessionTokensLine); + } + } + // Compaction count (opt-in, hidden until the first compaction) + const compactionsLine = renderCompactionsLine(ctx); + if (compactionsLine) { + lines.push(compactionsLine); + } + // Advisor is rendered inline on the project line; see renderProjectLine. + if (showSeparators) { + const firstActivityIndex = renderedLines.findIndex(({ isActivity }) => isActivity); + if (firstActivityIndex > 0) { + const separatorBaseWidth = Math.max(...renderedLines + .slice(0, firstActivityIndex) + .map(({ line }) => visualLength(line)), 20); + const separatorWidth = terminalWidth + ? Math.min(separatorBaseWidth, terminalWidth) + : separatorBaseWidth; + lines.splice(firstActivityIndex, 0, makeSeparator(separatorWidth)); + } + } + } + else { + const headerLines = renderCompact(ctx); + const activityLines = collectActivityLines(ctx); + lines = [...headerLines]; + if (showSeparators && activityLines.length > 0) { + const maxWidth = Math.max(...headerLines.map(visualLength), 20); + const separatorWidth = terminalWidth ? Math.min(maxWidth, terminalWidth) : maxWidth; + lines.push(makeSeparator(separatorWidth)); + } + lines.push(...activityLines); + } + const physicalLines = lines.flatMap(line => line.split('\n')); + // Only wrap when terminal width is real (known). When width is the + // UNKNOWN_TERMINAL_WIDTH fallback, wrapping would use an arbitrary value + // and produce incorrect line breaks. + const wrapWidth = terminalWidth !== UNKNOWN_TERMINAL_WIDTH ? (terminalWidth ?? 0) : 0; + const visibleLines = physicalLines.flatMap(line => wrapLineToWidth(line, wrapWidth)); + for (const line of visibleLines) { + const outputLine = `${RESET}${line}`; + console.log(outputLine); + } +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/index.js.map b/plugins/balance-hud/dist/render/index.js.map new file mode 100644 index 0000000..4d5a54c --- /dev/null +++ b/plugins/balance-hud/dist/render/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/added-dirs.d.ts b/plugins/balance-hud/dist/render/lines/added-dirs.d.ts new file mode 100644 index 0000000..6020660 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/added-dirs.d.ts @@ -0,0 +1,9 @@ +import type { RenderContext } from '../../types.js'; +export declare function sanitize(value: string): string; +export declare function basenameOf(dir: string): string; +export declare const MAX_RENDERED_ADDED_DIRS = 5; +export declare const MAX_ADDED_DIR_NAME_LEN = 24; +export declare function truncateBasename(name: string): string; +export declare function normalizeAddedDirs(value: unknown): string[]; +export declare function renderAddedDirsLine(ctx: RenderContext): string | null; +//# sourceMappingURL=added-dirs.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/added-dirs.d.ts.map b/plugins/balance-hud/dist/render/lines/added-dirs.d.ts.map new file mode 100644 index 0000000..5faff32 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/added-dirs.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"added-dirs.d.ts","sourceRoot":"","sources":["../../../src/render/lines/added-dirs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAKpD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAKzC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAQ3D;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAoBrE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/added-dirs.js b/plugins/balance-hud/dist/render/lines/added-dirs.js new file mode 100644 index 0000000..bc56b8e --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/added-dirs.js @@ -0,0 +1,49 @@ +import { dim, label } from '../colors.js'; +import { getFileHref, safeHyperlink } from '../../utils/hyperlinks.js'; +import { sanitizeDisplayText } from '../../utils/sanitize.js'; +export function sanitize(value) { + return sanitizeDisplayText(value); +} +export function basenameOf(dir) { + const segments = dir.split(/[/\\]/).filter(Boolean); + return segments[segments.length - 1] ?? dir; +} +export const MAX_RENDERED_ADDED_DIRS = 5; +export const MAX_ADDED_DIR_NAME_LEN = 24; +// Length is measured in UTF-16 code units, not grapheme clusters; a name +// of mostly 4-byte codepoints (emoji, rare CJK) may render slightly wider +// than MAX_ADDED_DIR_NAME_LEN. Acceptable simplification for a statusline. +export function truncateBasename(name) { + if (name.length <= MAX_ADDED_DIR_NAME_LEN) + return name; + return name.slice(0, MAX_ADDED_DIR_NAME_LEN - 1) + '…'; +} +export function normalizeAddedDirs(value) { + if (!Array.isArray(value)) + return []; + return value.filter((v) => typeof v === 'string' && + v.length > 0 && + sanitize(basenameOf(v)).length > 0); +} +export function renderAddedDirsLine(ctx) { + const display = ctx.config?.display; + if (display?.showAddedDirs === false) + return null; + if ((display?.addedDirsLayout ?? 'inline') !== 'line') + return null; + const dirs = normalizeAddedDirs(ctx.stdin.workspace?.added_dirs); + if (dirs.length === 0) + return null; + const colors = ctx.config?.colors; + const visible = dirs.slice(0, MAX_RENDERED_ADDED_DIRS); + const overflow = dirs.length - visible.length; + const rendered = visible.map((dir) => { + const name = truncateBasename(sanitize(basenameOf(dir))); + return safeHyperlink(getFileHref(dir), dim(name)); + }); + if (overflow > 0) { + rendered.push(dim(`+${overflow} more`)); + } + return `${label('Added dirs:', colors)} ${rendered.join(dim(', '))}`; +} +//# sourceMappingURL=added-dirs.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/added-dirs.js.map b/plugins/balance-hud/dist/render/lines/added-dirs.js.map new file mode 100644 index 0000000..5d324f5 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/added-dirs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"added-dirs.js","sourceRoot":"","sources":["../../../src/render/lines/added-dirs.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/advisor.d.ts b/plugins/balance-hud/dist/render/lines/advisor.d.ts new file mode 100644 index 0000000..1fa2778 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/advisor.d.ts @@ -0,0 +1,14 @@ +import type { RenderContext } from '../../types.js'; +/** + * Prettifies a raw advisor model ID (as captured from the transcript) into a + * human-friendly label. Falls back to the input string when the pattern is + * not recognised. + * + * claude-opus-4-7 → Opus 4.7 + * claude-sonnet-4-6 → Sonnet 4.6 + * claude-haiku-4-5-20251001 → Haiku 4.5 + * opus → Opus + */ +export declare function prettifyAdvisorId(rawId: string): string; +export declare function renderAdvisorLine(ctx: RenderContext): string | null; +//# sourceMappingURL=advisor.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/advisor.d.ts.map b/plugins/balance-hud/dist/render/lines/advisor.d.ts.map new file mode 100644 index 0000000..b3177b4 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/advisor.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"advisor.d.ts","sourceRoot":"","sources":["../../../src/render/lines/advisor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAapD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAmBvD;AAYD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAwBnE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/advisor.js b/plugins/balance-hud/dist/render/lines/advisor.js new file mode 100644 index 0000000..f8cbd27 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/advisor.js @@ -0,0 +1,67 @@ +import { label } from '../colors.js'; +import { t } from '../../i18n/index.js'; +import { sanitize as sanitizeDisplayText } from './added-dirs.js'; +const ADVISOR_ID_PATTERN = /^(?:claude-)?(opus|sonnet|haiku)-(\d+)-(\d+)/i; +// Hard cap on the visible advisor segment so a malformed transcript or +// override cannot grow into an oversized recurring statusline part. +// 64 leaves headroom for "Advisor: " prefix on narrow terminals while still +// fitting any realistic model name (e.g. "claude-haiku-4-5-20251001" = 25). +const MAX_ADVISOR_DISPLAY_LEN = 64; +/** + * Prettifies a raw advisor model ID (as captured from the transcript) into a + * human-friendly label. Falls back to the input string when the pattern is + * not recognised. + * + * claude-opus-4-7 → Opus 4.7 + * claude-sonnet-4-6 → Sonnet 4.6 + * claude-haiku-4-5-20251001 → Haiku 4.5 + * opus → Opus + */ +export function prettifyAdvisorId(rawId) { + const id = rawId.trim(); + if (!id) { + return ''; + } + const match = id.match(ADVISOR_ID_PATTERN); + if (match) { + const family = match[1].charAt(0).toUpperCase() + match[1].slice(1).toLowerCase(); + return `${family} ${match[2]}.${match[3]}`; + } + // Short alias fallbacks (e.g. settings.json stores `"opus"`, `"sonnet"`). + const lower = id.toLowerCase(); + if (lower === 'opus' || lower === 'sonnet' || lower === 'haiku') { + return lower.charAt(0).toUpperCase() + lower.slice(1); + } + return id.replace(/^claude-/i, ''); +} +function safeAdvisorText(input) { + if (typeof input !== 'string') { + return ''; + } + // Strip control + bidi + ANSI ESC bytes (sanitizeDisplayText handles all of + // U+0000-U+001F including ESC, so OSC/CSI/SS3 sequences lose their lead + // byte and the remainder renders as inert plain text), then cap length. + return sanitizeDisplayText(input.trim()).slice(0, MAX_ADVISOR_DISPLAY_LEN); +} +export function renderAdvisorLine(ctx) { + const display = ctx.config?.display; + if (display?.showAdvisor !== true) { + return null; + } + const override = safeAdvisorText(display.advisorOverride); + const transcriptValue = safeAdvisorText(ctx.transcript?.advisorModel); + const rawValue = override.length > 0 ? override : transcriptValue; + if (!rawValue) { + return null; + } + const pretty = override.length > 0 ? rawValue : prettifyAdvisorId(rawValue); + // Defence-in-depth: re-sanitize and re-cap after prettifying in case any + // future change to the prettifier reflects raw input bytes through. + const safePretty = safeAdvisorText(pretty); + if (!safePretty) { + return null; + } + const colors = ctx.config?.colors; + return `${label(`${t('label.advisor')}:`, colors)} ${safePretty}`; +} +//# sourceMappingURL=advisor.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/advisor.js.map b/plugins/balance-hud/dist/render/lines/advisor.js.map new file mode 100644 index 0000000..5dac4f2 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/advisor.js.map @@ -0,0 +1 @@ +{"version":3,"file":"advisor.js","sourceRoot":"","sources":["../../../src/render/lines/advisor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/compactions.d.ts b/plugins/balance-hud/dist/render/lines/compactions.d.ts new file mode 100644 index 0000000..2991cc0 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/compactions.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from '../../types.js'; +export declare function renderCompactionsLine(ctx: RenderContext): string | null; +//# sourceMappingURL=compactions.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/compactions.d.ts.map b/plugins/balance-hud/dist/render/lines/compactions.d.ts.map new file mode 100644 index 0000000..17baab9 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/compactions.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"compactions.d.ts","sourceRoot":"","sources":["../../../src/render/lines/compactions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAavE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/compactions.js b/plugins/balance-hud/dist/render/lines/compactions.js new file mode 100644 index 0000000..f10c327 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/compactions.js @@ -0,0 +1,15 @@ +import { label } from '../colors.js'; +import { t } from '../../i18n/index.js'; +export function renderCompactionsLine(ctx) { + const display = ctx.config?.display; + if (display?.showCompactions !== true) { + return null; + } + const compactions = ctx.transcript.compactionCount ?? 0; + if (compactions === 0) { + return null; + } + const colors = ctx.config?.colors; + return label(`${t('label.compactions')}: ${compactions}`, colors); +} +//# sourceMappingURL=compactions.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/compactions.js.map b/plugins/balance-hud/dist/render/lines/compactions.js.map new file mode 100644 index 0000000..9d43bab --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/compactions.js.map @@ -0,0 +1 @@ +{"version":3,"file":"compactions.js","sourceRoot":"","sources":["../../../src/render/lines/compactions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAExC,MAAM,UAAU,qBAAqB,CAAC,GAAkB;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,IAAI,OAAO,EAAE,eAAe,KAAK,IAAI,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,UAAU,CAAC,eAAe,IAAI,CAAC,CAAC;IACxD,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAClC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,mBAAmB,CAAC,KAAK,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC;AACpE,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/cost.d.ts b/plugins/balance-hud/dist/render/lines/cost.d.ts new file mode 100644 index 0000000..7006e4b --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/cost.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from '../../types.js'; +export declare function renderCostEstimate(ctx: RenderContext): string | null; +//# sourceMappingURL=cost.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/cost.d.ts.map b/plugins/balance-hud/dist/render/lines/cost.d.ts.map new file mode 100644 index 0000000..800ddbe --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/cost.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../../src/render/lines/cost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAKpD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAYpE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/cost.js b/plugins/balance-hud/dist/render/lines/cost.js new file mode 100644 index 0000000..ada1685 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/cost.js @@ -0,0 +1,15 @@ +import { resolveSessionCost, formatUsd } from '../../cost.js'; +import { t } from '../../i18n/index.js'; +import { label } from '../colors.js'; +export function renderCostEstimate(ctx) { + if (ctx.config?.display?.showCost !== true) { + return null; + } + const cost = resolveSessionCost(ctx.stdin, ctx.transcript.sessionTokens); + if (!cost) { + return null; + } + const labelKey = cost.source === 'native' ? 'label.cost' : 'label.estimatedCost'; + return label(`${t(labelKey)} ${formatUsd(cost.totalUsd)}`, ctx.config?.colors); +} +//# sourceMappingURL=cost.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/cost.js.map b/plugins/balance-hud/dist/render/lines/cost.js.map new file mode 100644 index 0000000..9a6a191 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/cost.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cost.js","sourceRoot":"","sources":["../../../src/render/lines/cost.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,MAAM,UAAU,kBAAkB,CAAC,GAAkB;IACnD,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACzE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,qBAAqB,CAAC;IACjF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACjF,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/environment.d.ts b/plugins/balance-hud/dist/render/lines/environment.d.ts new file mode 100644 index 0000000..7ac0092 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/environment.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from "../../types.js"; +export declare function renderEnvironmentLine(ctx: RenderContext): string | null; +//# sourceMappingURL=environment.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/environment.d.ts.map b/plugins/balance-hud/dist/render/lines/environment.d.ts.map new file mode 100644 index 0000000..8354b70 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/environment.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../../src/render/lines/environment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAoCvE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/environment.js b/plugins/balance-hud/dist/render/lines/environment.js new file mode 100644 index 0000000..8727a17 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/environment.js @@ -0,0 +1,32 @@ +import { label } from "../colors.js"; +import { t } from "../../i18n/index.js"; +export function renderEnvironmentLine(ctx) { + const display = ctx.config?.display; + const totalCounts = ctx.claudeMdCount + ctx.rulesCount + ctx.mcpCount + ctx.hooksCount; + const threshold = display?.environmentThreshold ?? 0; + const showCounts = display?.showConfigCounts !== false; + const showOutputStyle = display?.showOutputStyle === true; + const parts = []; + if (showCounts && totalCounts >= threshold && totalCounts > 0) { + if (ctx.claudeMdCount > 0) { + parts.push(`${ctx.claudeMdCount} CLAUDE.md`); + } + if (ctx.rulesCount > 0) { + parts.push(`${ctx.rulesCount} ${t("label.rules")}`); + } + if (ctx.mcpCount > 0) { + parts.push(`${ctx.mcpCount} MCPs`); + } + if (ctx.hooksCount > 0) { + parts.push(`${ctx.hooksCount} ${t("label.hooks")}`); + } + } + if (showOutputStyle && ctx.outputStyle) { + parts.push(`style: ${ctx.outputStyle}`); + } + if (parts.length === 0) { + return null; + } + return label(parts.join(" | "), ctx.config?.colors); +} +//# sourceMappingURL=environment.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/environment.js.map b/plugins/balance-hud/dist/render/lines/environment.js.map new file mode 100644 index 0000000..f0b3602 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/environment.js.map @@ -0,0 +1 @@ +{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../../src/render/lines/environment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAExC,MAAM,UAAU,qBAAqB,CAAC,GAAkB;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,MAAM,WAAW,GACf,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC;IACrE,MAAM,SAAS,GAAG,OAAO,EAAE,oBAAoB,IAAI,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,OAAO,EAAE,gBAAgB,KAAK,KAAK,CAAC;IACvD,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC1D,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,UAAU,IAAI,WAAW,IAAI,SAAS,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QAC9D,IAAI,GAAG,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,aAAa,YAAY,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,OAAO,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,IAAI,eAAe,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/identity.d.ts b/plugins/balance-hud/dist/render/lines/identity.d.ts new file mode 100644 index 0000000..f895fce --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/identity.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from "../../types.js"; +export declare function renderIdentityLine(ctx: RenderContext, alignLabels?: boolean): string; +//# sourceMappingURL=identity.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/identity.d.ts.map b/plugins/balance-hud/dist/render/lines/identity.d.ts.map new file mode 100644 index 0000000..ea1cbd0 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/identity.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../src/render/lines/identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAcpD,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,WAAW,UAAQ,GAClB,MAAM,CA4CR"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/identity.js b/plugins/balance-hud/dist/render/lines/identity.js new file mode 100644 index 0000000..9cf0e79 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/identity.js @@ -0,0 +1,43 @@ +import { getContextPercent, getBufferedPercent, } from "../../stdin.js"; +import { coloredBar, label, getContextColor, BRIGHT_BLUE, RESET } from "../colors.js"; +import { getAdaptiveBarWidth } from "../../utils/terminal.js"; +import { t } from "../../i18n/index.js"; + +import { formatTokens, formatContextValue } from "../../utils/format.js"; +import { createDebug } from "../../debug.js"; +const debug = createDebug("context"); +export function renderIdentityLine(ctx, alignLabels = false) { + const autoCompactWindow = ctx.config?.display?.autoCompactWindow ?? null; + const rawPercent = getContextPercent(ctx.stdin, autoCompactWindow); + const bufferedPercent = getBufferedPercent(ctx.stdin, autoCompactWindow); + const autocompactMode = ctx.config?.display?.autocompactBuffer ?? "enabled"; + const percent = autocompactMode === "disabled" ? rawPercent : bufferedPercent; + const colors = ctx.config?.colors; + if (autocompactMode === "disabled") { + debug(`autocompactBuffer=disabled, showing raw ${rawPercent}% (buffered would be ${bufferedPercent}%)`); + } + const display = ctx.config?.display; + const contextThresholds = { + warning: display?.contextWarningThreshold, + critical: display?.contextCriticalThreshold, + }; + const contextValueMode = display?.contextValue ?? "percent"; + const contextValue = formatContextValue(ctx, percent, contextValueMode); + // Context text uses DeepSeek blue (94) — same as "DeepSeek 余额" in v1.1.3 + const contextLabelDisplay = `${BRIGHT_BLUE}${t("label.context")}${RESET}`; + const contextValueDisplay = `${getContextColor(percent, colors, contextThresholds)}${contextValue}${RESET}`; + let line = display?.showContextBar !== false + ? `${contextLabelDisplay} ${coloredBar(percent, getAdaptiveBarWidth(), colors, contextThresholds)} ${contextValueDisplay}` + : `${contextLabelDisplay} ${contextValueDisplay}`; + if (display?.showTokenBreakdown !== false && percent >= (display?.contextCriticalThreshold ?? 85)) { + const usage = ctx.stdin.context_window?.current_usage; + if (usage) { + const input = formatTokens(usage.input_tokens ?? 0); + const cache = formatTokens((usage.cache_creation_input_tokens ?? 0) + + (usage.cache_read_input_tokens ?? 0)); + line += label(` (${t("format.in")}: ${input}, ${t("format.cache")}: ${cache})`, colors); + } + } + return line; +} +//# sourceMappingURL=identity.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/identity.js.map b/plugins/balance-hud/dist/render/lines/identity.js.map new file mode 100644 index 0000000..adec12b --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/identity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../src/render/lines/identity.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/index.d.ts b/plugins/balance-hud/dist/render/lines/index.d.ts new file mode 100644 index 0000000..cbc646e --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/index.d.ts @@ -0,0 +1,12 @@ +export { renderIdentityLine } from './identity.js'; +export { renderProjectLine, renderGitFilesLine } from './project.js'; +export { renderAddedDirsLine } from './added-dirs.js'; +export { renderEnvironmentLine } from './environment.js'; +export { renderPromptCacheLine, formatPromptCacheCountdown } from './prompt-cache.js'; +export { renderUsageLine } from './usage.js'; +export { renderMemoryLine } from './memory.js'; +export { renderSessionTokensLine } from './session-tokens.js'; +export { renderCompactionsLine } from './compactions.js'; +export { renderSessionTimeLine } from './session-time.js'; +export { renderAdvisorLine, prettifyAdvisorId } from './advisor.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/index.d.ts.map b/plugins/balance-hud/dist/render/lines/index.d.ts.map new file mode 100644 index 0000000..2cb8ba6 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/render/lines/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/index.js b/plugins/balance-hud/dist/render/lines/index.js new file mode 100644 index 0000000..063a976 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/index.js @@ -0,0 +1,12 @@ +export { renderIdentityLine } from './identity.js'; +export { renderProjectLine, renderGitFilesLine } from './project.js'; +export { renderAddedDirsLine } from './added-dirs.js'; +export { renderEnvironmentLine } from './environment.js'; +export { renderPromptCacheLine, formatPromptCacheCountdown } from './prompt-cache.js'; +export { renderUsageLine, renderBalanceLine } from './usage.js'; +export { renderMemoryLine } from './memory.js'; +export { renderSessionTokensLine } from './session-tokens.js'; +export { renderCompactionsLine } from './compactions.js'; +export { renderSessionTimeLine } from './session-time.js'; +export { renderAdvisorLine, prettifyAdvisorId } from './advisor.js'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/index.js.map b/plugins/balance-hud/dist/render/lines/index.js.map new file mode 100644 index 0000000..789ae21 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/render/lines/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/label-align.d.ts b/plugins/balance-hud/dist/render/lines/label-align.d.ts new file mode 100644 index 0000000..6ca8035 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/label-align.d.ts @@ -0,0 +1,19 @@ +import type { HudColorOverrides } from "../../config.js"; +import type { MessageKey } from "../../i18n/types.js"; +/** + * Compute the visual width of a plain-text string (no ANSI). + * CJK ideographs count as 2 cells; ASCII characters count as 1. + * In CJK locales, East Asian Ambiguous-width chars also count as 2. + */ +declare function plainTextWidth(str: string): number; +/** Compute the max visual width across the three progress-bar labels. */ +declare function maxLabelWidth(): number; +/** + * Return a label whose visible text is right-padded to align with the widest + * progress-bar label in the current locale, then wrapped with the `label()` + * ANSI helper. + */ +export declare function paddedLabel(key: MessageKey, colors?: Partial): string; +export declare function progressLabel(key: MessageKey, colors?: Partial, align?: boolean): string; +export { plainTextWidth as _plainTextWidth, maxLabelWidth as _maxLabelWidth }; +//# sourceMappingURL=label-align.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/label-align.d.ts.map b/plugins/balance-hud/dist/render/lines/label-align.d.ts.map new file mode 100644 index 0000000..c5c8400 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/label-align.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"label-align.d.ts","sourceRoot":"","sources":["../../../src/render/lines/label-align.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAYtD;;;;GAIG;AACH,iBAAS,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAY3C;AAED,yEAAyE;AACzE,iBAAS,aAAa,IAAI,MAAM,CAO/B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,UAAU,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAClC,MAAM,CAKR;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,UAAU,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,KAAK,UAAQ,GACZ,MAAM,CAER;AAGD,OAAO,EAAE,cAAc,IAAI,eAAe,EAAE,aAAa,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/label-align.js b/plugins/balance-hud/dist/render/lines/label-align.js new file mode 100644 index 0000000..fdc9344 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/label-align.js @@ -0,0 +1,55 @@ +import { label } from "../colors.js"; +import { t } from "../../i18n/index.js"; +import { codePointCellWidth, isCjkAmbiguousWide } from "../width.js"; +/** Label keys that should be aligned when rendered on separate lines. */ +const PROGRESS_LABEL_KEYS = [ + "label.context", + "label.usage", + "label.weekly", +]; +/** + * Compute the visual width of a plain-text string (no ANSI). + * CJK ideographs count as 2 cells; ASCII characters count as 1. + * In CJK locales, East Asian Ambiguous-width chars also count as 2. + */ +function plainTextWidth(str) { + const ambiguousWide = isCjkAmbiguousWide(); + let width = 0; + for (const char of str) { + const cp = char.codePointAt(0); + if (cp !== undefined) { + width += codePointCellWidth(cp, ambiguousWide); + } + else { + width += 1; + } + } + return width; +} +/** Compute the max visual width across the three progress-bar labels. */ +function maxLabelWidth() { + let max = 0; + for (const key of PROGRESS_LABEL_KEYS) { + const w = plainTextWidth(t(key)); + if (w > max) + max = w; + } + return max; +} +/** + * Return a label whose visible text is right-padded to align with the widest + * progress-bar label in the current locale, then wrapped with the `label()` + * ANSI helper. + */ +export function paddedLabel(key, colors) { + const text = t(key); + const pad = maxLabelWidth() - plainTextWidth(text); + const padded = pad > 0 ? text + " ".repeat(pad) : text; + return label(padded, colors); +} +export function progressLabel(key, colors, align = false) { + return align ? paddedLabel(key, colors) : label(t(key), colors); +} +// Exported for testing only. +export { plainTextWidth as _plainTextWidth, maxLabelWidth as _maxLabelWidth }; +//# sourceMappingURL=label-align.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/label-align.js.map b/plugins/balance-hud/dist/render/lines/label-align.js.map new file mode 100644 index 0000000..6af7af4 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/label-align.js.map @@ -0,0 +1 @@ +{"version":3,"file":"label-align.js","sourceRoot":"","sources":["../../../src/render/lines/label-align.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAErE,yEAAyE;AACzE,MAAM,mBAAmB,GAAiB;IACxC,eAAe;IACf,aAAa;IACb,cAAc;CACf,CAAC;AAEF;;;;GAIG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,aAAa,GAAG,kBAAkB,EAAE,CAAC;IAC3C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACrB,KAAK,IAAI,kBAAkB,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,yEAAyE;AACzE,SAAS,aAAa;IACpB,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG;YAAE,GAAG,GAAG,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,GAAe,EACf,MAAmC;IAEnC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACpB,MAAM,GAAG,GAAG,aAAa,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,OAAO,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,GAAe,EACf,MAAmC,EACnC,KAAK,GAAG,KAAK;IAEb,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;AAClE,CAAC;AAED,6BAA6B;AAC7B,OAAO,EAAE,cAAc,IAAI,eAAe,EAAE,aAAa,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/memory.d.ts b/plugins/balance-hud/dist/render/lines/memory.d.ts new file mode 100644 index 0000000..9a70530 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/memory.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from "../../types.js"; +export declare function renderMemoryLine(ctx: RenderContext): string | null; +//# sourceMappingURL=memory.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/memory.d.ts.map b/plugins/balance-hud/dist/render/lines/memory.d.ts.map new file mode 100644 index 0000000..00623c8 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/memory.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/render/lines/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAMpD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA0BlE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/memory.js b/plugins/balance-hud/dist/render/lines/memory.js new file mode 100644 index 0000000..7ad73f2 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/memory.js @@ -0,0 +1,23 @@ +import { formatBytes } from "../../memory.js"; +import { label, getQuotaColor, quotaBar, RESET } from "../colors.js"; +import { getAdaptiveBarWidth } from "../../utils/terminal.js"; +import { t } from "../../i18n/index.js"; +export function renderMemoryLine(ctx) { + const display = ctx.config?.display; + const colors = ctx.config?.colors; + if (ctx.config?.lineLayout !== "expanded") { + return null; + } + if (display?.showMemoryUsage !== true) { + return null; + } + if (!ctx.memoryUsage) { + return null; + } + const memoryLabel = label(t("label.approxRam"), colors); + const percentColor = getQuotaColor(ctx.memoryUsage.usedPercent, colors); + const percent = `${percentColor}${ctx.memoryUsage.usedPercent}%${RESET}`; + const bar = quotaBar(ctx.memoryUsage.usedPercent, getAdaptiveBarWidth(), colors); + return `${memoryLabel} ${bar} ${formatBytes(ctx.memoryUsage.usedBytes)} / ${formatBytes(ctx.memoryUsage.totalBytes)} (${percent})`; +} +//# sourceMappingURL=memory.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/memory.js.map b/plugins/balance-hud/dist/render/lines/memory.js.map new file mode 100644 index 0000000..0a111da --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/memory.js.map @@ -0,0 +1 @@ +{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/render/lines/memory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAExC,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAElC,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,UAAU,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO,EAAE,eAAe,KAAK,IAAI,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,GAAG,YAAY,GAAG,GAAG,CAAC,WAAW,CAAC,WAAW,IAAI,KAAK,EAAE,CAAC;IACzE,MAAM,GAAG,GAAG,QAAQ,CAClB,GAAG,CAAC,WAAW,CAAC,WAAW,EAC3B,mBAAmB,EAAE,EACrB,MAAM,CACP,CAAC;IAEF,OAAO,GAAG,WAAW,IAAI,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,OAAO,GAAG,CAAC;AACrI,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/project.d.ts b/plugins/balance-hud/dist/render/lines/project.d.ts new file mode 100644 index 0000000..f2021ed --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/project.d.ts @@ -0,0 +1,4 @@ +import type { RenderContext } from '../../types.js'; +export declare function renderProjectLine(ctx: RenderContext): string | null; +export declare function renderGitFilesLine(ctx: RenderContext, terminalWidth?: number | null): string | null; +//# sourceMappingURL=project.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/project.d.ts.map b/plugins/balance-hud/dist/render/lines/project.d.ts.map new file mode 100644 index 0000000..9b36771 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/project.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/render/lines/project.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAqBpD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA2InE;AAsBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,EAAE,aAAa,GAAE,MAAM,GAAG,IAAW,GAAG,MAAM,GAAG,IAAI,CAsDzG"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/project.js b/plugins/balance-hud/dist/render/lines/project.js new file mode 100644 index 0000000..55e5ba0 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/project.js @@ -0,0 +1,212 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { getModelName, formatModelName } from '../../stdin.js'; +import { getOutputSpeed } from '../../speed-tracker.js'; +import { git as gitColor, gitBranch as gitBranchColor, warning as warningColor, critical as criticalColor, label, model as modelColor, project as projectColor, red, green, yellow, dim, custom as customColor } from '../colors.js'; +import { t } from '../../i18n/index.js'; +import { renderCostEstimate } from './cost.js'; +import { renderAdvisorLine } from './advisor.js'; +import { normalizeAddedDirs, sanitize as sanitizeDisplayText, basenameOf, truncateBasename, MAX_RENDERED_ADDED_DIRS } from './added-dirs.js'; +import { getFileHref, safeHyperlink } from '../../utils/hyperlinks.js'; +import { formatModelDisplay } from '../model-display.js'; +function resolvePathWithinCwd(cwd, candidatePath) { + const resolvedCwd = path.resolve(cwd); + const resolvedPath = path.resolve(cwd, candidatePath); + const relative = path.relative(resolvedCwd, resolvedPath); + if (relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))) { + return resolvedPath; + } + return null; +} +export function renderProjectLine(ctx) { + const display = ctx.config?.display; + const colors = ctx.config?.colors; + const parts = []; + const customLine = display?.customLine; + const customLinePosition = display?.customLinePosition ?? 'last'; + if (customLine && customLinePosition === 'first') { + parts.push(customColor(customLine, colors)); + } + if (display?.showModel !== false) { + const model = formatModelName(getModelName(ctx.stdin), ctx.config?.display?.modelFormat, ctx.config?.display?.modelOverride); + const modelDisplay = formatModelDisplay(model, ctx); + parts.push(modelColor(`[${modelDisplay}]`, colors)); + } + let projectPart = null; + if (display?.showProject !== false && ctx.stdin.cwd) { + const segments = ctx.stdin.cwd.split(/[/\\]/).filter(Boolean); + const pathLevels = ctx.config?.pathLevels ?? 1; + const projectPath = sanitizeDisplayText(segments.length > 0 ? segments.slice(-pathLevels).join('/') : '/'); + const coloredProject = projectColor(projectPath, colors); + projectPart = safeHyperlink(getFileHref(ctx.stdin.cwd), coloredProject); + } + let addedDirsPart = null; + const addedDirs = normalizeAddedDirs(ctx.stdin.workspace?.added_dirs); + const addedDirsLayout = display?.addedDirsLayout ?? 'inline'; + if (display?.showAddedDirs !== false && addedDirsLayout === 'inline' && addedDirs.length > 0) { + const visible = addedDirs.slice(0, MAX_RENDERED_ADDED_DIRS); + const overflow = addedDirs.length - visible.length; + const rendered = visible.map((dir) => { + const name = truncateBasename(sanitizeDisplayText(basenameOf(dir))); + const text = dim(`+${name}`); + return safeHyperlink(getFileHref(dir), text); + }); + if (overflow > 0) { + rendered.push(dim(`+${overflow} more`)); + } + addedDirsPart = rendered.join(' '); + } + let gitPart = ''; + const gitConfig = ctx.config?.gitStatus; + const showGit = gitConfig?.enabled ?? true; + const branchOverflow = gitConfig?.branchOverflow ?? 'truncate'; + if (showGit && ctx.gitStatus) { + const branchText = sanitizeDisplayText(ctx.gitStatus.branch + ((gitConfig?.showDirty ?? true) && ctx.gitStatus.isDirty ? '*' : '')); + const coloredBranch = gitBranchColor(branchText, colors); + const linkedBranch = safeHyperlink(ctx.gitStatus.branchUrl, coloredBranch); + const gitInner = [linkedBranch]; + if (gitConfig?.showAheadBehind) { + if (ctx.gitStatus.ahead > 0) { + gitInner.push(formatAheadCount(ctx.gitStatus.ahead, gitConfig, colors)); + } + if (ctx.gitStatus.behind > 0) + gitInner.push(gitBranchColor(`↓${ctx.gitStatus.behind}`, colors)); + } + if (gitConfig?.showFileStats && ctx.gitStatus.lineDiff) { + const { added, deleted } = ctx.gitStatus.lineDiff; + const diffParts = []; + if (added > 0) + diffParts.push(green(`+${added}`)); + if (deleted > 0) + diffParts.push(red(`-${deleted}`)); + if (diffParts.length > 0) { + gitInner.push(`[${diffParts.join(' ')}]`); + } + } + gitPart = `${gitColor('git:(', colors)}${gitInner.join(' ')}${gitColor(')', colors)}`; + } + const projectWithDirs = projectPart && addedDirsPart + ? `${projectPart} ${addedDirsPart}` + : projectPart ?? addedDirsPart; + if (projectWithDirs && gitPart) { + if (branchOverflow === 'wrap') { + parts.push(projectWithDirs); + parts.push(gitPart); + } + else { + parts.push(`${projectWithDirs} ${gitPart}`); + } + } + else if (projectWithDirs) { + parts.push(projectWithDirs); + } + else if (gitPart) { + parts.push(gitPart); + } + // Advisor model sits inline with the model/project/git badge so the + // configured /advisor is visible on the first line at a glance. + if (display?.showAdvisor) { + const advisorPart = renderAdvisorLine(ctx); + if (advisorPart) { + parts.push(advisorPart); + } + } + if (display?.showSessionName && ctx.transcript.sessionName) { + parts.push(label(ctx.transcript.sessionName, colors)); + } + if (display?.showClaudeCodeVersion && ctx.claudeCodeVersion) { + parts.push(label(`CC v${ctx.claudeCodeVersion}`, colors)); + } + if (ctx.extraLabel) { + parts.push(label(ctx.extraLabel, colors)); + } + if (display?.showDuration !== false && ctx.sessionDuration) { + parts.push(label(`⏱️ ${ctx.sessionDuration}`, colors)); + } + const costEstimate = renderCostEstimate(ctx); + if (costEstimate) { + parts.push(costEstimate); + } + if (display?.showSpeed) { + const speed = getOutputSpeed(ctx.stdin); + if (speed !== null) { + parts.push(label(`${t('format.out')}: ${speed.toFixed(1)} ${t('format.tokPerSec')}`, colors)); + } + } + if (customLine && customLinePosition === 'last') { + parts.push(customColor(customLine, colors)); + } + if (parts.length === 0) { + return null; + } + return parts.join(' \u2502 '); +} +function formatAheadCount(ahead, gitConfig, colors) { + const value = `↑${ahead}`; + const criticalThreshold = gitConfig?.pushCriticalThreshold ?? 0; + const warningThreshold = gitConfig?.pushWarningThreshold ?? 0; + if (criticalThreshold > 0 && ahead >= criticalThreshold) { + return criticalColor(value, colors); + } + if (warningThreshold > 0 && ahead >= warningThreshold) { + return warningColor(value, colors); + } + return gitBranchColor(value, colors); +} +export function renderGitFilesLine(ctx, terminalWidth = null) { + const gitConfig = ctx.config?.gitStatus; + if (!(gitConfig?.showFileStats ?? false)) + return null; + if (!ctx.gitStatus?.fileStats) + return null; + const { trackedFiles, untracked } = ctx.gitStatus.fileStats; + if (trackedFiles.length === 0 && untracked === 0) + return null; + if (terminalWidth !== null && terminalWidth < 60) + return null; + const cwd = ctx.stdin.cwd; + const sorted = [...trackedFiles].sort((a, b) => { + try { + const aPath = cwd ? resolvePathWithinCwd(cwd, a.fullPath) : null; + const bPath = cwd ? resolvePathWithinCwd(cwd, b.fullPath) : null; + const aMtime = aPath ? fs.statSync(aPath).mtimeMs : 0; + const bMtime = bPath ? fs.statSync(bPath).mtimeMs : 0; + return bMtime - aMtime; + } + catch { + return 0; + } + }); + const shown = sorted.slice(0, 6); + const overflow = sorted.length - shown.length; + const statParts = []; + for (const trackedFile of shown) { + const prefix = trackedFile.type === 'added' ? green('+') : trackedFile.type === 'deleted' ? red('-') : yellow('~'); + const safeBasename = sanitizeDisplayText(trackedFile.basename); + const coloredName = trackedFile.type === 'added' + ? green(safeBasename) + : trackedFile.type === 'deleted' + ? red(safeBasename) + : yellow(safeBasename); + const resolvedPath = cwd ? resolvePathWithinCwd(cwd, trackedFile.fullPath) : null; + const linkedName = resolvedPath ? safeHyperlink(getFileHref(resolvedPath), coloredName) : coloredName; + let entry = `${prefix}${linkedName}`; + if (trackedFile.lineDiff) { + const diffParts = []; + if (trackedFile.lineDiff.added > 0) + diffParts.push(green(`+${trackedFile.lineDiff.added}`)); + if (trackedFile.lineDiff.deleted > 0) + diffParts.push(red(`-${trackedFile.lineDiff.deleted}`)); + if (diffParts.length > 0) { + entry += dim(`(${diffParts.join(' ')})`); + } + } + statParts.push(entry); + } + if (overflow > 0) + statParts.push(dim(`+${overflow} more`)); + if (untracked > 0) + statParts.push(dim(`?${untracked}`)); + return statParts.join(' '); +} +//# sourceMappingURL=project.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/project.js.map b/plugins/balance-hud/dist/render/lines/project.js.map new file mode 100644 index 0000000..3b81e76 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/project.js.map @@ -0,0 +1 @@ +{"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/render/lines/project.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/prompt-cache.d.ts b/plugins/balance-hud/dist/render/lines/prompt-cache.d.ts new file mode 100644 index 0000000..fc61f83 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/prompt-cache.d.ts @@ -0,0 +1,4 @@ +import type { RenderContext } from '../../types.js'; +export declare function formatPromptCacheCountdown(remainingMs: number): string; +export declare function renderPromptCacheLine(ctx: RenderContext, now?: number): string | null; +//# sourceMappingURL=prompt-cache.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/prompt-cache.d.ts.map b/plugins/balance-hud/dist/render/lines/prompt-cache.d.ts.map new file mode 100644 index 0000000..4b74f90 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/prompt-cache.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"prompt-cache.d.ts","sourceRoot":"","sources":["../../../src/render/lines/prompt-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAwBpD,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAetE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,GAAG,IAAI,CA2BjG"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/prompt-cache.js b/plugins/balance-hud/dist/render/lines/prompt-cache.js new file mode 100644 index 0000000..ed178ff --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/prompt-cache.js @@ -0,0 +1,50 @@ +import { getContextColor, RESET, label, warning as warningColor } from '../colors.js'; +import { t } from '../../i18n/index.js'; +function getPromptCacheWarningSeconds(ttlSeconds) { + return Math.min(ttlSeconds, Math.max(60, Math.floor(ttlSeconds / 5))); +} +function colorPromptCacheValue(value, state, ctx) { + if (state === 'expired') { + return label(value, ctx.config?.colors); + } + if (state === 'warning') { + return warningColor(value, ctx.config?.colors); + } + return `${getContextColor(0, ctx.config?.colors)}${value}${RESET}`; +} +export function formatPromptCacheCountdown(remainingMs) { + if (remainingMs <= 0) { + return t('status.expired'); + } + const totalSeconds = Math.ceil(remainingMs / 1000); + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; + if (hours > 0) { + return `${hours}h ${minutes}m ${seconds}s`; + } + return `${minutes}m ${seconds}s`; +} +export function renderPromptCacheLine(ctx, now = Date.now()) { + const display = ctx.config?.display; + if (!display?.showPromptCache) { + return null; + } + const lastAssistantResponseAt = ctx.transcript.lastAssistantResponseAt; + if (!lastAssistantResponseAt || Number.isNaN(lastAssistantResponseAt.getTime())) { + return null; + } + const ttlSeconds = (typeof display.promptCacheTtlSeconds === 'number' + && Number.isFinite(display.promptCacheTtlSeconds) + && display.promptCacheTtlSeconds > 0) + ? Math.floor(display.promptCacheTtlSeconds) + : 300; + const remainingMs = (lastAssistantResponseAt.getTime() + ttlSeconds * 1000) - now; + const state = remainingMs <= 0 + ? 'expired' + : remainingMs <= getPromptCacheWarningSeconds(ttlSeconds) * 1000 + ? 'warning' + : 'active'; + return `${label(t('label.promptCache'), ctx.config?.colors)} ${colorPromptCacheValue(`⏱ ${formatPromptCacheCountdown(remainingMs)}`, state, ctx)}`; +} +//# sourceMappingURL=prompt-cache.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/prompt-cache.js.map b/plugins/balance-hud/dist/render/lines/prompt-cache.js.map new file mode 100644 index 0000000..1225c4b --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/prompt-cache.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prompt-cache.js","sourceRoot":"","sources":["../../../src/render/lines/prompt-cache.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-time.d.ts b/plugins/balance-hud/dist/render/lines/session-time.d.ts new file mode 100644 index 0000000..6da4c96 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-time.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from '../../types.js'; +export declare function renderSessionTimeLine(ctx: RenderContext, nowFn?: () => number): string | null; +//# sourceMappingURL=session-time.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-time.d.ts.map b/plugins/balance-hud/dist/render/lines/session-time.d.ts.map new file mode 100644 index 0000000..85371dd --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-time.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"session-time.d.ts","sourceRoot":"","sources":["../../../src/render/lines/session-time.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AA6CpD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,CA4B7F"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-time.js b/plugins/balance-hud/dist/render/lines/session-time.js new file mode 100644 index 0000000..58a7ec4 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-time.js @@ -0,0 +1,62 @@ +import { label } from '../colors.js'; +import { getLanguage, t } from '../../i18n/index.js'; +function pad(n) { + return n < 10 ? `0${n}` : `${n}`; +} +function formatStartDate(date) { + const y = date.getFullYear(); + const m = pad(date.getMonth() + 1); + const d = pad(date.getDate()); + const h = pad(date.getHours()); + const min = pad(date.getMinutes()); + return `${y}-${m}-${d} ${h}:${min}`; +} +function formatRelativeTime(ms) { + if (ms < 0) { + return t('format.justNow'); + } + const withAgo = (value) => { + const ago = t('format.ago'); + return getLanguage() === 'zh' ? `${value}${ago}` : `${value} ${ago}`; + }; + const seconds = Math.floor(ms / 1000); + if (seconds < 60) { + return withAgo(`${seconds}s`); + } + const minutes = Math.floor(seconds / 60); + if (minutes < 60) { + return withAgo(`${minutes}m`); + } + const hours = Math.floor(minutes / 60); + const remainingMinutes = minutes % 60; + if (hours < 24) { + return withAgo(remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`); + } + const days = Math.floor(hours / 24); + const remainingHours = hours % 24; + return withAgo(remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`); +} +export function renderSessionTimeLine(ctx, nowFn) { + const display = ctx.config?.display; + const showStart = display?.showSessionStartDate === true; + const showLastReply = display?.showLastResponseAt === true; + const colors = ctx.config?.colors; + if (!showStart && !showLastReply) { + return null; + } + const parts = []; + if (showStart && ctx.transcript.sessionStart) { + const startStr = formatStartDate(ctx.transcript.sessionStart); + parts.push(`${label(`${t('label.sessionStarted')}:`, colors)} ${startStr}`); + } + if (showLastReply && ctx.transcript.lastAssistantResponseAt) { + const now = nowFn ? nowFn() : Date.now(); + const elapsed = now - ctx.transcript.lastAssistantResponseAt.getTime(); + parts.push(`${label(`${t('label.lastReply')}:`, colors)} ${formatRelativeTime(elapsed)}`); + } + if (parts.length === 0) { + return null; + } + return parts.join(` ${label('│', colors)} `); +} +//# sourceMappingURL=session-time.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-time.js.map b/plugins/balance-hud/dist/render/lines/session-time.js.map new file mode 100644 index 0000000..bd416c4 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-time.js.map @@ -0,0 +1 @@ +{"version":3,"file":"session-time.js","sourceRoot":"","sources":["../../../src/render/lines/session-time.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AAErD,SAAS,GAAG,CAAC,CAAS;IACpB,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,eAAe,CAAC,IAAU;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC7B,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACnC,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,kBAAkB,CAAC,EAAU;IACpC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,KAAa,EAAU,EAAE;QACxC,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;QAC5B,OAAO,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,GAAG,EAAE,CAAC;IACvE,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACvC,MAAM,gBAAgB,GAAG,OAAO,GAAG,EAAE,CAAC;IACtC,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,OAAO,OAAO,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,gBAAgB,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IACpC,MAAM,cAAc,GAAG,KAAK,GAAG,EAAE,CAAC;IAClC,OAAO,OAAO,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,cAAc,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAkB,EAAE,KAAoB;IAC5E,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,MAAM,SAAS,GAAG,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACzD,MAAM,aAAa,GAAG,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAElC,IAAI,CAAC,SAAS,IAAI,CAAC,aAAa,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,SAAS,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9D,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,aAAa,IAAI,GAAG,CAAC,UAAU,CAAC,uBAAuB,EAAE,CAAC;QAC5D,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,uBAAuB,CAAC,OAAO,EAAE,CAAC;QACvE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;AAC/C,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-tokens.d.ts b/plugins/balance-hud/dist/render/lines/session-tokens.d.ts new file mode 100644 index 0000000..256c24a --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-tokens.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from '../../types.js'; +export declare function renderSessionTokensLine(ctx: RenderContext): string | null; +//# sourceMappingURL=session-tokens.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-tokens.d.ts.map b/plugins/balance-hud/dist/render/lines/session-tokens.d.ts.map new file mode 100644 index 0000000..819997c --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-tokens.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"session-tokens.d.ts","sourceRoot":"","sources":["../../../src/render/lines/session-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAKpD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA2BzE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-tokens.js b/plugins/balance-hud/dist/render/lines/session-tokens.js new file mode 100644 index 0000000..d60127d --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-tokens.js @@ -0,0 +1,27 @@ +import { label } from '../colors.js'; +import { t } from '../../i18n/index.js'; +import { formatTokens } from '../../utils/format.js'; +export function renderSessionTokensLine(ctx) { + const display = ctx.config?.display; + if (display?.showSessionTokens === false) { + return null; + } + const tokens = ctx.transcript.sessionTokens; + if (!tokens) { + return null; + } + const total = tokens.inputTokens + tokens.outputTokens + tokens.cacheCreationTokens + tokens.cacheReadTokens; + if (total === 0) { + return null; + } + const colors = ctx.config?.colors; + const parts = [ + `${t('format.in')}: ${formatTokens(tokens.inputTokens)}`, + `${t('format.out')}: ${formatTokens(tokens.outputTokens)}`, + ]; + if (tokens.cacheCreationTokens > 0 || tokens.cacheReadTokens > 0) { + parts.push(`${t('format.cache')}: ${formatTokens(tokens.cacheCreationTokens + tokens.cacheReadTokens)}`); + } + return label(`${t('label.tokens')} ${formatTokens(total)} (${parts.join(', ')})`, colors); +} +//# sourceMappingURL=session-tokens.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/session-tokens.js.map b/plugins/balance-hud/dist/render/lines/session-tokens.js.map new file mode 100644 index 0000000..a158816 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/session-tokens.js.map @@ -0,0 +1 @@ +{"version":3,"file":"session-tokens.js","sourceRoot":"","sources":["../../../src/render/lines/session-tokens.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,UAAU,uBAAuB,CAAC,GAAkB;IACxD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,IAAI,OAAO,EAAE,iBAAiB,KAAK,KAAK,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC;IAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,mBAAmB,GAAG,MAAM,CAAC,eAAe,CAAC;IAC7G,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAClC,MAAM,KAAK,GAAa;QACtB,GAAG,CAAC,CAAC,WAAW,CAAC,KAAK,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;QACxD,GAAG,CAAC,CAAC,YAAY,CAAC,KAAK,YAAY,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;KAC3D,CAAC;IAEF,IAAI,MAAM,CAAC,mBAAmB,GAAG,CAAC,IAAI,MAAM,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;QACjE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,KAAK,YAAY,CAAC,MAAM,CAAC,mBAAmB,GAAG,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAC3G,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC5F,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/usage.d.ts b/plugins/balance-hud/dist/render/lines/usage.d.ts new file mode 100644 index 0000000..76c7881 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/usage.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from "../../types.js"; +export declare function renderUsageLine(ctx: RenderContext, alignLabels?: boolean): string | null; +//# sourceMappingURL=usage.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/usage.d.ts.map b/plugins/balance-hud/dist/render/lines/usage.d.ts.map new file mode 100644 index 0000000..fd23145 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/usage.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/render/lines/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAcpD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,aAAa,EAClB,WAAW,UAAQ,GAClB,MAAM,GAAG,IAAI,CAgIf"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/usage.js b/plugins/balance-hud/dist/render/lines/usage.js new file mode 100644 index 0000000..8bb954b --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/usage.js @@ -0,0 +1,201 @@ +import { isLimitReached } from "../../types.js"; +import { shouldHideUsage } from "../../stdin.js"; +import { critical, label, getQuotaColor, quotaBar, RESET } from "../colors.js"; +import { getAdaptiveBarWidth } from "../../utils/terminal.js"; +import { t } from "../../i18n/index.js"; + +import { formatResetTime } from "../format-reset-time.js"; +const FIVE_HOUR_WINDOW_MS = 5 * 60 * 60 * 1000; +const SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000; +export function renderUsageLine(ctx, alignLabels = false) { + const display = ctx.config?.display; + const colors = ctx.config?.colors; + if (display?.showUsage === false) { + return null; + } + if (!ctx.usageData) { + return null; + } + if (shouldHideUsage(ctx.stdin)) { + return null; + } + const usageLabel = ''; + const hasWindowData = ctx.usageData.fiveHour !== null || ctx.usageData.sevenDay !== null; + if (!hasWindowData) { + // No API window data — balance label already shows consumed info, skip + return null; + } + const timeFormat = normalizeTimeFormat(display?.timeFormat); + const showResetLabel = display?.showResetLabel ?? true; + const resetsKey = limitResetTimeFormat(timeFormat) === 'absolute' ? "format.resets" : "format.resetsIn"; + const usageCompact = display?.usageCompact ?? false; + const usageValueMode = display?.usageValue ?? 'percent'; + if (isLimitReached(ctx.usageData)) { + const limitTimeFormat = limitResetTimeFormat(timeFormat); + const resetTime = ctx.usageData.fiveHour === 100 + ? formatResetTime(ctx.usageData.fiveHourResetAt, limitTimeFormat) + : formatResetTime(ctx.usageData.sevenDayResetAt, limitTimeFormat); + if (usageCompact) { + return critical(`⚠ Limit${resetTime ? ` (${resetTime})` : ""}`, colors); + } + const resetSuffix = resetTime + ? showResetLabel + ? ` (${t(resetsKey)} ${resetTime})` + : ` (${resetTime})` + : ""; + return critical(`⚠ ${t("status.limitReached")}${resetSuffix}`, colors); + } + const threshold = display?.usageThreshold ?? 0; + const fiveHour = ctx.usageData.fiveHour; + const sevenDay = ctx.usageData.sevenDay; + const effectiveUsage = Math.max(fiveHour ?? 0, sevenDay ?? 0); + if (effectiveUsage < threshold) { + return null; + } + const sevenDayThreshold = display?.sevenDayThreshold ?? 80; + if (usageCompact) { + const fiveHourPart = fiveHour !== null + ? formatCompactWindowPart("5h", fiveHour, ctx.usageData.fiveHourResetAt, FIVE_HOUR_WINDOW_MS, timeFormat, colors, usageValueMode) + : null; + const sevenDayPart = (sevenDay !== null && (fiveHour === null || sevenDay >= sevenDayThreshold)) + ? formatCompactWindowPart("7d", sevenDay, ctx.usageData.sevenDayResetAt, SEVEN_DAY_WINDOW_MS, timeFormat, colors, usageValueMode) + : null; + if (fiveHourPart && sevenDayPart) { + return `${fiveHourPart} | ${sevenDayPart}`; + } + const compactLine = fiveHourPart ?? sevenDayPart; + return compactLine ? compactLine : null; + } + const usageBarEnabled = display?.usageBarEnabled ?? true; + const barWidth = getAdaptiveBarWidth(); + if (fiveHour === null && sevenDay !== null) { + const weeklyOnlyPart = formatUsageWindowPart({ + percent: sevenDay, + resetAt: ctx.usageData.sevenDayResetAt, + windowMs: SEVEN_DAY_WINDOW_MS, + colors, + usageBarEnabled, + barWidth, + timeFormat, + showResetLabel, + usageValueMode, + }); + return weeklyOnlyPart; + } + const fiveHourPart = formatUsageWindowPart({ + percent: fiveHour, + resetAt: ctx.usageData.fiveHourResetAt, + windowMs: FIVE_HOUR_WINDOW_MS, + colors, + usageBarEnabled, + barWidth, + timeFormat, + showResetLabel, + usageValueMode, + }); + if (sevenDay !== null && sevenDay >= sevenDayThreshold) { + const sevenDayPart = formatUsageWindowPart({ + percent: sevenDay, + resetAt: ctx.usageData.sevenDayResetAt, + windowMs: SEVEN_DAY_WINDOW_MS, + colors, + usageBarEnabled, + barWidth, + timeFormat, + showResetLabel, + usageValueMode, + }); + return `${fiveHourPart} | ${sevenDayPart}`; + } + return fiveHourPart; +} +export function renderBalanceLine(ctx) { + const display = ctx.config?.display; + const colors = ctx.config?.colors; + if (display?.showUsage === false) { + return null; + } + if (!ctx.usageData?.balanceLabel) { + return null; + } + if (shouldHideUsage(ctx.stdin)) { + return null; + } + return ctx.usageData.balanceLabel; +} +function formatCompactWindowPart(windowLabel, percent, resetAt, windowMs, timeFormat, colors, usageValueMode = 'percent') { + const usageDisplay = formatUsagePercent(percent, colors, usageValueMode); + const reset = formatWindowTime(resetAt, windowMs, timeFormat); + return reset + ? `${usageDisplay} ${label(`(${reset})`, colors)}` + : usageDisplay; +} +function formatUsagePercent(percent, colors, mode = 'percent') { + if (percent === null) { + return label("--", colors); + } + const color = getQuotaColor(percent, colors); + const displayPercent = mode === 'remaining' ? Math.max(0, 100 - percent) : percent; + return `${color}${displayPercent}%${RESET}`; +} +function formatUsageWindowPart({ percent, resetAt, windowMs, colors, usageBarEnabled, barWidth, timeFormat = 'relative', showResetLabel, usageValueMode = 'percent', }) { + const usageDisplay = formatUsagePercent(percent, colors, usageValueMode); + const reset = formatWindowTime(resetAt, windowMs, timeFormat); + const showResetWording = timeFormat !== 'elapsed' && timeFormat !== 'elapsedAndAbsolute'; + const resetsKey = timeFormat === 'absolute' ? "format.resets" : "format.resetsIn"; + const resetSuffix = reset + ? showResetLabel && showResetWording + ? `(${t(resetsKey)} ${reset})` + : `(${reset})` + : ""; + if (usageBarEnabled) { + return resetSuffix + ? `${quotaBar(percent ?? 0, barWidth, colors)} ${usageDisplay} ${resetSuffix}` + : `${quotaBar(percent ?? 0, barWidth, colors)} ${usageDisplay}`; + } + return resetSuffix + ? `${usageDisplay} ${resetSuffix}` + : usageDisplay; +} +function normalizeTimeFormat(value) { + if (value === 'absolute' + || value === 'both' + || value === 'elapsed' + || value === 'elapsedAndAbsolute') { + return value; + } + return 'relative'; +} +function limitResetTimeFormat(timeFormat) { + if (timeFormat === 'elapsedAndAbsolute') { + return 'absolute'; + } + if (timeFormat === 'elapsed') { + return 'relative'; + } + return timeFormat; +} +function formatWindowTime(resetAt, windowMs, timeFormat) { + if (timeFormat === 'elapsed') { + return formatElapsedWindow(resetAt, windowMs); + } + if (timeFormat === 'elapsedAndAbsolute') { + const elapsed = formatElapsedWindow(resetAt, windowMs); + const absolute = formatResetTime(resetAt, 'absolute'); + if (elapsed && absolute) { + return `${elapsed}, ${absolute}`; + } + return elapsed || absolute; + } + return formatResetTime(resetAt, timeFormat); +} +function formatElapsedWindow(resetAt, windowMs) { + if (!resetAt) { + return ''; + } + const windowStart = resetAt.getTime() - windowMs; + const rawElapsed = ((Date.now() - windowStart) / windowMs) * 100; + const elapsed = Math.max(0, Math.min(100, Math.round(rawElapsed))); + return `${elapsed}% elapsed`; +} +//# sourceMappingURL=usage.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/lines/usage.js.map b/plugins/balance-hud/dist/render/lines/usage.js.map new file mode 100644 index 0000000..aeeb930 --- /dev/null +++ b/plugins/balance-hud/dist/render/lines/usage.js.map @@ -0,0 +1 @@ +{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../../src/render/lines/usage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/model-display.d.ts b/plugins/balance-hud/dist/render/model-display.d.ts new file mode 100644 index 0000000..a235ee7 --- /dev/null +++ b/plugins/balance-hud/dist/render/model-display.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from '../types.js'; +export declare function formatModelDisplay(model: string, ctx: RenderContext): string; +//# sourceMappingURL=model-display.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/model-display.d.ts.map b/plugins/balance-hud/dist/render/model-display.d.ts.map new file mode 100644 index 0000000..137526f --- /dev/null +++ b/plugins/balance-hud/dist/render/model-display.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"model-display.d.ts","sourceRoot":"","sources":["../../src/render/model-display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAiB5E"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/model-display.js b/plugins/balance-hud/dist/render/model-display.js new file mode 100644 index 0000000..aecfbdb --- /dev/null +++ b/plugins/balance-hud/dist/render/model-display.js @@ -0,0 +1,19 @@ +import { getProviderLabel } from '../stdin.js'; +export function formatModelDisplay(model, ctx) { + let effortSuffix = ''; + if (ctx.effortLevel && ctx.effortSymbol) { + effortSuffix = ` ${ctx.effortSymbol} ${ctx.effortLevel}`; + } + else if (ctx.effortLevel) { + effortSuffix = ` ${ctx.effortLevel}`; + } + const display = ctx.config?.display; + const autoProvider = getProviderLabel(ctx.stdin); + if (display?.showProvider) { + const providerLabel = display.providerName?.trim() || autoProvider; + const core = `${model}${effortSuffix}`; + return providerLabel ? `${providerLabel} | ${core}` : core; + } + return autoProvider ? `${model} | ${autoProvider}${effortSuffix}` : `${model}${effortSuffix}`; +} +//# sourceMappingURL=model-display.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/model-display.js.map b/plugins/balance-hud/dist/render/model-display.js.map new file mode 100644 index 0000000..11bd8d1 --- /dev/null +++ b/plugins/balance-hud/dist/render/model-display.js.map @@ -0,0 +1 @@ +{"version":3,"file":"model-display.js","sourceRoot":"","sources":["../../src/render/model-display.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,UAAU,kBAAkB,CAAC,KAAa,EAAE,GAAkB;IAClE,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;QACxC,YAAY,GAAG,IAAI,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;IAC3D,CAAC;SAAM,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QAC3B,YAAY,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACjD,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;QAC1B,MAAM,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,YAAY,CAAC;QACnE,MAAM,IAAI,GAAG,GAAG,KAAK,GAAG,YAAY,EAAE,CAAC;QACvC,OAAO,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IAED,OAAO,YAAY,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,YAAY,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,YAAY,EAAE,CAAC;AAChG,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/session-line.d.ts b/plugins/balance-hud/dist/render/session-line.d.ts new file mode 100644 index 0000000..5cff9f6 --- /dev/null +++ b/plugins/balance-hud/dist/render/session-line.d.ts @@ -0,0 +1,7 @@ +import type { RenderContext } from '../types.js'; +/** + * Renders the full session line (model + context bar + project + git + counts + usage + duration). + * Used for compact layout mode. + */ +export declare function renderSessionLine(ctx: RenderContext): string; +//# sourceMappingURL=session-line.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/session-line.d.ts.map b/plugins/balance-hud/dist/render/session-line.d.ts.map new file mode 100644 index 0000000..7c4104d --- /dev/null +++ b/plugins/balance-hud/dist/render/session-line.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"session-line.d.ts","sourceRoot":"","sources":["../../src/render/session-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAmBjD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAmU5D"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/session-line.js b/plugins/balance-hud/dist/render/session-line.js new file mode 100644 index 0000000..77f56c6 --- /dev/null +++ b/plugins/balance-hud/dist/render/session-line.js @@ -0,0 +1,363 @@ +import { isLimitReached } from '../types.js'; +import { getContextPercent, getBufferedPercent, getModelName, formatModelName, shouldHideUsage } from '../stdin.js'; +import { getOutputSpeed } from '../speed-tracker.js'; +import { coloredBar, critical, git as gitColor, gitBranch as gitBranchColor, label, model as modelColor, project as projectColor, getContextColor, getQuotaColor, quotaBar, custom as customColor, RESET } from './colors.js'; +import { getAdaptiveBarWidth } from '../utils/terminal.js'; +import { renderCostEstimate } from './lines/cost.js'; +import { renderPromptCacheLine } from './lines/prompt-cache.js'; +import { renderSessionTimeLine } from './lines/session-time.js'; +import { renderAdvisorLine } from './lines/advisor.js'; +import { t } from '../i18n/index.js'; +import { formatResetTime } from './format-reset-time.js'; +import { formatTokens, formatContextValue } from '../utils/format.js'; +import { createDebug } from '../debug.js'; +import { formatModelDisplay } from './model-display.js'; +const debug = createDebug('context'); +/** + * Renders the full session line (model + context bar + project + git + counts + usage + duration). + * Used for compact layout mode. + */ +export function renderSessionLine(ctx) { + const model = formatModelName(getModelName(ctx.stdin), ctx.config?.display?.modelFormat, ctx.config?.display?.modelOverride); + const autoCompactWindow = ctx.config?.display?.autoCompactWindow ?? null; + const rawPercent = getContextPercent(ctx.stdin, autoCompactWindow); + const bufferedPercent = getBufferedPercent(ctx.stdin, autoCompactWindow); + const autocompactMode = ctx.config?.display?.autocompactBuffer ?? 'enabled'; + const percent = autocompactMode === 'disabled' ? rawPercent : bufferedPercent; + if (autocompactMode === 'disabled') { + debug(`autocompactBuffer=disabled, showing raw ${rawPercent}% (buffered would be ${bufferedPercent}%)`); + } + const colors = ctx.config?.colors; + const display = ctx.config?.display; + const contextThresholds = { + warning: display?.contextWarningThreshold, + critical: display?.contextCriticalThreshold, + }; + const barWidth = getAdaptiveBarWidth(); + const bar = coloredBar(percent, barWidth, colors, contextThresholds); + const parts = []; + const timeFormat = display?.timeFormat ?? 'relative'; + const resetsKey = timeFormat === 'absolute' ? 'format.resets' : 'format.resetsIn'; + const contextValueMode = display?.contextValue ?? 'percent'; + const contextValue = formatContextValue(ctx, percent, contextValueMode); + const contextValueDisplay = `${getContextColor(percent, colors, contextThresholds)}${contextValue}${RESET}`; + const customLine = display?.customLine; + const customLinePosition = display?.customLinePosition ?? 'last'; + if (customLine && customLinePosition === 'first') { + parts.push(customColor(customLine, colors)); + } + // Model and context bar + const modelDisplay = formatModelDisplay(model, ctx); + if (display?.showModel !== false && display?.showContextBar !== false) { + parts.push(`${modelColor(`[${modelDisplay}]`, colors)} ${bar} ${contextValueDisplay}`); + } + else if (display?.showModel !== false) { + parts.push(`${modelColor(`[${modelDisplay}]`, colors)} ${contextValueDisplay}`); + } + else if (display?.showContextBar !== false) { + parts.push(`${bar} ${contextValueDisplay}`); + } + else { + parts.push(contextValueDisplay); + } + // Project path + git status + let projectPart = null; + if (display?.showProject !== false && ctx.stdin.cwd) { + // Split by both Unix (/) and Windows (\) separators for cross-platform support + const segments = ctx.stdin.cwd.split(/[/\\]/).filter(Boolean); + const pathLevels = ctx.config?.pathLevels ?? 1; + // Always join with forward slash for consistent display + // Handle root path (/) which results in empty segments + const projectPath = segments.length > 0 ? segments.slice(-pathLevels).join('/') : '/'; + projectPart = projectColor(projectPath, colors); + } + let gitPart = ''; + const gitConfig = ctx.config?.gitStatus; + const showGit = gitConfig?.enabled ?? true; + const branchOverflow = gitConfig?.branchOverflow ?? 'truncate'; + if (showGit && ctx.gitStatus) { + const gitParts = [ctx.gitStatus.branch]; + // Show dirty indicator + if ((gitConfig?.showDirty ?? true) && ctx.gitStatus.isDirty) { + gitParts.push('*'); + } + // Show ahead/behind (with space separator for readability) + if (gitConfig?.showAheadBehind) { + if (ctx.gitStatus.ahead > 0) { + gitParts.push(` ↑${ctx.gitStatus.ahead}`); + } + if (ctx.gitStatus.behind > 0) { + gitParts.push(` ↓${ctx.gitStatus.behind}`); + } + } + // Show file stats in Starship-compatible format (!modified +added ✘deleted ?untracked) + if (gitConfig?.showFileStats && ctx.gitStatus.fileStats) { + const { modified, added, deleted, untracked } = ctx.gitStatus.fileStats; + const statParts = []; + if (modified > 0) + statParts.push(`!${modified}`); + if (added > 0) + statParts.push(`+${added}`); + if (deleted > 0) + statParts.push(`✘${deleted}`); + if (untracked > 0) + statParts.push(`?${untracked}`); + if (statParts.length > 0) { + gitParts.push(` ${statParts.join(' ')}`); + } + } + gitPart = `${gitColor('git:(', colors)}${gitBranchColor(gitParts.join(''), colors)}${gitColor(')', colors)}`; + } + if (projectPart && gitPart) { + if (branchOverflow === 'wrap') { + parts.push(projectPart); + parts.push(gitPart); + } + else { + parts.push(`${projectPart} ${gitPart}`); + } + } + else if (projectPart) { + parts.push(projectPart); + } + else if (gitPart) { + parts.push(gitPart); + } + // Session name (custom title from /rename, or auto-generated slug) + if (display?.showSessionName && ctx.transcript.sessionName) { + parts.push(label(ctx.transcript.sessionName, colors)); + } + if (display?.showClaudeCodeVersion && ctx.claudeCodeVersion) { + parts.push(label(`CC v${ctx.claudeCodeVersion}`, colors)); + } + // Config counts (respects environmentThreshold) + if (display?.showConfigCounts !== false) { + const totalCounts = ctx.claudeMdCount + ctx.rulesCount + ctx.mcpCount + ctx.hooksCount; + const envThreshold = display?.environmentThreshold ?? 0; + if (totalCounts > 0 && totalCounts >= envThreshold) { + if (ctx.claudeMdCount > 0) { + parts.push(label(`${ctx.claudeMdCount} CLAUDE.md`, colors)); + } + if (ctx.rulesCount > 0) { + parts.push(label(`${ctx.rulesCount} ${t('label.rules')}`, colors)); + } + if (ctx.mcpCount > 0) { + parts.push(label(`${ctx.mcpCount} MCPs`, colors)); + } + if (ctx.hooksCount > 0) { + parts.push(label(`${ctx.hooksCount} ${t('label.hooks')}`, colors)); + } + } + } + // Usage limits display (shown when enabled in config, respects usageThreshold) + if (display?.showUsage !== false && ctx.usageData && !shouldHideUsage(ctx.stdin)) { + const usageCompact = display?.usageCompact ?? false; + const showResetLabel = display?.showResetLabel ?? true; + const usageValueMode = display?.usageValue ?? 'percent'; + const hasWindowData = ctx.usageData.fiveHour !== null || ctx.usageData.sevenDay !== null; + if (isLimitReached(ctx.usageData)) { + const resetTime = ctx.usageData.fiveHour === 100 + ? formatResetTime(ctx.usageData.fiveHourResetAt, timeFormat) + : formatResetTime(ctx.usageData.sevenDayResetAt, timeFormat); + if (usageCompact) { + parts.push(critical(`⚠ Limit${resetTime ? ` (${resetTime})` : ''}`, colors)); + } + else { + const resetSuffix = resetTime + ? showResetLabel + ? ` (${t(resetsKey)} ${resetTime})` + : ` (${resetTime})` + : ''; + parts.push(critical(`⚠ ${t('status.limitReached')}${resetSuffix}`, colors)); + } + } + else { + const usageThreshold = display?.usageThreshold ?? 0; + const fiveHour = ctx.usageData.fiveHour; + const sevenDay = ctx.usageData.sevenDay; + const effectiveUsage = Math.max(fiveHour ?? 0, sevenDay ?? 0); + if ((hasWindowData || !ctx.usageData.balanceLabel) && effectiveUsage >= usageThreshold) { + const usageBarEnabled = display?.usageBarEnabled ?? true; + if (usageCompact) { + const fiveHourPart = fiveHour !== null + ? formatCompactWindowPart('5h', fiveHour, ctx.usageData.fiveHourResetAt, timeFormat, colors, usageValueMode) + : null; + const sevenDayThreshold = display?.sevenDayThreshold ?? 80; + const sevenDayPart = (sevenDay !== null && (fiveHour === null || sevenDay >= sevenDayThreshold)) + ? formatCompactWindowPart('7d', sevenDay, ctx.usageData.sevenDayResetAt, timeFormat, colors, usageValueMode) + : null; + if (fiveHourPart && sevenDayPart) { + parts.push(fiveHourPart); + parts.push(sevenDayPart); + } + else if (fiveHourPart) { + parts.push(fiveHourPart); + } + else if (sevenDayPart) { + parts.push(sevenDayPart); + } + } + else if (fiveHour === null && sevenDay !== null) { + const weeklyOnlyPart = formatUsageWindowPart({ + label: t('label.weekly'), + percent: sevenDay, + resetAt: ctx.usageData.sevenDayResetAt, + colors, + usageBarEnabled, + barWidth, + timeFormat, + showResetLabel, + forceLabel: true, + usageValueMode, + }); + parts.push(weeklyOnlyPart); + } + else { + const fiveHourPart = formatUsageWindowPart({ + label: '5h', + percent: fiveHour, + resetAt: ctx.usageData.fiveHourResetAt, + colors, + usageBarEnabled, + barWidth, + timeFormat, + showResetLabel, + usageValueMode, + }); + const sevenDayThreshold = display?.sevenDayThreshold ?? 80; + if (sevenDay !== null && sevenDay >= sevenDayThreshold) { + const sevenDayPart = formatUsageWindowPart({ + label: t('label.weekly'), + percent: sevenDay, + resetAt: ctx.usageData.sevenDayResetAt, + colors, + usageBarEnabled, + barWidth, + timeFormat, + showResetLabel, + forceLabel: true, + usageValueMode, + }); + parts.push(`${label(t('label.usage'), colors)} ${fiveHourPart}`); + parts.push(sevenDayPart); + } + else { + parts.push(`${label(t('label.usage'), colors)} ${fiveHourPart}`); + } + } + } + } + if (ctx.usageData.balanceLabel) { + if (!hasWindowData) { + parts.push(`${label(t('label.usage'), colors)} ${ctx.usageData.balanceLabel}`); + } + else { + parts.push(ctx.usageData.balanceLabel); + } + } + } + // Session token usage (cumulative) + if (display?.showSessionTokens && ctx.transcript.sessionTokens) { + const st = ctx.transcript.sessionTokens; + const total = st.inputTokens + st.outputTokens + st.cacheCreationTokens + st.cacheReadTokens; + if (total > 0) { + parts.push(label(`${t('format.tok')}: ${formatTokens(total)} (${t('format.in')}: ${formatTokens(st.inputTokens)}, ${t('format.out')}: ${formatTokens(st.outputTokens)})`, colors)); + } + } + // Compaction count from transcript compact_boundary entries (opt-in, + // hidden until the first compaction) + if (display?.showCompactions) { + const compactions = ctx.transcript.compactionCount ?? 0; + if (compactions > 0) { + parts.push(label(`${t('label.compactions')}: ${compactions}`, colors)); + } + } + // Advisor model (when `/advisor` is configured for the session) + if (display?.showAdvisor) { + const advisorLine = renderAdvisorLine(ctx); + if (advisorLine) { + parts.push(advisorLine); + } + } + if (display?.showDuration !== false && ctx.sessionDuration) { + parts.push(label(`⏱️ ${ctx.sessionDuration}`, colors)); + } + const sessionTimeLine = renderSessionTimeLine(ctx); + if (sessionTimeLine) { + parts.push(sessionTimeLine); + } + const promptCacheLine = renderPromptCacheLine(ctx); + if (promptCacheLine) { + parts.push(promptCacheLine); + } + const costEstimate = renderCostEstimate(ctx); + if (costEstimate) { + parts.push(costEstimate); + } + if (display?.showSpeed) { + const speed = getOutputSpeed(ctx.stdin); + if (speed !== null) { + parts.push(label(`${t('format.out')}: ${speed.toFixed(1)} ${t('format.tokPerSec')}`, colors)); + } + } + if (ctx.extraLabel) { + parts.push(label(ctx.extraLabel, colors)); + } + if (customLine && customLinePosition === 'last') { + parts.push(customColor(customLine, colors)); + } + let line = parts.join(' | '); + // Token breakdown at high context + if (display?.showTokenBreakdown !== false && percent >= (display?.contextCriticalThreshold ?? 85)) { + const usage = ctx.stdin.context_window?.current_usage; + if (usage) { + const input = formatTokens(usage.input_tokens ?? 0); + const cache = formatTokens((usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0)); + line += label(` (${t('format.in')}: ${input}, ${t('format.cache')}: ${cache})`, colors); + } + } + return line; +} +function formatCompactWindowPart(windowLabel, percent, resetAt, timeFormat, colors, usageValueMode = 'percent') { + const usageDisplay = formatUsagePercent(percent, colors, usageValueMode); + const reset = formatResetTime(resetAt, timeFormat); + const styledLabel = label(`${windowLabel}:`, colors); + return reset + ? `${styledLabel} ${usageDisplay} ${label(`(${reset})`, colors)}` + : `${styledLabel} ${usageDisplay}`; +} +function formatUsagePercent(percent, colors, mode = 'percent') { + if (percent === null) { + return label('--', colors); + } + const color = getQuotaColor(percent, colors); + const displayPercent = mode === 'remaining' ? Math.max(0, 100 - percent) : percent; + return `${color}${displayPercent}%${RESET}`; +} +function formatUsageWindowPart({ label: windowLabel, percent, resetAt, colors, usageBarEnabled, barWidth, timeFormat = 'relative', showResetLabel, forceLabel = false, usageValueMode = 'percent', }) { + const usageDisplay = formatUsagePercent(percent, colors, usageValueMode); + const reset = formatResetTime(resetAt, timeFormat); + const styledLabel = label(windowLabel, colors); + // "resets in X" for relative/both; "resets X" for absolute (avoids "resets in at 14:30") + const resetsKey = timeFormat === 'absolute' ? 'format.resets' : 'format.resetsIn'; + if (usageBarEnabled) { + // Relative mode keeps the upstream "(duration / windowLabel)" pattern (e.g. "2h 30m / 5h"). + // Absolute/both modes use the preposition form instead — "(at 14:30 / 5h)" is incoherent. + const barReset = timeFormat === 'relative' + ? (reset ? `${reset} / ${windowLabel}` : null) + : (reset ? (showResetLabel ? `${t(resetsKey)} ${reset}` : reset) : null); + const body = barReset + ? `${quotaBar(percent ?? 0, barWidth, colors)} ${usageDisplay} (${barReset})` + : `${quotaBar(percent ?? 0, barWidth, colors)} ${usageDisplay}`; + return forceLabel ? `${styledLabel} ${body}` : body; + } + const resetSuffix = reset + ? showResetLabel + ? `(${t(resetsKey)} ${reset})` + : `(${reset})` + : ''; + return resetSuffix + ? `${styledLabel} ${usageDisplay} ${resetSuffix}` + : `${styledLabel} ${usageDisplay}`; +} +//# sourceMappingURL=session-line.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/session-line.js.map b/plugins/balance-hud/dist/render/session-line.js.map new file mode 100644 index 0000000..736c608 --- /dev/null +++ b/plugins/balance-hud/dist/render/session-line.js.map @@ -0,0 +1 @@ +{"version":3,"file":"session-line.js","sourceRoot":"","sources":["../../src/render/session-line.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/skills-mcp-line.d.ts b/plugins/balance-hud/dist/render/skills-mcp-line.d.ts new file mode 100644 index 0000000..b46a00f --- /dev/null +++ b/plugins/balance-hud/dist/render/skills-mcp-line.d.ts @@ -0,0 +1,4 @@ +import type { RenderContext } from '../types.js'; +export declare function renderSkillsLine(ctx: RenderContext): string | null; +export declare function renderMcpLine(ctx: RenderContext): string | null; +//# sourceMappingURL=skills-mcp-line.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/skills-mcp-line.d.ts.map b/plugins/balance-hud/dist/render/skills-mcp-line.d.ts.map new file mode 100644 index 0000000..bd0fed6 --- /dev/null +++ b/plugins/balance-hud/dist/render/skills-mcp-line.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"skills-mcp-line.d.ts","sourceRoot":"","sources":["../../src/render/skills-mcp-line.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAOjD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAMlE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAM/D"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/skills-mcp-line.js b/plugins/balance-hud/dist/render/skills-mcp-line.js new file mode 100644 index 0000000..a4875e0 --- /dev/null +++ b/plugins/balance-hud/dist/render/skills-mcp-line.js @@ -0,0 +1,39 @@ +import { cyan, green, label } from './colors.js'; +import { sanitizeDisplayText } from '../utils/sanitize.js'; +const MAX_ITEMS_SHOWN = 4; +const ACTIVITY_NAME_MAX_LEN = 64; +export function renderSkillsLine(ctx) { + if (ctx.config?.display?.showSkills !== true) { + return null; + } + return renderNameListLine('Skills', ctx.transcript.skills ?? [], ctx.config?.colors); +} +export function renderMcpLine(ctx) { + if (ctx.config?.display?.showMcp !== true) { + return null; + } + return renderNameListLine('MCPs', ctx.transcript.mcpServers ?? [], ctx.config?.colors); +} +function renderNameListLine(title, names, colors) { + const safeNames = names.map(safeActivityName).filter((name) => Boolean(name)); + if (safeNames.length === 0) { + return null; + } + const visibleNames = safeNames.slice(0, MAX_ITEMS_SHOWN).map(name => cyan(name)); + const hiddenCount = safeNames.length - visibleNames.length; + if (hiddenCount > 0) { + visibleNames.push(label(`+${hiddenCount} more`, colors)); + } + return `${green('✓')} ${title} ${label(`(${safeNames.length})`, colors)}: ${visibleNames.join(', ')}`; +} +function safeActivityName(value) { + const sanitized = sanitizeDisplayText(value).trim(); + if (!sanitized) { + return null; + } + if (sanitized.length <= ACTIVITY_NAME_MAX_LEN) { + return sanitized; + } + return `${sanitized.slice(0, ACTIVITY_NAME_MAX_LEN - 1)}…`; +} +//# sourceMappingURL=skills-mcp-line.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/skills-mcp-line.js.map b/plugins/balance-hud/dist/render/skills-mcp-line.js.map new file mode 100644 index 0000000..8bdf8dd --- /dev/null +++ b/plugins/balance-hud/dist/render/skills-mcp-line.js.map @@ -0,0 +1 @@ +{"version":3,"file":"skills-mcp-line.js","sourceRoot":"","sources":["../../src/render/skills-mcp-line.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,IAAI,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAkB;IAC9C,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,UAAU,IAAI,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAa,EACb,KAAe,EACf,MAAmC;IAEnC,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9F,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjF,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC;IAC3D,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACpB,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE,MAAM,CAAC,KAAK,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACxG,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,SAAS,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,IAAI,qBAAqB,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,GAAG,CAAC,CAAC,GAAG,CAAC;AAC7D,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/todos-line.d.ts b/plugins/balance-hud/dist/render/todos-line.d.ts new file mode 100644 index 0000000..08e9f7a --- /dev/null +++ b/plugins/balance-hud/dist/render/todos-line.d.ts @@ -0,0 +1,3 @@ +import type { RenderContext } from "../types.js"; +export declare function renderTodosLine(ctx: RenderContext): string | null; +//# sourceMappingURL=todos-line.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/todos-line.d.ts.map b/plugins/balance-hud/dist/render/todos-line.d.ts.map new file mode 100644 index 0000000..479ea38 --- /dev/null +++ b/plugins/balance-hud/dist/render/todos-line.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"todos-line.d.ts","sourceRoot":"","sources":["../../src/render/todos-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAuBjE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/todos-line.js b/plugins/balance-hud/dist/render/todos-line.js new file mode 100644 index 0000000..fa02f0d --- /dev/null +++ b/plugins/balance-hud/dist/render/todos-line.js @@ -0,0 +1,23 @@ +import { yellow, green, label } from "./colors.js"; +import { t } from "../i18n/index.js"; +import { truncateString } from "../utils/truncate.js"; +export function renderTodosLine(ctx) { + const { todos } = ctx.transcript; + const colors = ctx.config?.colors; + if (!todos || todos.length === 0) { + return null; + } + const inProgress = todos.find((todo) => todo.status === "in_progress"); + const completed = todos.filter((todo) => todo.status === "completed").length; + const total = todos.length; + if (!inProgress) { + if (completed === total && total > 0) { + return `${green("✓")} ${t("status.allTodosComplete")} ${label(`(${completed}/${total})`, colors)}`; + } + return null; + } + const content = truncateString(inProgress.content, 50); + const progress = label(`(${completed}/${total})`, colors); + return `${yellow("▸")} ${content} ${progress}`; +} +//# sourceMappingURL=todos-line.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/todos-line.js.map b/plugins/balance-hud/dist/render/todos-line.js.map new file mode 100644 index 0000000..db021df --- /dev/null +++ b/plugins/balance-hud/dist/render/todos-line.js.map @@ -0,0 +1 @@ +{"version":3,"file":"todos-line.js","sourceRoot":"","sources":["../../src/render/todos-line.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,kBAAkB,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,UAAU,eAAe,CAAC,GAAkB;IAChD,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;IACjC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAElC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;IACvE,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IAC7E,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAE3B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,IAAI,SAAS,KAAK,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACrC,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,yBAAyB,CAAC,IAAI,KAAK,CAAC,IAAI,SAAS,IAAI,KAAK,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;QACrG,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,SAAS,IAAI,KAAK,GAAG,EAAE,MAAM,CAAC,CAAC;IAE1D,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;AACjD,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/tools-line.d.ts b/plugins/balance-hud/dist/render/tools-line.d.ts new file mode 100644 index 0000000..bdc3fbb --- /dev/null +++ b/plugins/balance-hud/dist/render/tools-line.d.ts @@ -0,0 +1,4 @@ +import type { RenderContext } from '../types.js'; +export declare function shortenToolName(name: string, maxLen: number): string; +export declare function renderToolsLine(ctx: RenderContext): string | null; +//# sourceMappingURL=tools-line.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/tools-line.d.ts.map b/plugins/balance-hud/dist/render/tools-line.d.ts.map new file mode 100644 index 0000000..9d41110 --- /dev/null +++ b/plugins/balance-hud/dist/render/tools-line.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"tools-line.d.ts","sourceRoot":"","sources":["../../src/render/tools-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAcpE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAkDjE"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/tools-line.js b/plugins/balance-hud/dist/render/tools-line.js new file mode 100644 index 0000000..e7548e8 --- /dev/null +++ b/plugins/balance-hud/dist/render/tools-line.js @@ -0,0 +1,70 @@ +import { yellow, green, cyan, label } from './colors.js'; +const ELLIPSIS = '…'; +const MCP_TOOL_NAME_PATTERN = /^mcp__.+__.+$/; +export function shortenToolName(name, maxLen) { + if (maxLen === 0) { + return name; + } + const displayName = MCP_TOOL_NAME_PATTERN.test(name) + ? (name.split('__').pop() ?? name) + : name; + if (displayName.length <= maxLen) { + return displayName; + } + return `${displayName.slice(0, Math.max(0, maxLen - 1))}${ELLIPSIS}`; +} +export function renderToolsLine(ctx) { + const hideSkillTools = ctx.config?.display?.showSkills === true; + const tools = hideSkillTools + ? ctx.transcript.tools.filter((tool) => tool.name !== 'Skill') + : ctx.transcript.tools; + const colors = ctx.config?.colors; + const toolNameMaxLength = ctx.config?.display?.toolNameMaxLength ?? 0; + const toolsMaxVisible = ctx.config?.display?.toolsMaxVisible ?? 4; + if (tools.length === 0) { + return null; + } + const parts = []; + const runningTools = tools.filter((t) => t.status === 'running'); + const completedTools = tools.filter((t) => t.status === 'completed' || t.status === 'error'); + for (const tool of runningTools.slice(-2)) { + const target = tool.target ? truncatePath(tool.target) : ''; + const name = shortenToolName(tool.name, toolNameMaxLength); + parts.push(`${yellow('◐')} ${cyan(name)}${target ? label(`: ${target}`, colors) : ''}`); + } + const toolCounts = new Map(); + for (const tool of completedTools) { + const count = toolCounts.get(tool.name) ?? 0; + toolCounts.set(tool.name, count + 1); + } + const sortedTools = Array.from(toolCounts.entries()) + .sort((a, b) => b[1] - a[1]); + const visibleTools = toolsMaxVisible === 0 + ? sortedTools + : sortedTools.slice(0, toolsMaxVisible); + for (const [name, count] of visibleTools) { + parts.push(`${green('✓')} ${shortenToolName(name, toolNameMaxLength)} ${label(`×${count}`, colors)}`); + } + const hiddenToolCount = toolsMaxVisible === 0 ? 0 : sortedTools.length - visibleTools.length; + if (hiddenToolCount > 0) { + parts.push(label(`+${hiddenToolCount} more`, colors)); + } + if (parts.length === 0) { + return null; + } + return parts.join(' | '); +} +function truncatePath(path, maxLen = 20) { + // Normalize Windows backslashes to forward slashes for consistent display + const normalizedPath = path.replace(/\\/g, '/'); + if (normalizedPath.length <= maxLen) + return normalizedPath; + // Split by forward slash (already normalized) + const parts = normalizedPath.split('/'); + const filename = parts.pop() || normalizedPath; + if (filename.length >= maxLen) { + return filename.slice(0, maxLen - 3) + '...'; + } + return '.../' + filename; +} +//# sourceMappingURL=tools-line.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/tools-line.js.map b/plugins/balance-hud/dist/render/tools-line.js.map new file mode 100644 index 0000000..76a96fa --- /dev/null +++ b/plugins/balance-hud/dist/render/tools-line.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tools-line.js","sourceRoot":"","sources":["../../src/render/tools-line.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/width.d.ts b/plugins/balance-hud/dist/render/width.d.ts new file mode 100644 index 0000000..88e0b2c --- /dev/null +++ b/plugins/balance-hud/dist/render/width.d.ts @@ -0,0 +1,5 @@ +export declare function isCjkAmbiguousWide(): boolean; +export declare function isWideCodePoint(codePoint: number): boolean; +export declare function isAmbiguousWideCodePoint(codePoint: number): boolean; +export declare function codePointCellWidth(codePoint: number, ambiguousWide: boolean): number; +//# sourceMappingURL=width.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/width.d.ts.map b/plugins/balance-hud/dist/render/width.d.ts.map new file mode 100644 index 0000000..b784453 --- /dev/null +++ b/plugins/balance-hud/dist/render/width.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"width.d.ts","sourceRoot":"","sources":["../../src/render/width.ts"],"names":[],"mappings":"AAQA,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAe1D;AAMD,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAcnE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,MAAM,CAIpF"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/width.js b/plugins/balance-hud/dist/render/width.js new file mode 100644 index 0000000..a4190f5 --- /dev/null +++ b/plugins/balance-hud/dist/render/width.js @@ -0,0 +1,50 @@ +import { isCjkLanguage } from '../i18n/index.js'; +// CJK terminals render East Asian Ambiguous-width chars (box drawing, +// block elements, arrows, etc.) as 2 cells. The HUD bar/separator/icon +// glyphs fall in those ranges, so width math must follow suit when the +// user's language is CJK — otherwise wrap calculations under-report +// visual width and the terminal itself wraps. +// https://www.unicode.org/reports/tr11/ +export function isCjkAmbiguousWide() { + return isCjkLanguage(); +} +export function isWideCodePoint(codePoint) { + return codePoint >= 0x1100 && (codePoint <= 0x115F || // Hangul Jamo + codePoint === 0x2329 || + codePoint === 0x232A || + (codePoint >= 0x2E80 && codePoint <= 0xA4CF && codePoint !== 0x303F) || + (codePoint >= 0xAC00 && codePoint <= 0xD7A3) || + (codePoint >= 0xF900 && codePoint <= 0xFAFF) || + (codePoint >= 0xFE10 && codePoint <= 0xFE19) || + (codePoint >= 0xFE30 && codePoint <= 0xFE6F) || + (codePoint >= 0xFF00 && codePoint <= 0xFF60) || + (codePoint >= 0xFFE0 && codePoint <= 0xFFE6) || + (codePoint >= 0x1F300 && codePoint <= 0x1FAFF) || + (codePoint >= 0x20000 && codePoint <= 0x3FFFD)); +} +// East Asian Ambiguous-width ranges actually emitted by the HUD: +// box drawing (│ ─), block elements (█ ░), geometric shapes (◐ ● ▸), +// arrows (↑ ↓ →), math operators (≤ ≥), misc symbols (⚠), dingbats +// (✓ ✘), general punctuation (— …), misc technical (⏱). +export function isAmbiguousWideCodePoint(codePoint) { + if (codePoint < 0x2010) + return false; + return ((codePoint >= 0x2010 && codePoint <= 0x2027) || + (codePoint >= 0x2030 && codePoint <= 0x205E) || + (codePoint >= 0x2190 && codePoint <= 0x21FF) || + (codePoint >= 0x2200 && codePoint <= 0x22FF) || + (codePoint >= 0x2300 && codePoint <= 0x23FF) || + (codePoint >= 0x2460 && codePoint <= 0x24FF) || + (codePoint >= 0x2500 && codePoint <= 0x259F) || + (codePoint >= 0x25A0 && codePoint <= 0x25FF) || + (codePoint >= 0x2600 && codePoint <= 0x26FF) || + (codePoint >= 0x2700 && codePoint <= 0x27BF)); +} +export function codePointCellWidth(codePoint, ambiguousWide) { + if (isWideCodePoint(codePoint)) + return 2; + if (ambiguousWide && isAmbiguousWideCodePoint(codePoint)) + return 2; + return 1; +} +//# sourceMappingURL=width.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/render/width.js.map b/plugins/balance-hud/dist/render/width.js.map new file mode 100644 index 0000000..70bfff4 --- /dev/null +++ b/plugins/balance-hud/dist/render/width.js.map @@ -0,0 +1 @@ +{"version":3,"file":"width.js","sourceRoot":"","sources":["../../src/render/width.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,sEAAsE;AACtE,uEAAuE;AACvE,uEAAuE;AACvE,oEAAoE;AACpE,8CAA8C;AAC9C,wCAAwC;AACxC,MAAM,UAAU,kBAAkB;IAChC,OAAO,aAAa,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,SAAS,IAAI,MAAM,IAAI,CAC5B,SAAS,IAAI,MAAM,IAAI,cAAc;QACrC,SAAS,KAAK,MAAM;QACpB,SAAS,KAAK,MAAM;QACpB,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,IAAI,SAAS,KAAK,MAAM,CAAC;QACpE,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,CAAC;QAC9C,CAAC,SAAS,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,CAAC,CAC/C,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,qEAAqE;AACrE,mEAAmE;AACnE,wDAAwD;AACxD,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,IAAI,SAAS,GAAG,MAAM;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,CACL,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,SAAiB,EAAE,aAAsB;IAC1E,IAAI,eAAe,CAAC,SAAS,CAAC;QAAE,OAAO,CAAC,CAAC;IACzC,IAAI,aAAa,IAAI,wBAAwB,CAAC,SAAS,CAAC;QAAE,OAAO,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACX,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/speed-tracker.d.ts b/plugins/balance-hud/dist/speed-tracker.d.ts new file mode 100644 index 0000000..1edca8e --- /dev/null +++ b/plugins/balance-hud/dist/speed-tracker.d.ts @@ -0,0 +1,7 @@ +import type { StdinData } from './types.js'; +export type SpeedTrackerDeps = { + homeDir: () => string; + now: () => number; +}; +export declare function getOutputSpeed(stdin: StdinData, overrides?: Partial): number | null; +//# sourceMappingURL=speed-tracker.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/speed-tracker.d.ts.map b/plugins/balance-hud/dist/speed-tracker.d.ts.map new file mode 100644 index 0000000..839e198 --- /dev/null +++ b/plugins/balance-hud/dist/speed-tracker.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"speed-tracker.d.ts","sourceRoot":"","sources":["../src/speed-tracker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA8B5C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB,CAAC;AAwJF,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,GAAE,OAAO,CAAC,gBAAgB,CAAM,GAAG,MAAM,GAAG,IAAI,CAsDzG"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/speed-tracker.js b/plugins/balance-hud/dist/speed-tracker.js new file mode 100644 index 0000000..b1ae3ee --- /dev/null +++ b/plugins/balance-hud/dist/speed-tracker.js @@ -0,0 +1,206 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { createHash } from 'node:crypto'; +import { getHudPluginDir } from './claude-config-dir.js'; +import { createDebug } from './debug.js'; +const debug = createDebug('speed-tracker'); +const SPEED_WINDOW_MS = 2000; +// Status lines can re-render many times per second while tokens stream. +// Computing a rate from sub-500ms windows amplifies noise and produces +// spurious multi-thousand tok/s readings (see #481). Require at least +// half a second of elapsed time before reporting a speed. +const MIN_DELTA_MS = 500; +const CACHE_DIRNAME = 'speed-cache'; +const LEGACY_CACHE_FILENAME = '.speed-cache.json'; +// Fallback: approximate bytes-per-token ratio for transcript file growth estimation. +// Claude's JSONL transcript is mostly ASCII with some overhead; ~4 bytes/token is a +// reasonable ballpark that avoids wild over/under-estimates. +const BYTES_PER_TOKEN = 4; +const defaultDeps = { + homeDir: () => os.homedir(), + now: () => Date.now(), +}; +// Scope the cache by a sha256 of the resolved transcript path so that +// concurrent Claude Code sessions never share or overwrite each other's +// cached output-token counters. Sharing the cache across sessions +// produced bogus speed readings on idle terminals whenever another +// terminal was actively streaming (see #495). +function getCachePath(homeDir, transcriptPath) { + const hash = createHash('sha256').update(path.resolve(transcriptPath)).digest('hex'); + return path.join(getHudPluginDir(homeDir), CACHE_DIRNAME, `${hash}.json`); +} +function readCache(homeDir, transcriptPath) { + try { + const cachePath = getCachePath(homeDir, transcriptPath); + if (!fs.existsSync(cachePath)) + return null; + const content = fs.readFileSync(cachePath, 'utf8'); + const parsed = JSON.parse(content); + if (typeof parsed.outputTokens !== 'number' || typeof parsed.timestamp !== 'number') { + return null; + } + return parsed; + } + catch (err) { + debug('Failed to read speed cache:', err instanceof Error ? err.message : err); + return null; + } +} +function ensurePrivateDir(dir) { + fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); + try { + fs.chmodSync(dir, 0o700); + } + catch { + // Best-effort: some filesystems do not support POSIX modes. + } +} +function writeCache(homeDir, transcriptPath, cache) { + try { + const cachePath = getCachePath(homeDir, transcriptPath); + const cacheDir = path.dirname(cachePath); + ensurePrivateDir(cacheDir); + fs.writeFileSync(cachePath, JSON.stringify(cache), { encoding: 'utf8', mode: 0o600 }); + try { + fs.chmodSync(cachePath, 0o600); + } + catch { + // Best-effort: cache permissions should not break speed tracking. + } + } + catch (err) { + debug('Failed to write speed cache:', err instanceof Error ? err.message : err); + } +} +function readFileSizeCache(cachePath) { + try { + if (!fs.existsSync(cachePath)) + return null; + const parsed = JSON.parse(fs.readFileSync(cachePath, 'utf8')); + if (typeof parsed.fileSize !== 'number' + || !Number.isFinite(parsed.fileSize) + || typeof parsed.timestamp !== 'number' + || !Number.isFinite(parsed.timestamp)) { + return null; + } + return parsed; + } + catch (err) { + debug('Failed to read file size cache:', err instanceof Error ? err.message : err); + return null; + } +} +function writeFileSizeCache(cachePath, cache) { + try { + const cacheDir = path.dirname(cachePath); + ensurePrivateDir(cacheDir); + fs.writeFileSync(cachePath, JSON.stringify(cache), { encoding: 'utf8', mode: 0o600 }); + try { + fs.chmodSync(cachePath, 0o600); + } + catch { + // Best-effort: cache permissions should not break speed tracking. + } + } + catch (err) { + debug('Failed to write file size cache:', err instanceof Error ? err.message : err); + } +} +// Remove the pre-0.x global cache file once, if present. It has no owner +// session so leaving it around only wastes disk. +function removeLegacyCache(homeDir) { + try { + const legacyPath = path.join(getHudPluginDir(homeDir), LEGACY_CACHE_FILENAME); + if (fs.existsSync(legacyPath)) { + fs.unlinkSync(legacyPath); + } + } + catch (err) { + debug('Failed to remove legacy cache:', err instanceof Error ? err.message : err); + } +} +/** + * Fallback speed estimation when output_tokens is unavailable. + * + * Measures the transcript file's byte-size growth between successive + * render calls and converts the delta to an approximate token/s rate + * using the BYTES_PER_TOKEN heuristic. This allows the HUD to show a + * speed reading even when the model provider (e.g. a non-standard + * proxy) does not populate `context_window.current_usage.output_tokens`. + */ +function getTranscriptSpeed(transcriptPath, homeDir, now) { + try { + const stat = fs.statSync(transcriptPath); + if (!stat.isFile()) + return null; + const canonicalTranscriptPath = fs.realpathSync(transcriptPath); + const fileSize = stat.size; + const cachePath = getCachePath(homeDir, canonicalTranscriptPath) + '.fs'; + const prev = readFileSizeCache(cachePath); + if (!prev) { + writeFileSizeCache(cachePath, { fileSize, timestamp: now }); + return null; + } + const deltaBytes = fileSize - prev.fileSize; + const deltaMs = now - prev.timestamp; + if (deltaMs > SPEED_WINDOW_MS || deltaMs < MIN_DELTA_MS || deltaBytes <= 0) { + if (deltaMs >= MIN_DELTA_MS) { + writeFileSizeCache(cachePath, { fileSize, timestamp: now }); + } + return null; + } + const estimatedTokens = deltaBytes / BYTES_PER_TOKEN; + writeFileSizeCache(cachePath, { fileSize, timestamp: now }); + return estimatedTokens / (deltaMs / 1000); + } + catch (err) { + debug('Failed to compute transcript speed:', err instanceof Error ? err.message : err); + return null; + } +} +export function getOutputSpeed(stdin, overrides = {}) { + const transcriptPath = stdin.transcript_path?.trim(); + if (!transcriptPath) { + // Without a stable session key we cannot safely isolate cache entries + // across concurrent Claude Code sessions, so skip speed tracking. + return null; + } + const deps = { ...defaultDeps, ...overrides }; + const now = deps.now(); + const homeDir = deps.homeDir(); + removeLegacyCache(homeDir); + // Primary: use output_tokens when the provider supplies it. + const outputTokens = stdin.context_window?.current_usage?.output_tokens; + if (typeof outputTokens === 'number' && Number.isFinite(outputTokens)) { + const previous = readCache(homeDir, transcriptPath); + if (!previous) { + writeCache(homeDir, transcriptPath, { outputTokens, timestamp: now }); + return null; + } + if (outputTokens < previous.outputTokens) { + writeCache(homeDir, transcriptPath, { outputTokens, timestamp: now }); + return null; + } + const deltaTokens = outputTokens - previous.outputTokens; + const deltaMs = now - previous.timestamp; + if (deltaMs > SPEED_WINDOW_MS) { + writeCache(homeDir, transcriptPath, { outputTokens, timestamp: now }); + return null; + } + if (deltaTokens <= 0) { + writeCache(homeDir, transcriptPath, { outputTokens, timestamp: now }); + return null; + } + if (deltaMs < MIN_DELTA_MS) { + return null; + } + const speed = deltaTokens / (deltaMs / 1000); + writeCache(homeDir, transcriptPath, { outputTokens, timestamp: now }); + return speed; + } + // Fallback: estimate from transcript file byte-size growth when the + // provider does not expose output_tokens (e.g. non-standard proxies). + return getTranscriptSpeed(transcriptPath, homeDir, now); +} +//# sourceMappingURL=speed-tracker.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/speed-tracker.js.map b/plugins/balance-hud/dist/speed-tracker.js.map new file mode 100644 index 0000000..174a4db --- /dev/null +++ b/plugins/balance-hud/dist/speed-tracker.js.map @@ -0,0 +1 @@ +{"version":3,"file":"speed-tracker.js","sourceRoot":"","sources":["../src/speed-tracker.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/stdin.d.ts b/plugins/balance-hud/dist/stdin.d.ts new file mode 100644 index 0000000..f985580 --- /dev/null +++ b/plugins/balance-hud/dist/stdin.d.ts @@ -0,0 +1,42 @@ +import type { StdinData, UsageData } from './types.js'; +import type { ModelFormatMode } from './config.js'; +type StdinStream = Pick & { + isTTY?: boolean; +}; +type ReadStdinOptions = { + firstByteTimeoutMs?: number; + idleTimeoutMs?: number; + maxBytes?: number; +}; +export declare function readStdin(stream?: StdinStream, options?: ReadStdinOptions): Promise; +export declare function getTotalTokens(stdin: StdinData): number; +export declare function getContextPercent(stdin: StdinData, autoCompactWindow?: number | null): number; +export declare function getBufferedPercent(stdin: StdinData, autoCompactWindow?: number | null): number; +export declare function getModelName(stdin: StdinData): string; +export declare function isBedrockModelId(modelId?: string): boolean; +export declare function isVertexModelId(modelId?: string): boolean; +export declare function isEnterpriseModelId(modelId?: string): boolean; +export declare function getProviderLabel(stdin: StdinData): string | null; +export declare function shouldHideUsage(stdin: StdinData): boolean; +export declare function getUsageFromStdin(stdin: StdinData): UsageData | null; +/** + * Strips redundant context-window size suffixes from model display names. + * + * Claude Code may include the context window size in the display name + * (e.g. "Opus 4.6 (1M context)"), but the HUD already shows context + * usage via the context bar — so the parenthetical is redundant. + */ +export declare function stripContextSuffix(name: string): string; +/** + * Formats a model name according to the user's chosen display settings. + * + * When `override` is set, it replaces the model name entirely. + * Otherwise, `format` controls how the raw name is abbreviated: + * + * full: Return raw name unchanged (e.g. "Opus 4.6 (1M context)") + * compact: Strip context-window suffix (e.g. "Opus 4.6") + * short: Strip context suffix AND leading "Claude " prefix (e.g. "Opus 4.6") + */ +export declare function formatModelName(name: string, format?: ModelFormatMode, override?: string): string; +export {}; +//# sourceMappingURL=stdin.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/stdin.d.ts.map b/plugins/balance-hud/dist/stdin.d.ts.map new file mode 100644 index 0000000..2f4d2bb --- /dev/null +++ b/plugins/balance-hud/dist/stdin.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"stdin.d.ts","sourceRoot":"","sources":["../src/stdin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAMnD,KAAK,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,CAAC,GAAG;IACnF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAMF,wBAAsB,SAAS,CAC7B,MAAM,GAAE,WAA2B,EACnC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAiH3B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAOvD;AAoBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAoB7F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CA8B9F;AASD,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAmBrD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAM1D;AAGD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAKzD;AAID,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAK7D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAWhE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAEzD;AAkBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAkBpE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBjG"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/stdin.js b/plugins/balance-hud/dist/stdin.js new file mode 100644 index 0000000..ec0ff0e --- /dev/null +++ b/plugins/balance-hud/dist/stdin.js @@ -0,0 +1,337 @@ +import { AUTOCOMPACT_BUFFER_PERCENT } from './constants.js'; +import { createDebug } from './debug.js'; +const debug = createDebug('stdin'); +const DEFAULT_FIRST_BYTE_TIMEOUT_MS = 250; +const DEFAULT_IDLE_TIMEOUT_MS = 30; +const DEFAULT_MAX_STDIN_BYTES = 256 * 1024; +export async function readStdin(stream = process.stdin, options = {}) { + if (stream.isTTY) { + return null; + } + const firstByteTimeoutMs = options.firstByteTimeoutMs ?? DEFAULT_FIRST_BYTE_TIMEOUT_MS; + const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS; + const maxBytes = options.maxBytes ?? DEFAULT_MAX_STDIN_BYTES; + try { + stream.setEncoding('utf8'); + } + catch (err) { + debug('Failed to set stream encoding:', err); + return null; + } + return await new Promise((resolve) => { + let raw = ''; + let settled = false; + let sawData = false; + let firstByteTimer; + let idleTimer; + const cleanup = () => { + if (firstByteTimer) { + clearTimeout(firstByteTimer); + firstByteTimer = undefined; + } + if (idleTimer) { + clearTimeout(idleTimer); + idleTimer = undefined; + } + stream.off('data', onData); + stream.off('end', onEnd); + stream.off('error', onError); + stream.pause(); + }; + const finish = (value) => { + if (settled) { + return; + } + settled = true; + cleanup(); + resolve(value); + }; + const tryParse = () => { + const trimmed = raw.trim(); + if (!trimmed) { + return null; + } + try { + return JSON.parse(trimmed); + } + catch (err) { + debug('JSON parse incomplete/invalid, waiting for more data'); + return undefined; + } + }; + const scheduleIdleParse = () => { + if (idleTimer) { + clearTimeout(idleTimer); + } + idleTimer = setTimeout(() => { + const parsed = tryParse(); + finish(parsed ?? null); + }, idleTimeoutMs); + }; + const onData = (chunk) => { + sawData = true; + if (firstByteTimer) { + clearTimeout(firstByteTimer); + firstByteTimer = undefined; + } + raw += String(chunk); + if (Buffer.byteLength(raw, 'utf8') > maxBytes) { + finish(null); + return; + } + const parsed = tryParse(); + if (parsed !== undefined) { + finish(parsed); + return; + } + scheduleIdleParse(); + }; + const onEnd = () => { + const parsed = tryParse(); + finish(parsed ?? null); + }; + const onError = (err) => { + debug('stdin stream error:', err); + finish(null); + }; + firstByteTimer = setTimeout(() => { + if (!sawData) { + finish(null); + } + }, firstByteTimeoutMs); + stream.on('data', onData); + stream.on('end', onEnd); + stream.on('error', onError); + }); +} +export function getTotalTokens(stdin) { + const usage = stdin.context_window?.current_usage; + return ((usage?.input_tokens ?? 0) + + (usage?.cache_creation_input_tokens ?? 0) + + (usage?.cache_read_input_tokens ?? 0)); +} +/** + * Get native percentage from Claude Code v2.1.6+ if available. + * Returns null if not available or invalid, triggering fallback to manual calculation. + * + * A value of 0 is treated as "not yet populated": on a fresh session Claude Code + * may emit used_percentage=0 before the first API response arrives, while + * current_usage already contains the real initial-context tokens (system prompt, + * tools, memory files, etc.). Falling through to the token-based calculation + * ensures those tokens are reflected in the context bar from the very first tick. + */ +function getNativePercent(stdin) { + const nativePercent = stdin.context_window?.used_percentage; + if (typeof nativePercent === 'number' && !Number.isNaN(nativePercent) && nativePercent > 0) { + return Math.min(100, Math.max(0, Math.round(nativePercent))); + } + return null; +} +export function getContextPercent(stdin, autoCompactWindow) { + if (typeof autoCompactWindow === 'number' && autoCompactWindow > 0) { + const totalTokens = getTotalTokens(stdin); + return Math.min(100, Math.round((totalTokens / autoCompactWindow) * 100)); + } + // Prefer native percentage (v2.1.6+) - accurate and matches /context + const native = getNativePercent(stdin); + if (native !== null) { + return native; + } + // Fallback: manual calculation without buffer + const size = stdin.context_window?.context_window_size; + if (!size || size <= 0) { + return 0; + } + const totalTokens = getTotalTokens(stdin); + return Math.min(100, Math.round((totalTokens / size) * 100)); +} +export function getBufferedPercent(stdin, autoCompactWindow) { + if (typeof autoCompactWindow === 'number' && autoCompactWindow > 0) { + const totalTokens = getTotalTokens(stdin); + return Math.min(100, Math.round((totalTokens / autoCompactWindow) * 100)); + } + // Prefer native percentage (v2.1.6+) so the HUD matches Claude Code's + // own context output. The buffered fallback only approximates older versions. + const native = getNativePercent(stdin); + if (native !== null) { + return native; + } + // Fallback: manual calculation with buffer for older Claude Code versions + const size = stdin.context_window?.context_window_size; + if (!size || size <= 0) { + return 0; + } + const totalTokens = getTotalTokens(stdin); + // Scale buffer by raw usage: no buffer at ≤5% (e.g. after /clear), + // full buffer at ≥50%. Autocompact doesn't kick in at very low usage. + const rawRatio = totalTokens / size; + const LOW = 0.05; + const HIGH = 0.50; + const scale = Math.min(1, Math.max(0, (rawRatio - LOW) / (HIGH - LOW))); + const buffer = size * AUTOCOMPACT_BUFFER_PERCENT * scale; + return Math.min(100, Math.round(((totalTokens + buffer) / size) * 100)); +} +// Enterprise plan alias → human-readable display name +const ENTERPRISE_ALIAS_LABELS = { + opusplan: 'Claude Opus', + sonnetplan: 'Claude Sonnet', + haikuplan: 'Claude Haiku', +}; +export function getModelName(stdin) { + const displayName = stdin.model?.display_name?.trim(); + if (displayName) { + return displayName; + } + const modelId = stdin.model?.id?.trim(); + if (!modelId) { + return 'Unknown'; + } + // Resolve enterprise plan aliases to readable labels + const enterpriseLabel = ENTERPRISE_ALIAS_LABELS[modelId.toLowerCase()]; + if (enterpriseLabel) { + return enterpriseLabel; + } + const normalizedBedrockLabel = normalizeBedrockModelLabel(modelId); + return normalizedBedrockLabel ?? modelId; +} +export function isBedrockModelId(modelId) { + if (!modelId) { + return false; + } + const normalized = modelId.toLowerCase(); + return normalized.includes('anthropic.claude-'); +} +// Vertex AI model IDs use '@' as version separator (e.g. claude-3-5-sonnet@20241022) +export function isVertexModelId(modelId) { + if (!modelId) { + return false; + } + return modelId.includes('@'); +} +const ENTERPRISE_MODEL_IDS = new Set(['opusplan', 'sonnetplan', 'haikuplan']); +export function isEnterpriseModelId(modelId) { + if (!modelId) { + return false; + } + return ENTERPRISE_MODEL_IDS.has(modelId.toLowerCase()); +} +export function getProviderLabel(stdin) { + if (process.env.CLAUDE_CODE_USE_BEDROCK === '1') { + return 'Bedrock'; + } + if (process.env.CLAUDE_CODE_USE_VERTEX === '1') { + return 'Vertex'; + } + if (isEnterpriseModelId(stdin.model?.id)) { + return 'Enterprise'; + } + return null; +} +export function shouldHideUsage(stdin) { + return getProviderLabel(stdin) === 'Bedrock' || isBedrockModelId(stdin.model?.id); +} +function parseRateLimitPercent(value) { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return null; + } + return Math.round(Math.min(100, Math.max(0, value))); +} +function parseRateLimitResetAt(value) { + if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) { + return null; + } + return new Date(value * 1000); +} +export function getUsageFromStdin(stdin) { + const rateLimits = stdin.rate_limits; + if (!rateLimits) { + return null; + } + const fiveHour = parseRateLimitPercent(rateLimits.five_hour?.used_percentage); + const sevenDay = parseRateLimitPercent(rateLimits.seven_day?.used_percentage); + if (fiveHour === null && sevenDay === null) { + return null; + } + return { + fiveHour, + sevenDay, + fiveHourResetAt: parseRateLimitResetAt(rateLimits.five_hour?.resets_at), + sevenDayResetAt: parseRateLimitResetAt(rateLimits.seven_day?.resets_at), + }; +} +/** + * Strips redundant context-window size suffixes from model display names. + * + * Claude Code may include the context window size in the display name + * (e.g. "Opus 4.6 (1M context)"), but the HUD already shows context + * usage via the context bar — so the parenthetical is redundant. + */ +export function stripContextSuffix(name) { + return name.replace(/\s*\([^)]*\bcontext\b[^)]*\)/i, '').trim(); +} +/** + * Formats a model name according to the user's chosen display settings. + * + * When `override` is set, it replaces the model name entirely. + * Otherwise, `format` controls how the raw name is abbreviated: + * + * full: Return raw name unchanged (e.g. "Opus 4.6 (1M context)") + * compact: Strip context-window suffix (e.g. "Opus 4.6") + * short: Strip context suffix AND leading "Claude " prefix (e.g. "Opus 4.6") + */ +export function formatModelName(name, format, override) { + if (override) { + return override; + } + if (!format || format === 'full') { + return name; + } + let result = stripContextSuffix(name); + if (format === 'short') { + result = result.replace(/^Claude\s+/i, ''); + } + return result; +} +function normalizeBedrockModelLabel(modelId) { + if (!isBedrockModelId(modelId)) { + return null; + } + const lowercaseId = modelId.toLowerCase(); + const claudePrefix = 'anthropic.claude-'; + const claudeIndex = lowercaseId.indexOf(claudePrefix); + if (claudeIndex === -1) { + return null; + } + let suffix = lowercaseId.slice(claudeIndex + claudePrefix.length); + suffix = suffix.replace(/-v\d+:\d+$/, ''); + suffix = suffix.replace(/-\d{8}$/, ''); + const tokens = suffix.split('-').filter(Boolean); + if (tokens.length === 0) { + return null; + } + const familyIndex = tokens.findIndex((token) => token === 'haiku' || token === 'sonnet' || token === 'opus'); + if (familyIndex === -1) { + return null; + } + const family = tokens[familyIndex]; + const beforeVersion = readNumericVersion(tokens, familyIndex - 1, -1).reverse(); + const afterVersion = readNumericVersion(tokens, familyIndex + 1, 1); + const versionParts = beforeVersion.length >= afterVersion.length ? beforeVersion : afterVersion; + const version = versionParts.length ? versionParts.join('.') : null; + const familyLabel = family[0].toUpperCase() + family.slice(1); + return version ? `Claude ${familyLabel} ${version}` : `Claude ${familyLabel}`; +} +function readNumericVersion(tokens, startIndex, step) { + const parts = []; + for (let i = startIndex; i >= 0 && i < tokens.length; i += step) { + if (!/^\d+$/.test(tokens[i])) { + break; + } + parts.push(tokens[i]); + if (parts.length === 2) { + break; + } + } + return parts; +} +//# sourceMappingURL=stdin.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/stdin.js.map b/plugins/balance-hud/dist/stdin.js.map new file mode 100644 index 0000000..15377e9 --- /dev/null +++ b/plugins/balance-hud/dist/stdin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"stdin.js","sourceRoot":"","sources":["../src/stdin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAYnC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAC1C,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,MAAM,uBAAuB,GAAG,GAAG,GAAG,IAAI,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,SAAsB,OAAO,CAAC,KAAK,EACnC,UAA4B,EAAE;IAE9B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,IAAI,6BAA6B,CAAC;IACvF,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAC;IACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,uBAAuB,CAAC;IAE7D,IAAI,CAAC;QACH,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,MAAM,IAAI,OAAO,CAAmB,CAAC,OAAO,EAAE,EAAE;QACrD,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,cAAyD,CAAC;QAC9D,IAAI,SAAoD,CAAC;QAEzD,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,IAAI,cAAc,EAAE,CAAC;gBACnB,YAAY,CAAC,cAAc,CAAC,CAAC;gBAC7B,cAAc,GAAG,SAAS,CAAC;YAC7B,CAAC;YACD,IAAI,SAAS,EAAE,CAAC;gBACd,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,SAAS,GAAG,SAAS,CAAC;YACxB,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7B,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,CAAC,KAAuB,EAAQ,EAAE;YAC/C,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC;QAEF,MAAM,QAAQ,GAAG,GAAiC,EAAE;YAClD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAC;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,sDAAsD,CAAC,CAAC;gBAC9D,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,iBAAiB,GAAG,GAAS,EAAE;YACnC,IAAI,SAAS,EAAE,CAAC;gBACd,YAAY,CAAC,SAAS,CAAC,CAAC;YAC1B,CAAC;YACD,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC1B,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;gBAC1B,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;YACzB,CAAC,EAAE,aAAa,CAAC,CAAC;QACpB,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAQ,EAAE;YAC9C,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,cAAc,EAAE,CAAC;gBACnB,YAAY,CAAC,cAAc,CAAC,CAAC;gBAC7B,cAAc,GAAG,SAAS,CAAC;YAC7B,CAAC;YAED,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;gBAC9C,MAAM,CAAC,IAAI,CAAC,CAAC;gBACb,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;YAC1B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,CAAC,MAAM,CAAC,CAAC;gBACf,OAAO;YACT,CAAC;YAED,iBAAiB,EAAE,CAAC;QACtB,CAAC,CAAC;QAEF,MAAM,KAAK,GAAG,GAAS,EAAE;YACvB,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;YAC1B,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;QACzB,CAAC,CAAC;QAEF,MAAM,OAAO,GAAG,CAAC,GAAU,EAAQ,EAAE;YACnC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC;QAEF,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,CAAC;YACf,CAAC;QACH,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAEvB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACxB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,EAAE,aAAa,CAAC;IAClD,OAAO,CACL,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC;QAC1B,CAAC,KAAK,EAAE,2BAA2B,IAAI,CAAC,CAAC;QACzC,CAAC,KAAK,EAAE,uBAAuB,IAAI,CAAC,CAAC,CACtC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,gBAAgB,CAAC,KAAgB;IACxC,MAAM,aAAa,GAAG,KAAK,CAAC,cAAc,EAAE,eAAe,CAAC;IAC5D,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QAC3F,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAgB,EAAE,iBAAiC;IACnF,IAAI,OAAO,iBAAiB,KAAK,QAAQ,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;QACnE,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,iBAAiB,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,qEAAqE;IACrE,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,8CAA8C;IAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,EAAE,mBAAmB,CAAC;IACvD,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAgB,EAAE,iBAAiC;IACpF,IAAI,OAAO,iBAAiB,KAAK,QAAQ,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;QACnE,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,iBAAiB,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,sEAAsE;IACtE,8EAA8E;IAC9E,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,0EAA0E;IAC1E,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,EAAE,mBAAmB,CAAC;IACvD,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAE1C,mEAAmE;IACnE,sEAAsE;IACtE,MAAM,QAAQ,GAAG,WAAW,GAAG,IAAI,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC;IACjB,MAAM,IAAI,GAAG,IAAI,CAAC;IAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,IAAI,GAAG,0BAA0B,GAAG,KAAK,CAAC;IAEzD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,sDAAsD;AACtD,MAAM,uBAAuB,GAA2B;IACtD,QAAQ,EAAE,aAAa;IACvB,UAAU,EAAE,eAAe;IAC3B,SAAS,EAAE,cAAc;CAC1B,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAgB;IAC3C,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;IACtD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,qDAAqD;IACrD,MAAM,eAAe,GAAG,uBAAuB,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACvE,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,MAAM,sBAAsB,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAC;IACnE,OAAO,sBAAsB,IAAI,OAAO,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AAClD,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;AAE9E,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAgB;IAC/C,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,GAAG,EAAE,CAAC;QAC/C,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;QACzC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAgB;IAC9C,OAAO,gBAAgB,CAAC,KAAK,CAAC,KAAK,SAAS,IAAI,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAgC;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAgC;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAgB;IAChD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC;IACrC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,qBAAqB,CAAC,UAAU,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAC9E,MAAM,QAAQ,GAAG,qBAAqB,CAAC,UAAU,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAC9E,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,QAAQ;QACR,QAAQ;QACR,eAAe,EAAE,qBAAqB,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC;QACvE,eAAe,EAAE,qBAAqB,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC;KACxE,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,OAAO,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,MAAwB,EAAE,QAAiB;IACvF,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACvB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,0BAA0B,CAAC,OAAe;IACjD,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAC1C,MAAM,YAAY,GAAG,mBAAmB,CAAC;IACzC,MAAM,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACtD,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAEvC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC;IAC7G,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACnC,MAAM,aAAa,GAAG,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAChF,MAAM,YAAY,GAAG,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC;IAChG,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE9D,OAAO,OAAO,CAAC,CAAC,CAAC,UAAU,WAAW,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,UAAU,WAAW,EAAE,CAAC;AAChF,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAgB,EAAE,UAAkB,EAAE,IAAY;IAC5E,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAChE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B,MAAM;QACR,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/transcript.d.ts b/plugins/balance-hud/dist/transcript.d.ts new file mode 100644 index 0000000..7e34240 --- /dev/null +++ b/plugins/balance-hud/dist/transcript.d.ts @@ -0,0 +1,5 @@ +import * as fs from 'node:fs'; +import type { TranscriptData } from './types.js'; +export declare function parseTranscript(transcriptPath: string): Promise; +export declare function _setCreateReadStreamForTests(impl: typeof fs.createReadStream | null): void; +//# sourceMappingURL=transcript.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/transcript.d.ts.map b/plugins/balance-hud/dist/transcript.d.ts.map new file mode 100644 index 0000000..4e421a7 --- /dev/null +++ b/plugins/balance-hud/dist/transcript.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAO9B,OAAO,KAAK,EAAE,cAAc,EAAsD,MAAM,YAAY,CAAC;AAgSrG,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA6KrF;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAE1F"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/transcript.js b/plugins/balance-hud/dist/transcript.js new file mode 100644 index 0000000..ae58e0e --- /dev/null +++ b/plugins/balance-hud/dist/transcript.js @@ -0,0 +1,567 @@ +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import * as readline from 'node:readline'; +import { createHash } from 'node:crypto'; +import { getHudPluginDir } from './claude-config-dir.js'; +import { createDebug } from './debug.js'; +import { sanitizeDisplayText } from './utils/sanitize.js'; +const debug = createDebug('transcript'); +const TRANSCRIPT_CACHE_VERSION = 9; +const MCP_TOOL_NAME_PATTERN = /^mcp__(.+?)__(.+)$/; +const ACTIVITY_NAME_MAX_LEN = 64; +// Hard cap on the advisor model ID captured from the transcript. Real Claude +// model IDs (e.g. "claude-haiku-4-5-20251001") fit comfortably under this; the +// cap exists to prevent a malformed transcript from persisting an oversized +// string through the JSON cache and onto every statusline refresh. +const ADVISOR_MODEL_MAX_LEN = 64; +let createReadStreamImpl = fs.createReadStream; +function normalizeTokenCount(value) { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return 0; + } + return Math.max(0, Math.trunc(value)); +} +function normalizeSessionTokens(tokens) { + if (!tokens || typeof tokens !== 'object') { + return undefined; + } + const raw = tokens; + return { + inputTokens: normalizeTokenCount(raw.inputTokens), + outputTokens: normalizeTokenCount(raw.outputTokens), + cacheCreationTokens: normalizeTokenCount(raw.cacheCreationTokens), + cacheReadTokens: normalizeTokenCount(raw.cacheReadTokens), + }; +} +function normalizeNameList(value) { + if (!Array.isArray(value)) { + return []; + } + const seen = new Set(); + const names = []; + for (const item of value) { + const name = normalizeActivityName(item); + if (!name || seen.has(name)) { + continue; + } + seen.add(name); + names.push(name); + } + return names; +} +function normalizeActivityName(value) { + if (typeof value !== 'string') { + return undefined; + } + const sanitized = sanitizeDisplayText(value).trim(); + if (!sanitized) { + return undefined; + } + if (sanitized.length <= ACTIVITY_NAME_MAX_LEN) { + return sanitized; + } + return `${sanitized.slice(0, ACTIVITY_NAME_MAX_LEN - 1)}…`; +} +function getTranscriptCachePath(transcriptPath, homeDir) { + const hash = createHash('sha256').update(path.resolve(transcriptPath)).digest('hex'); + return path.join(getHudPluginDir(homeDir), 'transcript-cache', `${hash}.json`); +} +function canonicalizeTranscriptPath(transcriptPath) { + try { + return fs.realpathSync(transcriptPath); + } + catch (err) { + debug('Failed to resolve transcript path %s:', transcriptPath, err instanceof Error ? err.message : err); + return null; + } +} +function readTranscriptFileState(transcriptPath) { + try { + const stat = fs.statSync(transcriptPath); + if (!stat.isFile()) { + debug('Transcript path is not a file: %s', transcriptPath); + return null; + } + return { + mtimeMs: stat.mtimeMs, + size: stat.size, + }; + } + catch (err) { + debug('Failed to stat transcript file %s:', transcriptPath, err instanceof Error ? err.message : err); + return null; + } +} +function serializeTranscriptData(data) { + return { + tools: data.tools.map((tool) => ({ + ...tool, + startTime: tool.startTime.toISOString(), + endTime: tool.endTime?.toISOString(), + })), + skills: [...data.skills], + mcpServers: [...data.mcpServers], + agents: data.agents.map((agent) => ({ + ...agent, + startTime: agent.startTime.toISOString(), + endTime: agent.endTime?.toISOString(), + })), + todos: data.todos.map((todo) => ({ ...todo })), + sessionStart: data.sessionStart?.toISOString(), + sessionName: data.sessionName, + lastAssistantResponseAt: data.lastAssistantResponseAt?.toISOString(), + sessionTokens: data.sessionTokens, + lastCompactBoundaryAt: data.lastCompactBoundaryAt?.toISOString(), + lastCompactPostTokens: data.lastCompactPostTokens, + compactionCount: data.compactionCount, + advisorModel: data.advisorModel, + }; +} +function deserializeTranscriptData(data) { + return { + tools: data.tools.map((tool) => ({ + ...tool, + startTime: new Date(tool.startTime), + endTime: tool.endTime ? new Date(tool.endTime) : undefined, + })), + skills: normalizeNameList(data.skills), + mcpServers: normalizeNameList(data.mcpServers), + agents: data.agents.map((agent) => ({ + ...agent, + startTime: new Date(agent.startTime), + endTime: agent.endTime ? new Date(agent.endTime) : undefined, + })), + todos: data.todos.map((todo) => ({ ...todo })), + sessionStart: data.sessionStart ? new Date(data.sessionStart) : undefined, + sessionName: data.sessionName, + lastAssistantResponseAt: data.lastAssistantResponseAt ? new Date(data.lastAssistantResponseAt) : undefined, + sessionTokens: normalizeSessionTokens(data.sessionTokens), + lastCompactBoundaryAt: data.lastCompactBoundaryAt ? new Date(data.lastCompactBoundaryAt) : undefined, + lastCompactPostTokens: typeof data.lastCompactPostTokens === 'number' ? data.lastCompactPostTokens : undefined, + compactionCount: typeof data.compactionCount === 'number' && Number.isFinite(data.compactionCount) && data.compactionCount >= 0 + ? Math.trunc(data.compactionCount) + : undefined, + advisorModel: typeof data.advisorModel === 'string' && data.advisorModel.length > 0 + ? data.advisorModel.slice(0, ADVISOR_MODEL_MAX_LEN) + : undefined, + }; +} +function readTranscriptCache(transcriptPath, state) { + try { + const cachePath = getTranscriptCachePath(transcriptPath, os.homedir()); + const raw = fs.readFileSync(cachePath, 'utf8'); + const parsed = JSON.parse(raw); + if (parsed.version !== TRANSCRIPT_CACHE_VERSION + || !parsed.data + || !parsed.transcriptPath + || parsed.transcriptPath !== path.resolve(transcriptPath) + || parsed.transcriptState?.mtimeMs !== state.mtimeMs + || parsed.transcriptState?.size !== state.size) { + return null; + } + return deserializeTranscriptData(parsed.data); + } + catch (err) { + debug('Failed to read transcript cache:', err instanceof Error ? err.message : err); + return null; + } +} +function writeTranscriptCache(transcriptPath, state, data) { + try { + const cachePath = getTranscriptCachePath(transcriptPath, os.homedir()); + const cacheDir = path.dirname(cachePath); + fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 }); + try { + fs.chmodSync(cacheDir, 0o700); + } + catch { + // Best-effort: some filesystems do not support POSIX modes. + } + const payload = { + version: TRANSCRIPT_CACHE_VERSION, + transcriptPath: path.resolve(transcriptPath), + transcriptState: state, + data: serializeTranscriptData(data), + }; + fs.writeFileSync(cachePath, JSON.stringify(payload), { encoding: 'utf8', mode: 0o600 }); + try { + fs.chmodSync(cachePath, 0o600); + } + catch { + // Best-effort: cache permissions should not break rendering. + } + } + catch (err) { + debug('Failed to write transcript cache:', err instanceof Error ? err.message : err); + } +} +export async function parseTranscript(transcriptPath) { + const result = { + tools: [], + skills: [], + mcpServers: [], + agents: [], + todos: [], + }; + if (!transcriptPath || !fs.existsSync(transcriptPath)) { + return result; + } + const canonicalTranscriptPath = canonicalizeTranscriptPath(transcriptPath); + if (!canonicalTranscriptPath) { + return result; + } + const transcriptState = readTranscriptFileState(canonicalTranscriptPath); + if (!transcriptState) { + return result; + } + const cached = readTranscriptCache(canonicalTranscriptPath, transcriptState); + if (cached) { + return cached; + } + const toolMap = new Map(); + const skillSet = new Set(); + const mcpServerSet = new Set(); + const agentMap = new Map(); + let latestTodos = []; + const taskIdToIndex = new Map(); + const queueCompletionMap = new Map(); + let latestSlug; + let customTitle; + let latestAdvisorModel; + let lastCompactBoundaryAt; + let lastCompactPostTokens; + let compactionCount = 0; + const sessionTokens = { + inputTokens: 0, + outputTokens: 0, + cacheCreationTokens: 0, + cacheReadTokens: 0, + }; + let lastUsageKey; + let parsedCleanly = false; + try { + const fileStream = createReadStreamImpl(canonicalTranscriptPath); + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity, + }); + for await (const line of rl) { + if (!line.trim()) { + lastUsageKey = undefined; + continue; + } + try { + const entry = JSON.parse(line); + if (entry.type === 'custom-title' && typeof entry.customTitle === 'string') { + customTitle = entry.customTitle; + } + else if (typeof entry.slug === 'string') { + latestSlug = entry.slug; + } + // Capture the advisor model from the top-level `advisorModel` field. + // Claude Code stamps this onto every *assistant* record after `/advisor` + // is set, so we restrict to that record type (matching the documented + // source) and the most recent occurrence reflects the current choice. + // Length is hard-capped so a malformed transcript cannot persist an + // unbounded value through the cache layer. + if (entry.type === 'assistant' + && typeof entry.advisorModel === 'string' + && entry.advisorModel.length > 0) { + latestAdvisorModel = entry.advisorModel.slice(0, ADVISOR_MODEL_MAX_LEN); + } + // Accumulate token usage from assistant messages. + // Claude Code can write the same API response to the transcript 2-3 times + // consecutively (dual-logging). Skip consecutive duplicates to avoid inflating counts. + if (entry.type === 'assistant' && entry.message?.usage) { + const usage = entry.message.usage; + const key = `${usage.input_tokens}|${usage.output_tokens}|${usage.cache_creation_input_tokens}|${usage.cache_read_input_tokens}`; + if (key !== lastUsageKey) { + sessionTokens.inputTokens += normalizeTokenCount(usage.input_tokens); + sessionTokens.outputTokens += normalizeTokenCount(usage.output_tokens); + sessionTokens.cacheCreationTokens += normalizeTokenCount(usage.cache_creation_input_tokens); + sessionTokens.cacheReadTokens += normalizeTokenCount(usage.cache_read_input_tokens); + } + lastUsageKey = key; + } + else { + lastUsageKey = undefined; + } + // Track Claude Code's compact_boundary marker. Both manual (/compact) + // and auto compaction emit this system entry with compactMetadata; we + // take the most recent one's timestamp so callers can distinguish a + // legitimate post-compact zero frame from a transient stdin glitch. + if (entry.type === 'system' && entry.subtype === 'compact_boundary') { + const ts = entry.timestamp ? new Date(entry.timestamp) : null; + if (ts && !Number.isNaN(ts.getTime())) { + compactionCount += 1; + if (!lastCompactBoundaryAt || ts.getTime() > lastCompactBoundaryAt.getTime()) { + lastCompactBoundaryAt = ts; + const post = entry.compactMetadata?.postTokens; + lastCompactPostTokens = typeof post === 'number' && Number.isFinite(post) && post >= 0 + ? Math.trunc(post) + : undefined; + } + } + } + // Capture accurate background-agent completion timestamps from queue-operation entries. + // The tool_result timestamp in the parent transcript is written at launch time, not + // when the agent actually finishes, so we override with the enqueue timestamp. + if (entry.type === 'queue-operation' && entry.operation === 'enqueue' && entry.content) { + const taskIdMatch = entry.content.match(/([^<]+)<\/task-id>/); + const toolUseIdMatch = entry.content.match(/([^<]+)<\/tool-use-id>/); + if (taskIdMatch && toolUseIdMatch && entry.timestamp) { + const ts = new Date(entry.timestamp); + if (!Number.isNaN(ts.getTime())) { + queueCompletionMap.set(toolUseIdMatch[1], ts); + } + } + } + processEntry(entry, toolMap, skillSet, mcpServerSet, agentMap, taskIdToIndex, latestTodos, result); + } + catch (err) { + lastUsageKey = undefined; + debug('Skipping malformed transcript line:', err instanceof Error ? err.message : err); + } + } + parsedCleanly = true; + } + catch (err) { + debug('Transcript stream read error, returning partial results:', err instanceof Error ? err.message : err); + } + // Resolve agent completion: prefer queue-operation timestamps (accurate for + // background agents), fall back to tool_result timestamps (inline agents). + // Status is deferred so background agents show ◐ until they truly finish. + for (const [toolUseId, endTime] of queueCompletionMap) { + const agent = agentMap.get(toolUseId); + if (agent?.background) { + agent.endTime = endTime; + agent.status = 'completed'; + } + } + for (const agent of agentMap.values()) { + if (agent.status === 'running' && agent.endTime) { + agent.status = 'completed'; + } + } + result.tools = Array.from(toolMap.values()).slice(-20); + result.skills = Array.from(skillSet.values()); + result.mcpServers = Array.from(mcpServerSet.values()); + result.agents = Array.from(agentMap.values()).slice(-10); + result.todos = latestTodos; + result.sessionName = customTitle ?? latestSlug; + result.sessionTokens = sessionTokens; + result.lastCompactBoundaryAt = lastCompactBoundaryAt; + result.lastCompactPostTokens = lastCompactPostTokens; + result.compactionCount = compactionCount; + result.advisorModel = latestAdvisorModel; + if (parsedCleanly) { + writeTranscriptCache(canonicalTranscriptPath, transcriptState, result); + } + return result; +} +export function _setCreateReadStreamForTests(impl) { + createReadStreamImpl = impl ?? fs.createReadStream; +} +function processEntry(entry, toolMap, skillSet, mcpServerSet, agentMap, taskIdToIndex, latestTodos, result) { + const timestamp = entry.timestamp ? new Date(entry.timestamp) : new Date(); + const hasValidTimestamp = !Number.isNaN(timestamp.getTime()); + if (!result.sessionStart && entry.timestamp && hasValidTimestamp) { + result.sessionStart = timestamp; + } + if (entry.type === 'assistant' && entry.timestamp && hasValidTimestamp) { + result.lastAssistantResponseAt = timestamp; + } + const content = entry.message?.content; + if (!content || !Array.isArray(content)) + return; + for (const block of content) { + if (block.type === 'tool_use' && block.id && block.name) { + const skillName = block.name === 'Skill' + ? normalizeSkillName(block.input?.skill) + : undefined; + if (skillName) { + skillSet.add(skillName); + } + const mcpServerName = extractMcpServerName(block.name); + if (mcpServerName) { + mcpServerSet.add(mcpServerName); + } + const toolEntry = { + id: block.id, + name: block.name, + target: extractTarget(block.name, block.input), + status: 'running', + startTime: timestamp, + }; + if (block.name === 'Task' || block.name === 'Agent') { + const input = block.input; + const agentEntry = { + id: block.id, + type: input?.subagent_type ?? 'agent', + model: input?.model ?? undefined, + description: input?.description ?? undefined, + status: 'running', + startTime: timestamp, + background: input?.run_in_background === true, + }; + agentMap.set(block.id, agentEntry); + } + else if (block.name === 'TodoWrite') { + const input = block.input; + if (input?.todos && Array.isArray(input.todos)) { + // Build a FIFO queue of taskIds per content string, ordered by the + // old array position. Two todos that share the same content must + // each get their own taskId back after the rebuild, so we cannot + // collapse duplicates to one index. + const contentToTaskIds = new Map(); + const taskIdsByOldIndex = []; + for (const [taskId, idx] of taskIdToIndex) { + if (idx < latestTodos.length) { + taskIdsByOldIndex.push([idx, taskId]); + } + } + taskIdsByOldIndex.sort((a, b) => a[0] - b[0]); + for (const [idx, taskId] of taskIdsByOldIndex) { + const content = latestTodos[idx].content; + const ids = contentToTaskIds.get(content) ?? []; + ids.push(taskId); + contentToTaskIds.set(content, ids); + } + latestTodos.length = 0; + taskIdToIndex.clear(); + latestTodos.push(...input.todos); + // Consume one queued taskId per new todo that matches by content, + // so duplicate-content items still each get their own taskId. + for (let i = 0; i < latestTodos.length; i++) { + const ids = contentToTaskIds.get(latestTodos[i].content); + if (ids && ids.length > 0) { + const taskId = ids.shift(); + taskIdToIndex.set(taskId, i); + if (ids.length === 0) { + contentToTaskIds.delete(latestTodos[i].content); + } + } + } + } + } + else if (block.name === 'TaskCreate') { + const input = block.input; + const subject = typeof input?.subject === 'string' ? input.subject : ''; + const description = typeof input?.description === 'string' ? input.description : ''; + const content = subject || description || 'Untitled task'; + const status = normalizeTaskStatus(input?.status) ?? 'pending'; + latestTodos.push({ content, status }); + const rawTaskId = input?.taskId; + const taskId = typeof rawTaskId === 'string' || typeof rawTaskId === 'number' + ? String(rawTaskId) + : block.id; + if (taskId) { + taskIdToIndex.set(taskId, latestTodos.length - 1); + } + } + else if (block.name === 'TaskUpdate') { + const input = block.input; + const index = resolveTaskIndex(input?.taskId, taskIdToIndex, latestTodos); + if (index !== null) { + const status = normalizeTaskStatus(input?.status); + if (status) { + latestTodos[index].status = status; + } + const subject = typeof input?.subject === 'string' ? input.subject : ''; + const description = typeof input?.description === 'string' ? input.description : ''; + const content = subject || description; + if (content) { + latestTodos[index].content = content; + } + } + } + else { + toolMap.set(block.id, toolEntry); + } + } + if (block.type === 'tool_result' && block.tool_use_id) { + const tool = toolMap.get(block.tool_use_id); + if (tool) { + tool.status = block.is_error ? 'error' : 'completed'; + tool.endTime = timestamp; + } + const agent = agentMap.get(block.tool_use_id); + if (agent && !agent.background) { + agent.endTime = timestamp; + } + } + } +} +function extractTarget(toolName, input) { + if (!input) + return undefined; + switch (toolName) { + case 'Read': + case 'Write': + case 'Edit': + return input.file_path ?? input.path; + case 'Glob': + return input.pattern; + case 'Grep': + return input.pattern; + case 'Skill': + return normalizeSkillName(input.skill); + case 'Bash': + if (typeof input.command !== 'string') { + return undefined; + } + const cmd = input.command.replace(/\s+/g, ' ').trim(); + return cmd + ? cmd.length > 30 + ? `${cmd.slice(0, 30).trimEnd()}...` + : cmd + : undefined; + } + return undefined; +} +function normalizeSkillName(value) { + return normalizeActivityName(value); +} +function extractMcpServerName(toolName) { + const match = MCP_TOOL_NAME_PATTERN.exec(toolName); + if (!match) { + return undefined; + } + return normalizeActivityName(match[1]); +} +function resolveTaskIndex(taskId, taskIdToIndex, latestTodos) { + if (typeof taskId === 'string' || typeof taskId === 'number') { + const key = String(taskId); + const mapped = taskIdToIndex.get(key); + if (typeof mapped === 'number') { + return mapped; + } + if (/^\d+$/.test(key)) { + const numericIndex = Number.parseInt(key, 10) - 1; + if (numericIndex >= 0 && numericIndex < latestTodos.length) { + return numericIndex; + } + } + } + return null; +} +function normalizeTaskStatus(status) { + if (typeof status !== 'string') + return null; + switch (status) { + case 'pending': + case 'not_started': + return 'pending'; + case 'in_progress': + case 'running': + return 'in_progress'; + case 'completed': + case 'complete': + case 'done': + return 'completed'; + default: + return null; + } +} +//# sourceMappingURL=transcript.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/transcript.js.map b/plugins/balance-hud/dist/transcript.js.map new file mode 100644 index 0000000..bef5094 --- /dev/null +++ b/plugins/balance-hud/dist/transcript.js.map @@ -0,0 +1 @@ +{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/types.d.ts b/plugins/balance-hud/dist/types.d.ts new file mode 100644 index 0000000..684a087 --- /dev/null +++ b/plugins/balance-hud/dist/types.d.ts @@ -0,0 +1,139 @@ +import type { HudConfig } from './config.js'; +import type { GitStatus } from './git.js'; +export interface StdinData { + transcript_path?: string; + cwd?: string; + workspace?: { + current_dir?: string; + project_dir?: string; + added_dirs?: string[]; + git_worktree?: string; + } | null; + model?: { + id?: string; + display_name?: string; + }; + context_window?: { + context_window_size?: number; + total_input_tokens?: number | null; + total_output_tokens?: number | null; + current_usage?: { + input_tokens?: number; + output_tokens?: number; + cache_creation_input_tokens?: number; + cache_read_input_tokens?: number; + } | null; + used_percentage?: number | null; + remaining_percentage?: number | null; + }; + cost?: { + total_cost_usd?: number | null; + total_duration_ms?: number | null; + total_api_duration_ms?: number | null; + total_lines_added?: number | null; + total_lines_removed?: number | null; + } | null; + rate_limits?: { + five_hour?: { + used_percentage?: number | null; + resets_at?: number | null; + } | null; + seven_day?: { + used_percentage?: number | null; + resets_at?: number | null; + } | null; + } | null; + effort?: string | { + level?: string | null; + [key: string]: unknown; + } | null; +} +export interface ToolEntry { + id: string; + name: string; + target?: string; + status: 'running' | 'completed' | 'error'; + startTime: Date; + endTime?: Date; +} +export interface AgentEntry { + id: string; + type: string; + model?: string; + description?: string; + status: 'running' | 'completed'; + startTime: Date; + endTime?: Date; + background?: boolean; +} +export interface TodoItem { + content: string; + status: 'pending' | 'in_progress' | 'completed'; +} +export interface UsageData { + fiveHour: number | null; + sevenDay: number | null; + fiveHourResetAt: Date | null; + sevenDayResetAt: Date | null; + balanceLabel?: string | null; +} +export interface ExternalUsageSnapshot { + five_hour?: { + used_percentage?: number | null; + resets_at?: string | number | null; + } | null; + seven_day?: { + used_percentage?: number | null; + resets_at?: string | number | null; + } | null; + updated_at?: string | number | null; + balance_label?: string | null; +} +export interface MemoryInfo { + totalBytes: number; + usedBytes: number; + freeBytes: number; + usedPercent: number; +} +/** Check if usage limit is reached (either window at 100%) */ +export declare function isLimitReached(data: UsageData): boolean; +export interface SessionTokenUsage { + inputTokens: number; + outputTokens: number; + cacheCreationTokens: number; + cacheReadTokens: number; +} +export interface TranscriptData { + tools: ToolEntry[]; + skills: string[]; + mcpServers: string[]; + agents: AgentEntry[]; + todos: TodoItem[]; + sessionStart?: Date; + sessionName?: string; + lastAssistantResponseAt?: Date; + sessionTokens?: SessionTokenUsage; + lastCompactBoundaryAt?: Date; + lastCompactPostTokens?: number; + compactionCount?: number; + advisorModel?: string; +} +export interface RenderContext { + stdin: StdinData; + transcript: TranscriptData; + claudeMdCount: number; + rulesCount: number; + mcpCount: number; + hooksCount: number; + sessionDuration: string; + gitStatus: GitStatus | null; + usageData: UsageData | null; + memoryUsage: MemoryInfo | null; + config: HudConfig; + extraLabel: string | null; + outputStyle?: string; + claudeCodeVersion?: string; + effortLevel?: string; + effortSymbol?: string; +} +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/types.d.ts.map b/plugins/balance-hud/dist/types.d.ts.map new file mode 100644 index 0000000..508a02d --- /dev/null +++ b/plugins/balance-hud/dist/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,IAAI,CAAC;IACT,KAAK,CAAC,EAAE;QACN,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,cAAc,CAAC,EAAE;QACf,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpC,aAAa,CAAC,EAAE;YACd,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,aAAa,CAAC,EAAE,MAAM,CAAC;YACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;YACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;SAClC,GAAG,IAAI,CAAC;QAET,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACtC,CAAC;IACF,IAAI,CAAC,EAAE;QACL,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACrC,GAAG,IAAI,CAAC;IACT,WAAW,CAAC,EAAE;QACZ,SAAS,CAAC,EAAE;YACV,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SAC3B,GAAG,IAAI,CAAC;QACT,SAAS,CAAC,EAAE;YACV,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SAC3B,GAAG,IAAI,CAAC;KACV,GAAG,IAAI,CAAC;IAKT,MAAM,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CAC5E;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAC1C,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAChC,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;CACjD;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE;QACV,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;KACpC,GAAG,IAAI,CAAC;IACT,SAAS,CAAC,EAAE;QACV,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;KACpC,GAAG,IAAI,CAAC;IACT,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAEvD;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,qBAAqB,CAAC,EAAE,IAAI,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,eAAe,CAAC,EAAE,MAAM,CAAC;IAKzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/types.js b/plugins/balance-hud/dist/types.js new file mode 100644 index 0000000..6f773f9 --- /dev/null +++ b/plugins/balance-hud/dist/types.js @@ -0,0 +1,5 @@ +/** Check if usage limit is reached (either window at 100%) */ +export function isLimitReached(data) { + return data.fiveHour === 100 || data.sevenDay === 100; +} +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/types.js.map b/plugins/balance-hud/dist/types.js.map new file mode 100644 index 0000000..2a9b4b6 --- /dev/null +++ b/plugins/balance-hud/dist/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2GA,8DAA8D;AAC9D,MAAM,UAAU,cAAc,CAAC,IAAe;IAC5C,OAAO,IAAI,CAAC,QAAQ,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC;AACxD,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/usage-api.d.ts b/plugins/balance-hud/dist/usage-api.d.ts new file mode 100644 index 0000000..45c2dc7 --- /dev/null +++ b/plugins/balance-hud/dist/usage-api.d.ts @@ -0,0 +1,65 @@ +import * as https from 'https'; +import type { UsageData } from './types.js'; +export type { UsageData } from './types.js'; +interface UsageApiResponse { + five_hour?: { + utilization?: number; + resets_at?: string; + }; + seven_day?: { + utilization?: number; + resets_at?: string; + }; +} +interface UsageApiResult { + data: UsageApiResponse | null; + error?: string; + /** Retry-After header value in seconds (from 429 responses) */ + retryAfterSec?: number; +} +export declare const USAGE_API_USER_AGENT = "claude-code/2.1"; +type CacheTtls = { + cacheTtlMs: number; + failureCacheTtlMs: number; +}; +export type UsageApiDeps = { + homeDir: () => string; + fetchApi: (accessToken: string) => Promise; + now: () => number; + readKeychain: (now: number, homeDir: string) => { + accessToken: string; + subscriptionType: string; + } | null; + ttls: CacheTtls; +}; +/** + * Get OAuth usage data from Anthropic API. + * Returns null if user is an API user (no OAuth credentials) or credentials are expired. + * Returns { apiUnavailable: true, ... } if API call fails (to show warning in HUD). + * + * Uses file-based cache since HUD runs as a new process each render (~300ms). + * Cache TTL is configurable via usage.cacheTtlSeconds / usage.failureCacheTtlSeconds in config.json + * (defaults: 60s for success, 15s for failures). + */ +export declare function getUsage(overrides?: Partial): Promise; +/** + * Determine the macOS Keychain service name for Claude Code credentials. + * Claude Code uses the default service for ~/.claude and a hashed suffix for custom config directories. + */ +export declare function getKeychainServiceName(configDir: string, homeDir: string): string; +export declare function getKeychainServiceNames(configDir: string, homeDir: string, env?: NodeJS.ProcessEnv): string[]; +export declare function resolveKeychainCredentials(serviceNames: string[], now: number, loadService: (serviceName: string, accountName?: string) => string, accountName?: string | null): { + credentials: { + accessToken: string; + subscriptionType: string; + } | null; + shouldBackoff: boolean; +}; +export declare function getUsagePlanNameFallback(homeDir?: string): string | null; +export declare function getUsageApiTimeoutMs(env?: NodeJS.ProcessEnv): number; +export declare function isNoProxy(hostname: string, env?: NodeJS.ProcessEnv): boolean; +export declare function getProxyUrl(hostname: string, env?: NodeJS.ProcessEnv): URL | null; +export declare function getProxyTunnelRejectUnauthorized(rejectUnauthorized: https.RequestOptions['rejectUnauthorized'], env?: NodeJS.ProcessEnv): boolean; +export declare function parseRetryAfterSeconds(raw: string | string[] | undefined, nowMs?: number): number | undefined; +export declare function clearCache(homeDir?: string): void; +//# sourceMappingURL=usage-api.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/usage-api.d.ts.map b/plugins/balance-hud/dist/usage-api.d.ts.map new file mode 100644 index 0000000..a7e0500 --- /dev/null +++ b/plugins/balance-hud/dist/usage-api.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"usage-api.d.ts","sourceRoot":"","sources":["../src/usage-api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/usage-api.js b/plugins/balance-hud/dist/usage-api.js new file mode 100644 index 0000000..338bf26 --- /dev/null +++ b/plugins/balance-hud/dist/usage-api.js @@ -0,0 +1,945 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import * as net from 'net'; +import * as tls from 'tls'; +import * as https from 'https'; +import { execFileSync } from 'child_process'; +import { createHash } from 'crypto'; +import { createDebug } from './debug.js'; +import { getClaudeConfigDir, getHudPluginDir } from './claude-config-dir.js'; +const debug = createDebug('usage'); +const LEGACY_KEYCHAIN_SERVICE_NAME = 'Claude Code-credentials'; +// File-based cache (HUD runs as new process each render, so in-memory cache won't persist) +const CACHE_TTL_MS = 5 * 60_000; // 5 minutes — matches Anthropic usage API rate limit window +const CACHE_FAILURE_TTL_MS = 15_000; // 15 seconds for failed requests +const CACHE_RATE_LIMITED_BASE_MS = 60_000; // 60s base for 429 backoff +const CACHE_RATE_LIMITED_MAX_MS = 5 * 60_000; // 5 min max backoff +const CACHE_LOCK_STALE_MS = 30_000; +const CACHE_LOCK_WAIT_MS = 2_000; +const CACHE_LOCK_POLL_MS = 50; +const KEYCHAIN_TIMEOUT_MS = 3000; +const KEYCHAIN_BACKOFF_MS = 60_000; // Backoff on keychain failures to avoid re-prompting +const USAGE_API_TIMEOUT_MS_DEFAULT = 15_000; +export const USAGE_API_USER_AGENT = 'claude-code/2.1'; +/** + * Check if user is using a custom API endpoint instead of the default Anthropic API. + * When using custom providers (e.g., via cc-switch), the OAuth usage API is not applicable. + */ +function isUsingCustomApiEndpoint(env = process.env) { + const baseUrl = env.ANTHROPIC_BASE_URL?.trim() || env.ANTHROPIC_API_BASE_URL?.trim(); + // No custom endpoint configured - using default Anthropic API + if (!baseUrl) { + return false; + } + try { + return new URL(baseUrl).origin !== 'https://api.anthropic.com'; + } + catch { + return true; + } +} +function getCachePath(homeDir) { + return path.join(getHudPluginDir(homeDir), '.usage-cache.json'); +} +function getCacheLockPath(homeDir) { + return path.join(getHudPluginDir(homeDir), '.usage-cache.lock'); +} +function hydrateCacheData(data) { + // JSON.stringify converts Date to ISO string, so we need to reconvert on read. + // new Date() handles both Date objects and ISO strings safely. + if (data.fiveHourResetAt) { + data.fiveHourResetAt = new Date(data.fiveHourResetAt); + } + if (data.sevenDayResetAt) { + data.sevenDayResetAt = new Date(data.sevenDayResetAt); + } + return data; +} +function getRateLimitedTtlMs(count) { + // Exponential backoff: 60s, 120s, 240s, capped at 5 min + return Math.min(CACHE_RATE_LIMITED_BASE_MS * Math.pow(2, Math.max(0, count - 1)), CACHE_RATE_LIMITED_MAX_MS); +} +function getRateLimitedRetryUntil(cache) { + if (cache.data.apiError !== 'rate-limited') { + return null; + } + if (cache.retryAfterUntil && cache.retryAfterUntil > cache.timestamp) { + return cache.retryAfterUntil; + } + if (cache.rateLimitedCount && cache.rateLimitedCount > 0) { + return cache.timestamp + getRateLimitedTtlMs(cache.rateLimitedCount); + } + return null; +} +function withRateLimitedSyncing(data) { + return { + ...data, + apiError: 'rate-limited', + }; +} +function readCacheState(homeDir, now, ttls) { + try { + const cachePath = getCachePath(homeDir); + if (!fs.existsSync(cachePath)) + return null; + const content = fs.readFileSync(cachePath, 'utf8'); + const cache = JSON.parse(content); + // Only serve lastGoodData during rate-limit backoff. Other failures should remain visible. + const displayData = (cache.data.apiError === 'rate-limited' && cache.lastGoodData) + ? withRateLimitedSyncing(cache.lastGoodData) + : cache.data; + const rateLimitedRetryUntil = getRateLimitedRetryUntil(cache); + if (rateLimitedRetryUntil && now < rateLimitedRetryUntil) { + return { data: hydrateCacheData(displayData), timestamp: cache.timestamp, isFresh: true }; + } + const ttl = cache.data.apiUnavailable ? ttls.failureCacheTtlMs : ttls.cacheTtlMs; + return { + data: hydrateCacheData(displayData), + timestamp: cache.timestamp, + isFresh: now - cache.timestamp < ttl, + }; + } + catch { + return null; + } +} +function readRateLimitedCount(homeDir) { + try { + const cachePath = getCachePath(homeDir); + if (!fs.existsSync(cachePath)) + return 0; + const content = fs.readFileSync(cachePath, 'utf8'); + const cache = JSON.parse(content); + return cache.rateLimitedCount ?? 0; + } + catch { + return 0; + } +} +function readLastGoodData(homeDir) { + try { + const cachePath = getCachePath(homeDir); + if (!fs.existsSync(cachePath)) + return null; + const content = fs.readFileSync(cachePath, 'utf8'); + const cache = JSON.parse(content); + return cache.lastGoodData ? hydrateCacheData(cache.lastGoodData) : null; + } + catch { + return null; + } +} +function readCachedPlanName(homeDir) { + try { + const cachePath = getCachePath(homeDir); + if (!fs.existsSync(cachePath)) + return null; + const content = fs.readFileSync(cachePath, 'utf8'); + const cache = JSON.parse(content); + return cache.data.planName ?? cache.lastGoodData?.planName ?? null; + } + catch { + return null; + } +} +function readCache(homeDir, now, ttls) { + const cache = readCacheState(homeDir, now, ttls); + return cache?.isFresh ? cache.data : null; +} +function writeCache(homeDir, data, timestamp, opts) { + try { + const cachePath = getCachePath(homeDir); + const cacheDir = path.dirname(cachePath); + if (!fs.existsSync(cacheDir)) { + fs.mkdirSync(cacheDir, { recursive: true }); + } + const cache = { data, timestamp }; + if (opts?.rateLimitedCount && opts.rateLimitedCount > 0) { + cache.rateLimitedCount = opts.rateLimitedCount; + } + if (opts?.retryAfterUntil) { + cache.retryAfterUntil = opts.retryAfterUntil; + } + if (opts?.lastGoodData) { + cache.lastGoodData = opts.lastGoodData; + } + fs.writeFileSync(cachePath, JSON.stringify(cache), 'utf8'); + } + catch { + // Ignore cache write failures + } +} +function readLockTimestamp(lockPath) { + try { + if (!fs.existsSync(lockPath)) + return null; + const raw = fs.readFileSync(lockPath, 'utf8').trim(); + const parsed = Number.parseInt(raw, 10); + return Number.isFinite(parsed) ? parsed : null; + } + catch { + return null; + } +} +function tryAcquireCacheLock(homeDir) { + const lockPath = getCacheLockPath(homeDir); + const cacheDir = path.dirname(lockPath); + try { + if (!fs.existsSync(cacheDir)) { + fs.mkdirSync(cacheDir, { recursive: true }); + } + const fd = fs.openSync(lockPath, 'wx'); + try { + fs.writeFileSync(fd, String(Date.now()), 'utf8'); + } + finally { + fs.closeSync(fd); + } + return 'acquired'; + } + catch (error) { + const maybeError = error; + if (maybeError.code !== 'EEXIST') { + debug('Usage cache lock unavailable, continuing without coordination:', maybeError.message); + return 'unsupported'; + } + } + const lockTimestamp = readLockTimestamp(lockPath); + // Unparseable timestamp — use mtime to distinguish a crash leftover from an active writer. + if (lockTimestamp === null) { + try { + const lockStat = fs.statSync(lockPath); + if (Date.now() - lockStat.mtimeMs < CACHE_LOCK_STALE_MS) { + return 'busy'; + } + } + catch { + return tryAcquireCacheLock(homeDir); + } + try { + fs.unlinkSync(lockPath); + } + catch { + return 'busy'; + } + return tryAcquireCacheLock(homeDir); + } + if (lockTimestamp != null && Date.now() - lockTimestamp > CACHE_LOCK_STALE_MS) { + try { + fs.unlinkSync(lockPath); + } + catch { + return 'busy'; + } + return tryAcquireCacheLock(homeDir); + } + return 'busy'; +} +function releaseCacheLock(homeDir) { + try { + const lockPath = getCacheLockPath(homeDir); + if (fs.existsSync(lockPath)) { + fs.unlinkSync(lockPath); + } + } + catch { + // Ignore lock cleanup failures + } +} +async function waitForFreshCache(homeDir, now, ttls, timeoutMs = CACHE_LOCK_WAIT_MS) { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + await new Promise((resolve) => setTimeout(resolve, CACHE_LOCK_POLL_MS)); + const cached = readCache(homeDir, now(), ttls); + if (cached) { + return cached; + } + if (!fs.existsSync(getCacheLockPath(homeDir))) { + break; + } + } + return readCache(homeDir, now(), ttls); +} +const defaultDeps = { + homeDir: () => os.homedir(), + fetchApi: fetchUsageApi, + now: () => Date.now(), + readKeychain: readKeychainCredentials, + ttls: { cacheTtlMs: CACHE_TTL_MS, failureCacheTtlMs: CACHE_FAILURE_TTL_MS }, +}; +/** + * Get OAuth usage data from Anthropic API. + * Returns null if user is an API user (no OAuth credentials) or credentials are expired. + * Returns { apiUnavailable: true, ... } if API call fails (to show warning in HUD). + * + * Uses file-based cache since HUD runs as a new process each render (~300ms). + * Cache TTL is configurable via usage.cacheTtlSeconds / usage.failureCacheTtlSeconds in config.json + * (defaults: 60s for success, 15s for failures). + */ +export async function getUsage(overrides = {}) { + const deps = { ...defaultDeps, ...overrides }; + const now = deps.now(); + const homeDir = deps.homeDir(); + // Skip usage API if user is using a custom provider + if (isUsingCustomApiEndpoint()) { + debug('Skipping usage API: custom API endpoint configured'); + return null; + } + // Check file-based cache first + const cacheState = readCacheState(homeDir, now, deps.ttls); + if (cacheState?.isFresh) { + return cacheState.data; + } + let holdsCacheLock = false; + const lockStatus = tryAcquireCacheLock(homeDir); + if (lockStatus === 'busy') { + if (cacheState) { + return cacheState.data; + } + return await waitForFreshCache(homeDir, deps.now, deps.ttls); + } + holdsCacheLock = lockStatus === 'acquired'; + try { + const refreshedCache = readCache(homeDir, deps.now(), deps.ttls); + if (refreshedCache) { + return refreshedCache; + } + const credentials = readCredentials(homeDir, now, deps.readKeychain); + if (!credentials) { + return null; + } + const { accessToken, subscriptionType } = credentials; + // Determine plan name from subscriptionType + let planName = getPlanName(subscriptionType); + if (!planName) { + // Only fall back to cache when subscriptionType is genuinely missing or + // empty after an OAuth refresh. Explicit values like "api" should remain + // API users with no usage display. + if (!subscriptionType.trim()) { + const cached = readCacheState(homeDir, now, deps.ttls); + if (cached?.data?.planName) { + planName = cached.data.planName; + } + } + if (!planName) { + return null; + } + } + // Fetch usage from API + const apiResult = await deps.fetchApi(accessToken); + if (!apiResult.data) { + const isRateLimited = apiResult.error === 'rate-limited'; + const prevCount = readRateLimitedCount(homeDir); + const rateLimitedCount = isRateLimited ? prevCount + 1 : 0; + const retryAfterUntil = isRateLimited && apiResult.retryAfterSec + ? now + apiResult.retryAfterSec * 1000 + : undefined; + const backoffOpts = { + rateLimitedCount: isRateLimited ? rateLimitedCount : undefined, + retryAfterUntil, + }; + const failureResult = { + planName, + fiveHour: null, + sevenDay: null, + fiveHourResetAt: null, + sevenDayResetAt: null, + apiUnavailable: true, + apiError: apiResult.error, + }; + if (isRateLimited) { + const staleCache = readCacheState(homeDir, now, deps.ttls); + const lastGood = readLastGoodData(homeDir); + const goodData = (staleCache && !staleCache.data.apiUnavailable) + ? staleCache.data + : lastGood; + if (goodData) { + // Preserve the backoff state in cache, but keep rendering the last successful values + // with a syncing hint so stale data is visible to the user. + writeCache(homeDir, failureResult, now, { ...backoffOpts, lastGoodData: goodData }); + return withRateLimitedSyncing(goodData); + } + } + writeCache(homeDir, failureResult, now, backoffOpts); + return failureResult; + } + // Parse response - API returns 0-100 percentage directly + // Clamp to 0-100 and handle NaN/Infinity + const fiveHour = parseUtilization(apiResult.data.five_hour?.utilization); + const sevenDay = parseUtilization(apiResult.data.seven_day?.utilization); + const fiveHourResetAt = parseDate(apiResult.data.five_hour?.resets_at); + const sevenDayResetAt = parseDate(apiResult.data.seven_day?.resets_at); + const result = { + planName, + fiveHour, + sevenDay, + fiveHourResetAt, + sevenDayResetAt, + }; + // Write to file cache — also store as lastGoodData for rate-limit resilience + writeCache(homeDir, result, now, { lastGoodData: result }); + return result; + } + catch (error) { + debug('getUsage failed:', error); + return null; + } + finally { + if (holdsCacheLock) { + releaseCacheLock(homeDir); + } + } +} +/** + * Get path for keychain failure backoff cache. + * Separate from usage cache to track keychain-specific failures. + */ +function getKeychainBackoffPath(homeDir) { + return path.join(getHudPluginDir(homeDir), '.keychain-backoff'); +} +/** + * Check if we're in keychain backoff period (recent failure/timeout). + * Prevents re-prompting user on every render cycle. + */ +function isKeychainBackoff(homeDir, now) { + try { + const backoffPath = getKeychainBackoffPath(homeDir); + if (!fs.existsSync(backoffPath)) + return false; + const timestamp = parseInt(fs.readFileSync(backoffPath, 'utf8'), 10); + return now - timestamp < KEYCHAIN_BACKOFF_MS; + } + catch { + return false; + } +} +/** + * Record keychain failure for backoff. + */ +function recordKeychainFailure(homeDir, now) { + try { + const backoffPath = getKeychainBackoffPath(homeDir); + const dir = path.dirname(backoffPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(backoffPath, String(now), 'utf8'); + } + catch { + // Ignore write failures + } +} +/** + * Determine the macOS Keychain service name for Claude Code credentials. + * Claude Code uses the default service for ~/.claude and a hashed suffix for custom config directories. + */ +export function getKeychainServiceName(configDir, homeDir) { + const normalizedConfigDir = path.normalize(path.resolve(configDir)); + const normalizedDefaultDir = path.normalize(path.resolve(path.join(homeDir, '.claude'))); + if (normalizedConfigDir === normalizedDefaultDir) { + return LEGACY_KEYCHAIN_SERVICE_NAME; + } + const hash = createHash('sha256').update(normalizedConfigDir).digest('hex').slice(0, 8); + return `${LEGACY_KEYCHAIN_SERVICE_NAME}-${hash}`; +} +export function getKeychainServiceNames(configDir, homeDir, env = process.env) { + const serviceNames = [getKeychainServiceName(configDir, homeDir)]; + const envConfigDir = env.CLAUDE_CONFIG_DIR?.trim(); + if (envConfigDir) { + const normalizedDefaultDir = path.normalize(path.resolve(path.join(homeDir, '.claude'))); + const normalizedEnvDir = path.normalize(path.resolve(envConfigDir)); + if (normalizedEnvDir === normalizedDefaultDir) { + serviceNames.push(LEGACY_KEYCHAIN_SERVICE_NAME); + } + else { + const envHash = createHash('sha256').update(envConfigDir).digest('hex').slice(0, 8); + serviceNames.push(`${LEGACY_KEYCHAIN_SERVICE_NAME}-${envHash}`); + } + } + serviceNames.push(LEGACY_KEYCHAIN_SERVICE_NAME); + return [...new Set(serviceNames)]; +} +function isMissingKeychainItemError(error) { + if (!error || typeof error !== 'object') + return false; + const maybeError = error; + if (maybeError.status === 44) + return true; + const message = typeof maybeError.message === 'string' ? maybeError.message.toLowerCase() : ''; + if (message.includes('could not be found in the keychain')) + return true; + const stderr = typeof maybeError.stderr === 'string' + ? maybeError.stderr.toLowerCase() + : Buffer.isBuffer(maybeError.stderr) + ? maybeError.stderr.toString('utf8').toLowerCase() + : ''; + return stderr.includes('could not be found in the keychain'); +} +export function resolveKeychainCredentials(serviceNames, now, loadService, accountName) { + let shouldBackoff = false; + let allowGenericFallback = Boolean(accountName); + for (const serviceName of serviceNames) { + try { + const keychainData = accountName + ? loadService(serviceName, accountName) + : loadService(serviceName); + if (accountName) + allowGenericFallback = false; + const trimmedKeychainData = keychainData.trim(); + if (!trimmedKeychainData) + continue; + const data = JSON.parse(trimmedKeychainData); + const credentials = parseCredentialsData(data, now); + if (credentials) { + return { credentials, shouldBackoff: false }; + } + } + catch (error) { + if (!isMissingKeychainItemError(error)) { + if (accountName) + allowGenericFallback = false; + shouldBackoff = true; + } + } + } + if (!accountName || !allowGenericFallback) { + return { credentials: null, shouldBackoff }; + } + for (const serviceName of serviceNames) { + try { + const keychainData = loadService(serviceName).trim(); + if (!keychainData) + continue; + const data = JSON.parse(keychainData); + const credentials = parseCredentialsData(data, now); + if (credentials) { + return { credentials, shouldBackoff: false }; + } + } + catch (error) { + if (!isMissingKeychainItemError(error)) { + shouldBackoff = true; + } + } + } + return { credentials: null, shouldBackoff }; +} +function getKeychainAccountName() { + try { + const username = os.userInfo().username.trim(); + return username || null; + } + catch { + return null; + } +} +/** + * Read credentials from macOS Keychain. + * Claude Code stores OAuth credentials in the macOS Keychain with profile-specific service names. + * Returns null if not on macOS or credentials not found. + * + * Security: Uses execFileSync with absolute path to avoid shell injection and PATH hijacking. + */ +function readKeychainCredentials(now, homeDir) { + // Only available on macOS + if (process.platform !== 'darwin') { + return null; + } + // Check backoff to avoid re-prompting on every render after a failure + if (isKeychainBackoff(homeDir, now)) { + debug('Keychain in backoff period, skipping'); + return null; + } + try { + const configDir = getClaudeConfigDir(homeDir); + const serviceNames = getKeychainServiceNames(configDir, homeDir); + const accountName = getKeychainAccountName(); + debug('Trying keychain service names:', serviceNames); + if (accountName) { + debug('Trying keychain account name:', accountName); + } + const resolved = resolveKeychainCredentials(serviceNames, now, (serviceName, lookupAccountName) => execFileSync('/usr/bin/security', lookupAccountName + ? ['find-generic-password', '-s', serviceName, '-a', lookupAccountName, '-w'] + : ['find-generic-password', '-s', serviceName, '-w'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: KEYCHAIN_TIMEOUT_MS }), accountName); + if (resolved.credentials) { + return resolved.credentials; + } + if (resolved.shouldBackoff) { + recordKeychainFailure(homeDir, now); + } + return null; + } + catch (error) { + // Security: Only log error message, not full error object (may contain stdout/stderr with tokens) + const message = error instanceof Error ? error.message : 'unknown error'; + debug('Failed to read from macOS Keychain:', message); + // Record failure for backoff to avoid re-prompting + recordKeychainFailure(homeDir, now); + return null; + } +} +/** + * Read credentials from file (legacy method). + * Older versions of Claude Code stored credentials in {CLAUDE_CONFIG_DIR}/.credentials.json. + */ +function readFileCredentials(homeDir, now) { + const credentialsPath = path.join(getClaudeConfigDir(homeDir), '.credentials.json'); + if (!fs.existsSync(credentialsPath)) { + return null; + } + try { + const content = fs.readFileSync(credentialsPath, 'utf8'); + const data = JSON.parse(content); + return parseCredentialsData(data, now); + } + catch (error) { + debug('Failed to read credentials file:', error); + return null; + } +} +function readFileSubscriptionType(homeDir) { + const credentialsPath = path.join(getClaudeConfigDir(homeDir), '.credentials.json'); + if (!fs.existsSync(credentialsPath)) { + return null; + } + try { + const content = fs.readFileSync(credentialsPath, 'utf8'); + const data = JSON.parse(content); + const subscriptionType = data.claudeAiOauth?.subscriptionType; + const normalizedSubscriptionType = typeof subscriptionType === 'string' + ? subscriptionType.trim() + : ''; + if (!normalizedSubscriptionType) { + return null; + } + return normalizedSubscriptionType; + } + catch (error) { + debug('Failed to read file subscriptionType:', error); + return null; + } +} +/** + * Parse and validate credentials data from either Keychain or file. + */ +function parseCredentialsData(data, now) { + const accessToken = data.claudeAiOauth?.accessToken; + const subscriptionType = data.claudeAiOauth?.subscriptionType ?? ''; + if (!accessToken) { + return null; + } + // Check if token is expired (expiresAt is Unix ms timestamp) + // Use != null to handle expiresAt=0 correctly (would be expired) + const expiresAt = data.claudeAiOauth?.expiresAt; + if (expiresAt != null && expiresAt <= now) { + debug('OAuth token expired'); + return null; + } + return { accessToken, subscriptionType }; +} +/** + * Read OAuth credentials, trying macOS Keychain first (Claude Code 2.x), + * then falling back to file-based credentials (older versions). + * + * Token priority: Keychain token is authoritative (Claude Code 2.x stores current token there). + * SubscriptionType: Can be supplemented from file if keychain lacks it (display-only field). + */ +function readCredentials(homeDir, now, readKeychain) { + // Try macOS Keychain first (Claude Code 2.x) + const keychainCreds = readKeychain(now, homeDir); + if (keychainCreds) { + if (keychainCreds.subscriptionType) { + debug('Using credentials from macOS Keychain'); + return keychainCreds; + } + // Keychain has token but no subscriptionType - try to supplement from file + const fileSubscriptionType = readFileSubscriptionType(homeDir); + if (fileSubscriptionType) { + debug('Using keychain token with file subscriptionType'); + return { + accessToken: keychainCreds.accessToken, + subscriptionType: fileSubscriptionType, + }; + } + // No subscriptionType available - use keychain token anyway + debug('Using keychain token without subscriptionType'); + return keychainCreds; + } + // Fall back to file-based credentials (older versions or non-macOS) + const fileCreds = readFileCredentials(homeDir, now); + if (fileCreds) { + debug('Using credentials from file'); + return fileCreds; + } + return null; +} +function getPlanName(subscriptionType) { + const lower = subscriptionType.toLowerCase(); + if (lower.includes('max')) + return 'Max'; + if (lower.includes('pro')) + return 'Pro'; + if (lower.includes('team')) + return 'Team'; + // API users don't have subscriptionType or have 'api' + if (!subscriptionType || lower.includes('api')) + return null; + // Unknown subscription type - show it capitalized + return subscriptionType.charAt(0).toUpperCase() + subscriptionType.slice(1); +} +export function getUsagePlanNameFallback(homeDir = os.homedir()) { + const subscriptionType = readFileSubscriptionType(homeDir); + if (subscriptionType) { + return getPlanName(subscriptionType); + } + const cachedPlanName = readCachedPlanName(homeDir); + return cachedPlanName ?? null; +} +/** Parse utilization value, clamping to 0-100 and handling NaN/Infinity */ +function parseUtilization(value) { + if (value == null) + return null; + if (!Number.isFinite(value)) + return null; // Handles NaN and Infinity + return Math.round(Math.max(0, Math.min(100, value))); +} +/** Parse ISO date string safely, returning null for invalid dates */ +function parseDate(dateStr) { + if (!dateStr) + return null; + const date = new Date(dateStr); + // Check for Invalid Date + if (isNaN(date.getTime())) { + debug('Invalid date string:', dateStr); + return null; + } + return date; +} +export function getUsageApiTimeoutMs(env = process.env) { + const raw = (env.BALANCE_HUD_USAGE_TIMEOUT_MS || env.CLAUDE_HUD_USAGE_TIMEOUT_MS)?.trim(); + if (!raw) + return USAGE_API_TIMEOUT_MS_DEFAULT; + const parsed = Number(raw); + if (!Number.isInteger(parsed) || parsed <= 0) { + debug('Invalid BALANCE_HUD_USAGE_TIMEOUT_MS value:', raw); + return USAGE_API_TIMEOUT_MS_DEFAULT; + } + return parsed; +} +export function isNoProxy(hostname, env = process.env) { + const noProxy = env.NO_PROXY ?? env.no_proxy; + if (!noProxy) + return false; + const host = hostname.toLowerCase(); + return noProxy.split(',').some((entry) => { + const pattern = entry.trim().toLowerCase(); + if (!pattern) + return false; + if (pattern === '*') + return true; + if (host === pattern) + return true; + const suffix = pattern.startsWith('.') ? pattern : `.${pattern}`; + return host.endsWith(suffix); + }); +} +export function getProxyUrl(hostname, env = process.env) { + if (isNoProxy(hostname, env)) { + debug('Proxy bypassed by NO_PROXY for host:', hostname); + return null; + } + const proxyEnv = env.HTTPS_PROXY + ?? env.https_proxy + ?? env.ALL_PROXY + ?? env.all_proxy + ?? env.HTTP_PROXY + ?? env.http_proxy; + if (!proxyEnv) + return null; + try { + const proxyUrl = new URL(proxyEnv); + if (proxyUrl.protocol !== 'http:' && proxyUrl.protocol !== 'https:') { + debug('Unsupported proxy protocol:', proxyUrl.protocol); + return null; + } + return proxyUrl; + } + catch { + debug('Invalid proxy URL:', proxyEnv); + return null; + } +} +function createProxyTunnelAgent(proxyUrl) { + const proxyHost = proxyUrl.hostname; + const proxyPort = Number.parseInt(proxyUrl.port || (proxyUrl.protocol === 'https:' ? '443' : '80'), 10); + const proxyAuth = proxyUrl.username + ? `Basic ${Buffer.from(`${decodeURIComponent(proxyUrl.username)}:${decodeURIComponent(proxyUrl.password || '')}`).toString('base64')}` + : null; + return new class extends https.Agent { + createConnection(options, callback) { + const targetHost = String(options.host ?? options.hostname ?? 'localhost'); + const targetPort = Number(options.port) || 443; + let settled = false; + const settle = (err, socket) => { + if (settled) + return; + settled = true; + callback?.(err, socket); + }; + const proxySocket = proxyUrl.protocol === 'https:' + ? tls.connect({ host: proxyHost, port: proxyPort, servername: proxyHost }) + : net.connect(proxyPort, proxyHost); + proxySocket.once('error', (error) => { + settle(error, proxySocket); + }); + proxySocket.once('connect', () => { + const connectHeaders = [ + `CONNECT ${targetHost}:${targetPort} HTTP/1.1`, + `Host: ${targetHost}:${targetPort}`, + ]; + if (proxyAuth) { + connectHeaders.push(`Proxy-Authorization: ${proxyAuth}`); + } + connectHeaders.push('', ''); + proxySocket.write(connectHeaders.join('\r\n')); + let responseBuffer = Buffer.alloc(0); + const onData = (chunk) => { + responseBuffer = Buffer.concat([responseBuffer, chunk]); + const headerEndIndex = responseBuffer.indexOf('\r\n\r\n'); + if (headerEndIndex === -1) + return; + proxySocket.removeListener('data', onData); + const headerText = responseBuffer.subarray(0, headerEndIndex).toString('utf8'); + const statusLine = headerText.split('\r\n')[0] ?? ''; + if (!/^HTTP\/1\.[01] 200 /.test(statusLine)) { + const error = new Error(`Proxy CONNECT rejected: ${statusLine || 'unknown status'}`); + proxySocket.destroy(error); + settle(error, proxySocket); + return; + } + const tlsSocket = tls.connect({ + socket: proxySocket, + servername: String(options.servername ?? targetHost), + rejectUnauthorized: getProxyTunnelRejectUnauthorized(options.rejectUnauthorized), + }, () => { + settle(null, tlsSocket); + }); + tlsSocket.once('error', (error) => { + settle(error, tlsSocket); + }); + }; + proxySocket.on('data', onData); + }); + // Must not return the socket here. In Node.js _http_agent.js, createSocket() + // calls: `if (newSocket) oncreate(null, newSocket)` — returning a truthy value + // causes the HTTP request to be written to the raw proxy socket immediately, + // before the CONNECT tunnel is established. Only deliver the final TLS socket + // asynchronously via the callback after the CONNECT handshake succeeds. + return undefined; + } + }(); +} +export function getProxyTunnelRejectUnauthorized(rejectUnauthorized, env = process.env) { + if (rejectUnauthorized === false) { + return false; + } + return env.NODE_TLS_REJECT_UNAUTHORIZED !== '0'; +} +function fetchUsageApi(accessToken) { + return new Promise((resolve) => { + const host = 'api.anthropic.com'; + const timeoutMs = getUsageApiTimeoutMs(); + const proxyUrl = getProxyUrl(host); + const options = { + hostname: host, + path: '/api/oauth/usage', + method: 'GET', + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'anthropic-beta': 'oauth-2025-04-20', + 'User-Agent': USAGE_API_USER_AGENT, + }, + timeout: timeoutMs, + agent: proxyUrl ? createProxyTunnelAgent(proxyUrl) : undefined, + }; + if (proxyUrl) { + debug('Using proxy for usage API:', proxyUrl.origin); + } + const req = https.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk.toString(); + }); + res.on('end', () => { + if (res.statusCode !== 200) { + debug('API returned non-200 status:', res.statusCode); + // Use a distinct error key for 429 so cache/render can handle it specially + const error = res.statusCode === 429 + ? 'rate-limited' + : res.statusCode ? `http-${res.statusCode}` : 'http-error'; + const retryAfterSec = res.statusCode === 429 + ? parseRetryAfterSeconds(res.headers['retry-after']) + : undefined; + if (retryAfterSec) { + debug('Retry-After:', retryAfterSec, 'seconds'); + } + resolve({ data: null, error, retryAfterSec }); + return; + } + try { + const parsed = JSON.parse(data); + resolve({ data: parsed }); + } + catch (error) { + debug('Failed to parse API response:', error); + resolve({ data: null, error: 'parse' }); + } + }); + }); + req.on('error', (error) => { + debug('API request error:', error); + resolve({ data: null, error: 'network' }); + }); + req.on('timeout', () => { + debug('API request timeout'); + req.destroy(); + resolve({ data: null, error: 'timeout' }); + }); + req.end(); + }); +} +export function parseRetryAfterSeconds(raw, nowMs = Date.now()) { + const value = Array.isArray(raw) ? raw[0] : raw; + if (!value) + return undefined; + const parsedSeconds = Number.parseInt(value, 10); + if (Number.isFinite(parsedSeconds) && parsedSeconds > 0) { + return parsedSeconds; + } + const retryAtMs = Date.parse(value); + if (!Number.isFinite(retryAtMs)) { + return undefined; + } + const retryAfterSeconds = Math.ceil((retryAtMs - nowMs) / 1000); + return retryAfterSeconds > 0 ? retryAfterSeconds : undefined; +} +// Export for testing +export function clearCache(homeDir) { + if (homeDir) { + try { + const cachePath = getCachePath(homeDir); + if (fs.existsSync(cachePath)) { + fs.unlinkSync(cachePath); + } + const lockPath = getCacheLockPath(homeDir); + if (fs.existsSync(lockPath)) { + fs.unlinkSync(lockPath); + } + } + catch { + // Ignore + } + } +} +//# sourceMappingURL=usage-api.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/usage-api.js.map b/plugins/balance-hud/dist/usage-api.js.map new file mode 100644 index 0000000..a8f370b --- /dev/null +++ b/plugins/balance-hud/dist/usage-api.js.map @@ -0,0 +1 @@ +{"version":3,"file":"usage-api.js","sourceRoot":"","sources":["../src/usage-api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/format.d.ts b/plugins/balance-hud/dist/utils/format.d.ts new file mode 100644 index 0000000..8490673 --- /dev/null +++ b/plugins/balance-hud/dist/utils/format.d.ts @@ -0,0 +1,17 @@ +import type { RenderContext } from '../types.js'; +/** + * Format a token count into a human-readable short string. + * >= 1M → "1.2M" + * >= 1k → "45k" + * < 1k → "800" + */ +export declare function formatTokens(n: number): string; +/** + * Format the context-window value for display. + * percent → "45%" + * tokens → "45k/200k" + * remaining → "55%" + * both → "45% (45k/200k)" + */ +export declare function formatContextValue(ctx: RenderContext, percent: number, mode: 'percent' | 'tokens' | 'remaining' | 'both'): string; +//# sourceMappingURL=format.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/format.d.ts.map b/plugins/balance-hud/dist/utils/format.d.ts.map new file mode 100644 index 0000000..0c93c50 --- /dev/null +++ b/plugins/balance-hud/dist/utils/format.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ9C;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAChD,MAAM,CA2BR"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/format.js b/plugins/balance-hud/dist/utils/format.js new file mode 100644 index 0000000..d210333 --- /dev/null +++ b/plugins/balance-hud/dist/utils/format.js @@ -0,0 +1,47 @@ +import { getTotalTokens } from '../stdin.js'; +/** + * Format a token count into a human-readable short string. + * >= 1M → "1.2M" + * >= 1k → "45k" + * < 1k → "800" + */ +export function formatTokens(n) { + if (n >= 1000000) { + return `${(n / 1000000).toFixed(1)}M`; + } + if (n >= 1000) { + return `${(n / 1000).toFixed(0)}k`; + } + return n.toString(); +} +/** + * Format the context-window value for display. + * percent → "45%" + * tokens → "45k/200k" + * remaining → "55%" + * both → "45% (45k/200k)" + */ +export function formatContextValue(ctx, percent, mode) { + const totalTokens = getTotalTokens(ctx.stdin); + const autoCompactWindow = ctx.config?.display?.autoCompactWindow ?? null; + const size = typeof autoCompactWindow === 'number' && autoCompactWindow > 0 + ? autoCompactWindow + : ctx.stdin.context_window?.context_window_size ?? 0; + if (mode === 'tokens') { + if (size > 0) { + return `${formatTokens(totalTokens)}/${formatTokens(size)}`; + } + return formatTokens(totalTokens); + } + if (mode === 'both') { + if (size > 0) { + return `${percent}% (${formatTokens(totalTokens)}/${formatTokens(size)})`; + } + return `${percent}%`; + } + if (mode === 'remaining') { + return `${Math.max(0, 100 - percent)}%`; + } + return `${percent}%`; +} +//# sourceMappingURL=format.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/format.js.map b/plugins/balance-hud/dist/utils/format.js.map new file mode 100644 index 0000000..cfc2fe3 --- /dev/null +++ b/plugins/balance-hud/dist/utils/format.js.map @@ -0,0 +1 @@ +{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;QACjB,OAAO,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACxC,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QACd,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;AACtB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAkB,EAClB,OAAe,EACf,IAAiD;IAEjD,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,iBAAiB,IAAI,IAAI,CAAC;IACzE,MAAM,IAAI,GACR,OAAO,iBAAiB,KAAK,QAAQ,IAAI,iBAAiB,GAAG,CAAC;QAC5D,CAAC,CAAC,iBAAiB;QACnB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,mBAAmB,IAAI,CAAC,CAAC;IAEzD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9D,CAAC;QACD,OAAO,YAAY,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,OAAO,GAAG,OAAO,MAAM,YAAY,CAAC,WAAW,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;QAC5E,CAAC;QACD,OAAO,GAAG,OAAO,GAAG,CAAC;IACvB,CAAC;IAED,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAC1C,CAAC;IAED,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/hyperlinks.d.ts b/plugins/balance-hud/dist/utils/hyperlinks.d.ts new file mode 100644 index 0000000..8564682 --- /dev/null +++ b/plugins/balance-hud/dist/utils/hyperlinks.d.ts @@ -0,0 +1,21 @@ +/** + * Wrap `text` in an OSC 8 hyperlink pointing to `uri`. + */ +export declare function hyperlink(uri: string, text: string): string; +/** + * Convert a filesystem path to a `file://` URL string. + * Returns `null` on failure. + */ +export declare function getFileHref(filePath: string): string | null; +/** + * Wrap `text` in an OSC 8 hyperlink after validating the URI. + * + * Only `https:` and `file:` protocols are allowed. Returns plain `text` + * when the URI is missing, invalid, or uses a disallowed protocol. + * + * @param uri - The URI to link to (may be undefined/null). + * @param text - The visible text to display. + * @param protocols - Allowed URL protocols (default: `['https:', 'file:']`). + */ +export declare function safeHyperlink(uri: string | undefined | null, text: string, protocols?: string[]): string; +//# sourceMappingURL=hyperlinks.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/hyperlinks.d.ts.map b/plugins/balance-hud/dist/utils/hyperlinks.d.ts.map new file mode 100644 index 0000000..d1e896f --- /dev/null +++ b/plugins/balance-hud/dist/utils/hyperlinks.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"hyperlinks.d.ts","sourceRoot":"","sources":["../../src/utils/hyperlinks.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAM,EAAwB,GACxC,MAAM,CAeR"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/hyperlinks.js b/plugins/balance-hud/dist/utils/hyperlinks.js new file mode 100644 index 0000000..db27bcb --- /dev/null +++ b/plugins/balance-hud/dist/utils/hyperlinks.js @@ -0,0 +1,50 @@ +import * as path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { sanitizeDisplayText } from './sanitize.js'; +/** + * Wrap `text` in an OSC 8 hyperlink pointing to `uri`. + */ +export function hyperlink(uri, text) { + const esc = '\x1b'; + const st = '\\'; + return `${esc}]8;;${uri}${esc}${st}${text}${esc}]8;;${esc}${st}`; +} +/** + * Convert a filesystem path to a `file://` URL string. + * Returns `null` on failure. + */ +export function getFileHref(filePath) { + try { + return pathToFileURL(path.resolve(filePath)).toString(); + } + catch { + return null; + } +} +/** + * Wrap `text` in an OSC 8 hyperlink after validating the URI. + * + * Only `https:` and `file:` protocols are allowed. Returns plain `text` + * when the URI is missing, invalid, or uses a disallowed protocol. + * + * @param uri - The URI to link to (may be undefined/null). + * @param text - The visible text to display. + * @param protocols - Allowed URL protocols (default: `['https:', 'file:']`). + */ +export function safeHyperlink(uri, text, protocols = ['https:', 'file:']) { + if (!uri) { + return text; + } + const sanitizedUri = sanitizeDisplayText(uri); + try { + const parsed = new URL(sanitizedUri); + if (!protocols.includes(parsed.protocol)) { + return text; + } + return hyperlink(parsed.toString(), text); + } + catch { + return text; + } +} +//# sourceMappingURL=hyperlinks.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/hyperlinks.js.map b/plugins/balance-hud/dist/utils/hyperlinks.js.map new file mode 100644 index 0000000..d52be47 --- /dev/null +++ b/plugins/balance-hud/dist/utils/hyperlinks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hyperlinks.js","sourceRoot":"","sources":["../../src/utils/hyperlinks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,IAAY;IACjD,MAAM,GAAG,GAAG,MAAM,CAAC;IACnB,MAAM,EAAE,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,GAAG,GAAG,EAAE,GAAG,IAAI,GAAG,GAAG,OAAO,GAAG,GAAG,EAAE,EAAE,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,IAAI,CAAC;QACH,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,GAA8B,EAC9B,IAAY,EACZ,YAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC;IAEzC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;QACrC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/sanitize.d.ts b/plugins/balance-hud/dist/utils/sanitize.d.ts new file mode 100644 index 0000000..939c4cd --- /dev/null +++ b/plugins/balance-hud/dist/utils/sanitize.d.ts @@ -0,0 +1,9 @@ +/** + * Strip ANSI escape sequences (CSI, OSC, 7-bit C1/Fe), control characters, + * and bidi overrides from an untrusted string. + * + * Use this whenever displaying user-supplied or external text in the + * terminal to prevent escape injection and layout corruption. + */ +export declare function sanitizeDisplayText(input: string): string; +//# sourceMappingURL=sanitize.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/sanitize.d.ts.map b/plugins/balance-hud/dist/utils/sanitize.d.ts.map new file mode 100644 index 0000000..50125db --- /dev/null +++ b/plugins/balance-hud/dist/utils/sanitize.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AAiBA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/sanitize.js b/plugins/balance-hud/dist/utils/sanitize.js new file mode 100644 index 0000000..6ce1764 --- /dev/null +++ b/plugins/balance-hud/dist/utils/sanitize.js @@ -0,0 +1,28 @@ +/** + * Regex matching display-unsafe control characters and bidi overrides. + * + * Covers C0 (U+0000–U+001F), DEL + C1 (U+007F–U+009F), Arabic bidi + * mark (U+061C), LRM/RLM (U+200E–U+200F), bidi embedding/override + * (U+202A–U+202E), bidi isolate (U+2066–U+2069), and deprecated + * formatting chars (U+206A–U+206F). + */ +const CONTROL_AND_BIDI_PATTERN = new RegExp('[' + + '\\u0000-\\u001F\\u007F-\\u009F' + + '\\u061C\\u200E\\u200F' + + '\\u202A-\\u202E\\u2066-\\u2069\\u206A-\\u206F' + + ']', 'g'); +/** + * Strip ANSI escape sequences (CSI, OSC, 7-bit C1/Fe), control characters, + * and bidi overrides from an untrusted string. + * + * Use this whenever displaying user-supplied or external text in the + * terminal to prevent escape injection and layout corruption. + */ +export function sanitizeDisplayText(input) { + return input + .replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '') // CSI sequences + .replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, '') // OSC sequences + .replace(/\x1B[@-Z\\-_]/g, '') // 7-bit C1 / ESC Fe + .replace(CONTROL_AND_BIDI_PATTERN, ''); // control + bidi chars +} +//# sourceMappingURL=sanitize.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/sanitize.js.map b/plugins/balance-hud/dist/utils/sanitize.js.map new file mode 100644 index 0000000..59fdb4c --- /dev/null +++ b/plugins/balance-hud/dist/utils/sanitize.js.map @@ -0,0 +1 @@ +{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,wBAAwB,GAAG,IAAI,MAAM,CACzC,GAAG;IACH,gCAAgC;IAChC,uBAAuB;IACvB,+CAA+C;IAC/C,GAAG,EACH,GAAG,CACJ,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,KAAK;SACT,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAU,gBAAgB;SACjE,OAAO,CAAC,oCAAoC,EAAE,EAAE,CAAC,CAAC,gBAAgB;SAClE,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAsB,oBAAoB;SACvE,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC,CAAc,uBAAuB;AAChF,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/terminal.d.ts b/plugins/balance-hud/dist/utils/terminal.d.ts new file mode 100644 index 0000000..dbc8c56 --- /dev/null +++ b/plugins/balance-hud/dist/utils/terminal.d.ts @@ -0,0 +1,7 @@ +export declare const UNKNOWN_TERMINAL_WIDTH: null; +export declare function getTerminalWidth(options?: { + preferEnv?: boolean; + fallback?: number | null; +}): number | null; +export declare function getAdaptiveBarWidth(): number; +//# sourceMappingURL=terminal.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/terminal.d.ts.map b/plugins/balance-hud/dist/utils/terminal.d.ts.map new file mode 100644 index 0000000..053364f --- /dev/null +++ b/plugins/balance-hud/dist/utils/terminal.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/utils/terminal.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,MAAO,CAAC;AAa3C,wBAAgB,gBAAgB,CAAC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GAAG,MAAM,GAAG,IAAI,CAc/G;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAS5C"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/terminal.js b/plugins/balance-hud/dist/utils/terminal.js new file mode 100644 index 0000000..851c978 --- /dev/null +++ b/plugins/balance-hud/dist/utils/terminal.js @@ -0,0 +1,37 @@ +export const UNKNOWN_TERMINAL_WIDTH = null; +function parseEnvColumns() { + const envColumns = Number.parseInt(process.env.COLUMNS ?? '', 10); + return Number.isFinite(envColumns) && envColumns > 0 ? envColumns : null; +} +function parseStreamColumns(columns) { + return typeof columns === 'number' && Number.isFinite(columns) && columns > 0 + ? Math.floor(columns) + : null; +} +export function getTerminalWidth(options = {}) { + const { preferEnv = false, fallback = null } = options; + if (preferEnv) { + return parseEnvColumns() + ?? parseStreamColumns(process.stdout?.columns) + ?? parseStreamColumns(process.stderr?.columns) + ?? fallback; + } + return parseStreamColumns(process.stdout?.columns) + ?? parseStreamColumns(process.stderr?.columns) + ?? parseEnvColumns() + ?? fallback; +} +// Returns a progress bar width scaled to the current terminal width. +// Wide (>=100): 10, Medium (60-99): 6, Narrow (<60): 4. +export function getAdaptiveBarWidth() { + const cols = getTerminalWidth({ preferEnv: true }); + if (cols !== null) { + if (cols >= 100) + return 10; + if (cols >= 60) + return 6; + return 4; + } + return 10; +} +//# sourceMappingURL=terminal.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/terminal.js.map b/plugins/balance-hud/dist/utils/terminal.js.map new file mode 100644 index 0000000..bd693d8 --- /dev/null +++ b/plugins/balance-hud/dist/utils/terminal.js.map @@ -0,0 +1 @@ +{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/utils/terminal.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,SAAS,eAAe;IACtB,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAgB;IAC1C,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;QAC3E,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;QACrB,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAA6D,EAAE;IAC9F,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,QAAQ,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAEvD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,eAAe,EAAE;eACnB,kBAAkB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;eAC3C,kBAAkB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;eAC3C,QAAQ,CAAC;IAChB,CAAC;IAED,OAAO,kBAAkB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;WAC7C,kBAAkB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;WAC3C,eAAe,EAAE;WACjB,QAAQ,CAAC;AAChB,CAAC;AAED,qEAAqE;AACrE,wDAAwD;AACxD,MAAM,UAAU,mBAAmB;IACjC,MAAM,IAAI,GAAG,gBAAgB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEnD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,IAAI,IAAI,IAAI,GAAG;YAAE,OAAO,EAAE,CAAC;QAC3B,IAAI,IAAI,IAAI,EAAE;YAAE,OAAO,CAAC,CAAC;QACzB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/truncate.d.ts b/plugins/balance-hud/dist/utils/truncate.d.ts new file mode 100644 index 0000000..c3c5ba7 --- /dev/null +++ b/plugins/balance-hud/dist/utils/truncate.d.ts @@ -0,0 +1,10 @@ +/** + * Truncate a string to `maxLen` characters, appending an ellipsis when + * the string exceeds the limit. + * + * @param text - The string to truncate. + * @param maxLen - Maximum character length (including the suffix). + * @param suffix - The truncation indicator (default: `'...'`). + */ +export declare function truncateString(text: string | null | undefined, maxLen: number, suffix?: string): string; +//# sourceMappingURL=truncate.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/truncate.d.ts.map b/plugins/balance-hud/dist/utils/truncate.d.ts.map new file mode 100644 index 0000000..c696aea --- /dev/null +++ b/plugins/balance-hud/dist/utils/truncate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../src/utils/truncate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,EAAE,MAAM,EACd,MAAM,SAAQ,GACb,MAAM,CAIR"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/truncate.js b/plugins/balance-hud/dist/utils/truncate.js new file mode 100644 index 0000000..876a0cf --- /dev/null +++ b/plugins/balance-hud/dist/utils/truncate.js @@ -0,0 +1,16 @@ +/** + * Truncate a string to `maxLen` characters, appending an ellipsis when + * the string exceeds the limit. + * + * @param text - The string to truncate. + * @param maxLen - Maximum character length (including the suffix). + * @param suffix - The truncation indicator (default: `'...'`). + */ +export function truncateString(text, maxLen, suffix = '...') { + if (!text) + return ''; + if (text.length <= maxLen) + return text; + return text.slice(0, Math.max(0, maxLen - suffix.length)) + suffix; +} +//# sourceMappingURL=truncate.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/utils/truncate.js.map b/plugins/balance-hud/dist/utils/truncate.js.map new file mode 100644 index 0000000..d0098b6 --- /dev/null +++ b/plugins/balance-hud/dist/utils/truncate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"truncate.js","sourceRoot":"","sources":["../../src/utils/truncate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,IAA+B,EAC/B,MAAc,EACd,MAAM,GAAG,KAAK;IAEd,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC;AACrE,CAAC"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/version.d.ts b/plugins/balance-hud/dist/version.d.ts new file mode 100644 index 0000000..525d620 --- /dev/null +++ b/plugins/balance-hud/dist/version.d.ts @@ -0,0 +1,25 @@ +type ExecFileResult = { + stdout: string; +}; +type ExecFileImpl = (file: string, args: string[], options: { + timeout: number; + encoding: BufferEncoding; + windowsHide?: boolean; +}) => Promise; +type ClaudeBinaryInfo = { + path: string; + mtimeMs: number; +}; +type ClaudeVersionInvocation = { + file: string; + args: string[]; +}; +export declare function _parseClaudeCodeVersion(output: string): string | undefined; +export declare function _getClaudeVersionInvocation(binaryPath: string, platform?: NodeJS.Platform, windowsCmd?: string): ClaudeVersionInvocation; +export declare function getClaudeCodeVersion(): Promise; +export declare function _resetVersionCache(): void; +export declare function _setExecFileImplForTests(impl: ExecFileImpl | null): void; +export declare function _setResolveClaudeBinaryForTests(impl: (() => ClaudeBinaryInfo | null) | null): void; +export declare function _setVersionInvocationEnvForTests(platformGetter: (() => NodeJS.Platform) | null, windowsCmdGetter: (() => string) | null): void; +export {}; +//# sourceMappingURL=version.d.ts.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/version.d.ts.map b/plugins/balance-hud/dist/version.d.ts.map new file mode 100644 index 0000000..1a09e38 --- /dev/null +++ b/plugins/balance-hud/dist/version.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAUA,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,YAAY,GAAG,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IACP,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,KACE,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AASF,KAAK,uBAAuB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC;AAsKF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ1E;AAED,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAAM,CAAC,QAAyB,EAC1C,UAAU,GAAE,MAAyB,GACpC,uBAAuB,CAczB;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiExE;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAExE;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAElG;AAED,wBAAgB,gCAAgC,CAC9C,cAAc,EAAE,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,EAC9C,gBAAgB,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,IAAI,GACtC,IAAI,CAGN"} \ No newline at end of file diff --git a/plugins/balance-hud/dist/version.js b/plugins/balance-hud/dist/version.js new file mode 100644 index 0000000..5444468 --- /dev/null +++ b/plugins/balance-hud/dist/version.js @@ -0,0 +1,246 @@ +import { execFile } from 'node:child_process'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { promisify } from 'node:util'; +import { getHudPluginDir } from './claude-config-dir.js'; +import { createDebug } from './debug.js'; +const debug = createDebug('version'); +const CACHE_FILENAME = '.claude-code-version-cache.json'; +const defaultExecFile = promisify(execFile); +let execFileImpl = defaultExecFile; +let resolveClaudeBinaryImpl = resolveClaudeBinaryFromPath; +let platformImpl = () => process.platform; +let windowsCmdImpl = () => 'C:\\Windows\\System32\\cmd.exe'; +let cachedBinaryKey; +let cachedVersion; +let hasResolved = false; +function getVersionCachePath(homeDir) { + return path.join(getHudPluginDir(homeDir), CACHE_FILENAME); +} +function getBinaryCacheKey(binaryInfo) { + return `${binaryInfo.path}:${binaryInfo.mtimeMs}`; +} +function quoteForCmd(arg) { + if (!arg) { + return '""'; + } + if (!/[\s"&|<>^()]/.test(arg)) { + return arg; + } + return `"${arg.replace(/"/g, '""')}"`; +} +function statResolvedBinary(binaryPath) { + try { + const realPath = fs.realpathSync(binaryPath); + const stat = fs.statSync(realPath); + if (!stat.isFile()) { + return null; + } + return { + path: realPath, + mtimeMs: stat.mtimeMs, + }; + } + catch (err) { + debug('Failed to stat binary %s:', binaryPath, err instanceof Error ? err.message : err); + return null; + } +} +function readVersionCache(homeDir) { + try { + const cachePath = getVersionCachePath(homeDir); + if (!fs.existsSync(cachePath)) { + return null; + } + const parsed = JSON.parse(fs.readFileSync(cachePath, 'utf8')); + if ((parsed.resolvedFromPath !== undefined && typeof parsed.resolvedFromPath !== 'string') + || + typeof parsed.binaryPath !== 'string' + || typeof parsed.binaryMtimeMs !== 'number' + || (typeof parsed.version !== 'string' && parsed.version !== null)) { + return null; + } + return parsed; + } + catch (err) { + debug('Failed to read version cache:', err instanceof Error ? err.message : err); + return null; + } +} +function writeVersionCache(homeDir, cache) { + try { + const cachePath = getVersionCachePath(homeDir); + const cacheDir = path.dirname(cachePath); + if (!fs.existsSync(cacheDir)) { + fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 }); + } + try { + fs.chmodSync(cacheDir, 0o700); + } + catch { + // Best-effort: some filesystems do not support POSIX modes. + } + fs.writeFileSync(cachePath, JSON.stringify(cache), { encoding: 'utf8', mode: 0o600 }); + try { + fs.chmodSync(cachePath, 0o600); + } + catch { + // Best-effort: version cache permissions should not affect rendering. + } + } + catch (err) { + debug('Failed to write version cache:', err instanceof Error ? err.message : err); + } +} +function isExecutableFile(candidatePath) { + try { + const stat = fs.statSync(candidatePath); + if (!stat.isFile()) { + return false; + } + if (process.platform === 'win32') { + return true; + } + fs.accessSync(candidatePath, fs.constants.X_OK); + return true; + } + catch (err) { + debug('Binary candidate not executable %s:', candidatePath, err instanceof Error ? err.message : err); + return false; + } +} +function getPathCandidates(command) { + if (process.platform !== 'win32') { + return [command]; + } + const ext = path.extname(command); + if (ext) { + return [command]; + } + const pathExt = (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD') + .split(';') + .map((value) => value.trim()) + .filter(Boolean); + return [command, ...pathExt.map((suffix) => `${command}${suffix.toLowerCase()}`), ...pathExt.map((suffix) => `${command}${suffix.toUpperCase()}`)]; +} +function resolveClaudeBinaryFromPath() { + const pathValue = process.env.PATH; + if (!pathValue) { + return null; + } + const candidates = getPathCandidates('claude'); + for (const entry of pathValue.split(path.delimiter)) { + if (!entry) { + continue; + } + const dir = entry.replace(/^"(.*)"$/, '$1'); + for (const candidate of candidates) { + const candidatePath = path.join(dir, candidate); + if (!isExecutableFile(candidatePath)) { + continue; + } + const binaryInfo = statResolvedBinary(candidatePath); + if (binaryInfo) { + return binaryInfo; + } + } + } + return null; +} +export function _parseClaudeCodeVersion(output) { + const trimmed = output.trim(); + if (!trimmed) { + return undefined; + } + const match = trimmed.match(/\d+(?:\.\d+)+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?/); + return match?.[0]; +} +export function _getClaudeVersionInvocation(binaryPath, platform = platformImpl(), windowsCmd = windowsCmdImpl()) { + const ext = path.extname(binaryPath).toLowerCase(); + if (platform === 'win32' && (ext === '.cmd' || ext === '.bat')) { + const command = [quoteForCmd(binaryPath), '--version'].join(' '); + return { + file: windowsCmd, + args: ['/d', '/s', '/c', `"${command}"`], + }; + } + return { + file: binaryPath, + args: ['--version'], + }; +} +export async function getClaudeCodeVersion() { + const homeDir = os.homedir(); + const diskCache = readVersionCache(homeDir); + if (diskCache) { + const cachedBinaryInfo = statResolvedBinary(diskCache.binaryPath); + const resolvedBinaryCandidate = resolveClaudeBinaryImpl(); + const currentResolvedBinary = resolvedBinaryCandidate + ? (statResolvedBinary(resolvedBinaryCandidate.path) ?? resolvedBinaryCandidate) + : null; + if (cachedBinaryInfo + && cachedBinaryInfo.path === diskCache.binaryPath + && cachedBinaryInfo.mtimeMs === diskCache.binaryMtimeMs + && currentResolvedBinary + && currentResolvedBinary.path === diskCache.binaryPath) { + const cachedKey = getBinaryCacheKey(cachedBinaryInfo); + if (hasResolved && cachedBinaryKey === cachedKey) { + return cachedVersion; + } + cachedBinaryKey = cachedKey; + cachedVersion = diskCache.version ?? undefined; + hasResolved = true; + return cachedVersion; + } + } + const resolvedBinaryInfo = resolveClaudeBinaryImpl(); + if (!resolvedBinaryInfo) { + return undefined; + } + // Normalize resolver output to the actual on-disk binary so cache keys and + // persisted mtimes stay stable across process boundaries. + const binaryInfo = statResolvedBinary(resolvedBinaryInfo.path) ?? resolvedBinaryInfo; + const binaryKey = getBinaryCacheKey(binaryInfo); + if (hasResolved && cachedBinaryKey === binaryKey) { + return cachedVersion; + } + try { + const invocation = _getClaudeVersionInvocation(binaryInfo.path); + const { stdout } = await execFileImpl(invocation.file, invocation.args, { + timeout: 2000, + encoding: 'utf8', + windowsHide: true, + }); + cachedVersion = _parseClaudeCodeVersion(stdout); + } + catch (err) { + debug('Failed to execute claude --version:', err instanceof Error ? err.message : err); + cachedVersion = undefined; + } + writeVersionCache(homeDir, { + resolvedFromPath: resolvedBinaryInfo.path, + binaryPath: binaryInfo.path, + binaryMtimeMs: binaryInfo.mtimeMs, + version: cachedVersion ?? null, + }); + cachedBinaryKey = binaryKey; + hasResolved = true; + return cachedVersion; +} +export function _resetVersionCache() { + cachedBinaryKey = undefined; + cachedVersion = undefined; + hasResolved = false; +} +export function _setExecFileImplForTests(impl) { + execFileImpl = impl ?? defaultExecFile; +} +export function _setResolveClaudeBinaryForTests(impl) { + resolveClaudeBinaryImpl = impl ?? resolveClaudeBinaryFromPath; +} +export function _setVersionInvocationEnvForTests(platformGetter, windowsCmdGetter) { + platformImpl = platformGetter ?? (() => process.platform); + windowsCmdImpl = windowsCmdGetter ?? (() => 'C:\\Windows\\System32\\cmd.exe'); +} +//# sourceMappingURL=version.js.map \ No newline at end of file diff --git a/plugins/balance-hud/dist/version.js.map b/plugins/balance-hud/dist/version.js.map new file mode 100644 index 0000000..85b7039 --- /dev/null +++ b/plugins/balance-hud/dist/version.js.map @@ -0,0 +1 @@ +{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/balance-hud/hooks/hooks.json b/plugins/balance-hud/hooks/hooks.json new file mode 100644 index 0000000..1054bb8 --- /dev/null +++ b/plugins/balance-hud/hooks/hooks.json @@ -0,0 +1,16 @@ +{ + "description": "Auto-start balance-hud background balance refresh daemon on every Claude Code session. Also initializes the HUD statusline engine.", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/auto_refresh.mjs", + "async": true + } + ] + } + ] + } +} diff --git a/plugins/balance-hud/package.json b/plugins/balance-hud/package.json new file mode 100644 index 0000000..9ae4dc7 --- /dev/null +++ b/plugins/balance-hud/package.json @@ -0,0 +1,16 @@ +{ + "name": "balance-hud", + "version": "2.0.0", + "description": "Real-time HUD statusline + API balance monitoring for Claude Code", + "type": "module", + "main": "dist/index.js", + "scripts": { + "start": "node dist/index.js" + }, + "keywords": ["hud", "balance", "monitoring", "claude-code", "deepseek", "openai", "anthropic"], + "author": "NYRO", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } +} diff --git a/plugins/balance-hud/scripts/auto_refresh.mjs b/plugins/balance-hud/scripts/auto_refresh.mjs new file mode 100644 index 0000000..8c41f8f --- /dev/null +++ b/plugins/balance-hud/scripts/auto_refresh.mjs @@ -0,0 +1,254 @@ +#!/usr/bin/env node +/** + * Balance HUD v2.0.0 — Background refresh daemon. + * + * Queries DeepSeek API every N seconds and writes last_balance/last_check + * to session_state.json for the HUD renderer (hud_balance.mjs) to read. + * No history entries — this is cache-only refresh. + * + * PID preemptive lock: new session kills old daemon and takes over. + * On startup, resets session state so consumption starts from zero. + * + * Usage: + * node auto_refresh.mjs [seconds] Start daemon (default 15s) + * node auto_refresh.mjs --warn N Set low-balance warning threshold (default ¥5) + */ + +import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PLUGIN_DIR = resolve(__dirname, '..'); +const STATE_FILE = resolve(PLUGIN_DIR, 'session_state.json'); +const PID_FILE = resolve(PLUGIN_DIR, '.auto_refresh_pid'); +const args = process.argv.slice(2); +const INTERVAL_MS = (parseInt(args.find(a => !a.startsWith('--')), 10) || 15) * 1000; + +// ── --warn : set low-balance warning threshold ───── +const warnIdx = args.indexOf('--warn'); +if (warnIdx !== -1 && args[warnIdx + 1] != null) { + const warnVal = parseFloat(args[warnIdx + 1]); + if (isNaN(warnVal) || warnVal < 0) { + process.stderr.write('❌ --warn 需要有效的正数金额,例如: --warn 10\n'); + process.exit(1); + } + let state = {}; + try { state = JSON.parse(readFileSync(STATE_FILE, 'utf-8')); } catch {} + state._warn_threshold = warnVal; + mkdirSync(resolve(__dirname, '..'), { recursive: true }); + writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf-8'); + process.stderr.write(`✅ 低余额预警阈值已设为 ¥${warnVal.toFixed(2)}\n`); + process.stderr.write(` 余额 ≤ ¥${warnVal.toFixed(2)} 时 HUD 进度条变黄 + 充值提醒\n`); + process.exit(0); +} + +// ── Singleton guard ───────────────────────────────────────── +function isProcessAlive(pid) { + try { + // process.kill(pid, 0) works on both Windows & Unix in Node 18+ + process.kill(pid, 0); + return true; + } catch { + return false; + } +} + +async function acquireLock() { + try { + if (existsSync(PID_FILE)) { + const oldPid = parseInt(readFileSync(PID_FILE, 'utf-8').trim(), 10); + if (isProcessAlive(oldPid)) { + // Previous session's daemon survived — kill it so new session starts fresh + try { process.kill(oldPid, 'SIGTERM'); } catch {} + try { unlinkSync(PID_FILE); } catch {} + // Brief yield to let OS reap the old process + await new Promise(r => setTimeout(r, 200)); + } else { + // Stale PID file — remove it + try { unlinkSync(PID_FILE); } catch {} + } + } + writeFileSync(PID_FILE, String(process.pid), 'utf-8'); + // Clean PID file on exit + process.on('exit', () => { try { unlinkSync(PID_FILE); } catch {} }); + process.on('SIGINT', () => { process.exit(0); }); + process.on('SIGTERM', () => { process.exit(0); }); + return true; + } catch (e) { + process.stderr.write(`[balance-refresh] Lock failed: ${e.message}\n`); + process.exit(1); + } +} + +// ── Key discovery ────────────────────────────────────────── +function getKeys() { + const keys = {}; + const ds = process.env.DEEPSEEK_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN || ''; + if (ds) keys.deepseek = ds; + return keys; // Only DeepSeek supports real-time balance; OpenAI/Anthropic need admin keys +} + +// ── API call ─────────────────────────────────────────────── +async function checkDeepSeek(key) { + try { + const resp = await fetch('https://api.deepseek.com/user/balance', { + headers: { Accept: 'application/json', Authorization: `Bearer ${key}` } + }); + if (!resp.ok) return null; + const data = await resp.json(); + if (!data.is_available) return null; + const b = (data.balance_infos || [])[0] || {}; + return parseFloat(b.total_balance || 0); + } catch { return null; } +} + +// ── Session lifecycle ────────────────────────────────────── +function now() { + const d = new Date(); + const pad = n => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; +} + +function resetSessionState() { + // Clear provider state so this session starts fresh. + // auto_refresh runs per-session (dies when Claude Code exits), + // so startup always means a new session. + try { + let state = {}; + try { state = JSON.parse(readFileSync(STATE_FILE, 'utf-8')); } catch { /* new file */ } + for (const [key, s] of Object.entries(state)) { + if (key.startsWith('_')) continue; + if (typeof s === 'object' && s !== null && 'last_balance' in s) { + delete s.initial_balance; + delete s.session_start; + s.history = []; + } + } + state._session_started_at = now(); + mkdirSync(resolve(__dirname, '..'), { recursive: true }); + writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf-8'); + } catch { /* ignore */ } +} + +function refreshState(provider, balance) { + try { + const state = JSON.parse(readFileSync(STATE_FILE, 'utf-8')); + if (!state[provider]) state[provider] = {}; + const s = state[provider]; + if (s.initial_balance == null) { + s.initial_balance = balance; + s.session_start = now(); + s.history = []; + } + s.last_balance = balance; + s.last_check = now(); + // No history push — this is cache-only refresh + mkdirSync(resolve(__dirname, '..'), { recursive: true }); + writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf-8'); + } catch { /* ignore */ } +} + +// ── Main loop ────────────────────────────────────────────── +const keys = getKeys(); + +if (!Object.keys(keys).length) { + process.stderr.write('[balance-refresh] No API keys configured. Exiting.\n'); + process.exit(0); +} + +// Ensure only one instance runs; kill old session daemon if still alive +await acquireLock(); + +// New session: reset saved state so consumption starts from zero +resetSessionState(); + +process.stderr.write(`[balance-refresh] Started (every ${INTERVAL_MS / 1000}s, ${Object.keys(keys).join(', ')})\n`); + +function writeBalanceSnapshot() { + // Write external-usage snapshot for HUD engine to read via externalUsagePath. + // Called after each tick so the HUD always has fresh balance data. + // ANSI colors match hud_balance.mjs v1.1.3: + // BLUE label, BRIGHT_GREEN balance (YELLOW if low), DIM separators, + // RED consumed, BRIGHT_MAGENTA pct, ORANGE time. + const SNAPSHOT_FILE = resolve(PLUGIN_DIR, 'balance_usage.json'); + const LABELS = { deepseek: 'DeepSeek', openai: 'OpenAI', anthropic: 'Anthropic' }; + const STALE_MS = 30 * 60 * 1000; + // ANSI constants matching v1.1.3 hud_balance.mjs + const B_BLUE = '\x1b[94m'; // Provider label + const B_GREEN = '\x1b[92m'; // Balance (normal) + const B_YELLOW = '\x1b[93m'; // Balance (low) + const B_RED = '\x1b[31m'; // Consumed amount + const B_MAGENTA = '\x1b[95m'; // Percentage + const B_ORANGE = '\x1b[38;5;208m'; // Refresh time + const B_DIM = '\x1b[2m'; // Separators + const B_RESET = '\x1b[0m'; + try { + let state = {}; + try { state = JSON.parse(readFileSync(STATE_FILE, 'utf-8')); } catch { return; } + const nowTs = Date.now(); + const warnThreshold = (state._warn_threshold != null) + ? parseFloat(state._warn_threshold) : 5.0; + const parts = []; + let lastConsumed = null, lastPct = null, lastRefreshTime = null, lastIsLow = false; + for (const [name, s] of Object.entries(state)) { + if (!s.last_balance || s.initial_balance == null) continue; + const lastCheck = s.last_check ? new Date(s.last_check.includes('T') ? s.last_check : s.last_check.replace(' ', 'T')).getTime() : 0; + if (lastCheck && (nowTs - lastCheck > STALE_MS)) continue; + const consumed = Math.max(0, Math.round((s.initial_balance - s.last_balance) * 10000) / 10000); + const pct = s.initial_balance > 0 ? (consumed / s.initial_balance * 100) : 0; + const label = LABELS[name] || name; + const refreshTime = s.last_check ? s.last_check.slice(-8) : '--:--:--'; + const isLow = s.last_balance <= warnThreshold; + const balColor = isLow ? B_YELLOW : B_GREEN; + // Capture structured consumed data for HUD usage renderer + lastConsumed = consumed; + lastPct = pct; + lastRefreshTime = refreshTime; + lastIsLow = isLow; + parts.push( + B_BLUE + label + ' 余额' + B_RESET + ' ' + + balColor + '¥' + s.last_balance.toFixed(2) + B_RESET + ' ' + B_DIM + '|' + B_RESET + ' ' + + B_RED + '-¥' + consumed.toFixed(2) + B_RESET + ' ' + + B_MAGENTA + '(' + pct.toFixed(1) + '%)' + B_RESET + ' ' + + B_ORANGE + refreshTime + B_RESET + ); + // Low-balance warning banner + if (isLow) { + parts.push(B_RED + '⚠️ 余额仅剩 ¥' + s.last_balance.toFixed(2) + ',请及时充值!' + B_RESET); + } + } + const snapshot = { + updated_at: new Date().toISOString(), + five_hour: { used_percentage: null, resets_at: null }, + seven_day: { used_percentage: null, resets_at: null }, + ...(parts.length > 0 && { balance_label: parts.join(' ') }), + ...(lastConsumed != null && { balance_consumed: lastConsumed }), + ...(lastPct != null && { balance_pct: lastPct }), + ...(lastRefreshTime != null && { balance_time: lastRefreshTime }), + ...(lastIsLow && { balance_is_low: true }), + }; + writeFileSync(SNAPSHOT_FILE, JSON.stringify(snapshot, null, 2), 'utf-8'); + } catch { /* ignore */ } +} + +async function tick() { + for (const [name, key] of Object.entries(keys)) { + if (name === 'deepseek') { + const balance = await checkDeepSeek(key); + if (balance !== null) { + refreshState(name, balance); + } + } + } + writeBalanceSnapshot(); +} + +// First tick immediately, then loop +await tick(); + +// setInterval with async is fine — each tick is independent +setInterval(() => { tick().catch(() => {}); }, INTERVAL_MS); + +// Keep the process alive +process.stdin.resume(); diff --git a/plugins/balance-hud/scripts/balance_snapshot.mjs b/plugins/balance-hud/scripts/balance_snapshot.mjs new file mode 100644 index 0000000..bb35b78 --- /dev/null +++ b/plugins/balance-hud/scripts/balance_snapshot.mjs @@ -0,0 +1,83 @@ +#!/usr/bin/env node +/** + * Balance HUD v2.0.0 — External usage snapshot writer. + * + * Reads session_state.json and writes an external-usage-compatible JSON + * snapshot that the HUD engine (dist/index.js) reads via + * display.externalUsagePath config. + * + * This bridges the balance monitoring daemon (auto_refresh.mjs) with + * the claude-hud rendering engine, so balance info appears in the HUD. + * + * Usage: + * node balance_snapshot.mjs + * + * Called by auto_refresh.mjs after each balance refresh tick. + */ + +import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PLUGIN_DIR = resolve(__dirname, '..'); +const STATE_FILE = resolve(PLUGIN_DIR, 'session_state.json'); +const SNAPSHOT_FILE = resolve(PLUGIN_DIR, 'balance_usage.json'); + +const LABELS = { deepseek: 'DeepSeek', openai: 'OpenAI', anthropic: 'Anthropic' }; +const STALE_MS = 30 * 60 * 1000; + +function parseTs(ts) { + try { + const iso = ts.includes('T') ? ts : ts.replace(' ', 'T'); + return new Date(iso).getTime(); + } catch { return 0; } +} + +try { + if (!existsSync(STATE_FILE)) { + // No state yet — write empty snapshot + writeSnapshot(null); + process.exit(0); + } + + const state = JSON.parse(readFileSync(STATE_FILE, 'utf-8')); + const now = Date.now(); + const balanceParts = []; + + for (const [name, s] of Object.entries(state)) { + if (!s.last_balance || s.initial_balance == null) continue; + + const lastCheck = s.last_check ? parseTs(s.last_check) : 0; + if (lastCheck && (now - lastCheck > STALE_MS)) continue; + + const consumed = Math.max(0, Math.round((s.initial_balance - s.last_balance) * 10000) / 10000); + const pct = s.initial_balance > 0 ? (consumed / s.initial_balance * 100) : 0; + const label = LABELS[name] || name; + const currentBalance = s.last_balance.toFixed(2); + const refreshTime = s.last_check ? s.last_check.slice(-8) : '--:--:--'; + + balanceParts.push( + `${label} ¥${currentBalance} | -¥${consumed.toFixed(2)} (${pct.toFixed(1)}%) ${refreshTime}` + ); + } + + const balanceLabel = balanceParts.length > 0 ? balanceParts.join(' ') : null; + writeSnapshot(balanceLabel); +} catch { + // Silent — snapshot just won't be written +} + +function writeSnapshot(balanceLabel) { + const snapshot = { + updated_at: new Date().toISOString(), + five_hour: { used_percentage: null, resets_at: null }, + seven_day: { used_percentage: null, resets_at: null }, + ...(balanceLabel != null && { balance_label: balanceLabel }), + }; + + try { + mkdirSync(PLUGIN_DIR, { recursive: true }); + writeFileSync(SNAPSHOT_FILE, JSON.stringify(snapshot, null, 2), 'utf-8'); + } catch { /* ignore */ } +} diff --git a/plugins/balance-hud/scripts/hud_balance.mjs b/plugins/balance-hud/scripts/hud_balance.mjs new file mode 100644 index 0000000..a04069c --- /dev/null +++ b/plugins/balance-hud/scripts/hud_balance.mjs @@ -0,0 +1,94 @@ +#!/usr/bin/env node +/** + * Balance HUD v2.0.0 — Cache reader for claude-hud statusLine. + * + * Reads session_state.json (no HTTP, < 1ms) and outputs an ANSI-colored + * single-line balance display. Designed to be called every statusLine + * refresh cycle by claude-hud. + * + * Output format: + * DeepSeek 余额 ¥13.37 | -¥0.93 (6.5%) 20:34:27 + * + * Low balance (≤ warn threshold): yellow text + red warning banner. + * Threshold set via: node auto_refresh.mjs --warn + */ + +import { readFileSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const STATE_FILE = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'session_state.json'); + +// ANSI color constants +const BRIGHT_GREEN = '\x1b[92m'; // Balance text — normal (bright green) +const BRIGHT_YELLOW = '\x1b[93m'; // Balance text — low balance (bright yellow) +const RED = '\x1b[31m'; // Consumed amount text +const MAGENTA = '\x1b[95m'; // Percentage text +const BLUE = '\x1b[94m'; // Provider label (bright blue, matches claude-hud) +const ORANGE = '\x1b[38;5;208m'; // Refresh time (256-color orange) +const RESET = '\x1b[0m'; // Reset all ANSI attributes +const DIM = '\x1b[2m'; // Dim text (separator pipe) + +const LABELS = { deepseek: 'DeepSeek', openai: 'OpenAI', anthropic: 'Anthropic' }; +const STALE_MS = 30 * 60 * 1000; + +// ── Parse local-time timestamps ────────────────────────────── +function parseTs(ts) { + try { + const iso = ts.includes('T') ? ts : ts.replace(' ', 'T'); + return new Date(iso).getTime(); + } catch { return 0; } +} + +// ── Main ───────────────────────────────────────────────────── +try { + const state = JSON.parse(readFileSync(STATE_FILE, 'utf-8')); + const parts = []; + const now = Date.now(); + + for (const [name, s] of Object.entries(state)) { + if (!s.last_balance || s.initial_balance == null) continue; + + // Check staleness + const lastCheck = s.last_check ? parseTs(s.last_check) : 0; + if (lastCheck && (now - lastCheck > STALE_MS)) continue; + + const consumed = Math.max(0, Math.round((s.initial_balance - s.last_balance) * 10000) / 10000); + const pct = s.initial_balance > 0 ? (consumed / s.initial_balance * 100) : 0; + const label = LABELS[name] || name; + const sym = '¥'; + const currentBalance = s.last_balance.toFixed(2); + + // ── Low-balance threshold ────────────────────────────────── + // Default ≤ ¥5. Override via: node auto_refresh.mjs --warn + const warnThreshold = (state._warn_threshold != null) + ? parseFloat(state._warn_threshold) : 5.0; + const isLow = s.last_balance <= warnThreshold; + + // Color scheme: green (normal) → yellow (low balance) + const balanceColor = isLow ? BRIGHT_YELLOW : BRIGHT_GREEN; + + // Extract HH:MM:SS from last_check timestamp + const refreshTime = s.last_check ? s.last_check.slice(-8) : '--:--:--'; + + // "厂商 余额" blue label → balance number → consumed → pct → time + parts.push( + BLUE + label + ' 余额' + RESET + ' ' + + balanceColor + sym + currentBalance + RESET + ' ' + DIM + '|' + RESET + ' ' + + RED + '-' + sym + consumed.toFixed(2) + RESET + ' ' + + MAGENTA + '(' + pct.toFixed(1) + '%)' + RESET + ' ' + + ORANGE + refreshTime + RESET + ); + + // Low-balance warning banner + if (isLow) { + parts.push(RED + '⚠️ 余额仅剩 ' + sym + currentBalance + ',请及时充值!' + RESET); + } + } + + if (parts.length > 0) { + process.stdout.write(parts.join(' ') + '\n'); + } +} catch { + // No cache or invalid — silent, HUD just won't show balance +}