Reorganize: Move all skills to skills/ folder
- Created skills/ directory - Moved 272 skills to skills/ subfolder - Kept agents/ at root level - Kept installation scripts and docs at root level Repository structure: - skills/ - All 272 skills from skills.sh - agents/ - Agent definitions - *.sh, *.ps1 - Installation scripts - README.md, etc. - Documentation Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
138
skills/plugins/claude-hud/CHANGELOG.md
Normal file
138
skills/plugins/claude-hud/CHANGELOG.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to Claude HUD will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
---
|
||||
|
||||
## [0.0.6] - 2026-01-14
|
||||
|
||||
### Added
|
||||
- **Expanded multi-line layout mode** - splits the overloaded session line into semantic lines (#76)
|
||||
- Identity line: model, plan, context bar, duration
|
||||
- Project line: path, git status
|
||||
- Environment line: config counts (CLAUDE.md, rules, MCPs, hooks)
|
||||
- Usage line: rate limits with reset times
|
||||
- New config options:
|
||||
- `lineLayout`: `'compact'` | `'expanded'` (default: `'expanded'` for new users)
|
||||
- `showSeparators`: boolean (orthogonal to layout)
|
||||
- `display.usageThreshold`: show usage line only when >= N%
|
||||
- `display.environmentThreshold`: show env line only when counts >= N
|
||||
|
||||
### Changed
|
||||
- Default layout is now `expanded` for new installations
|
||||
- Threshold logic uses `max(5h, 7d)` to ensure high 7-day usage isn't hidden
|
||||
|
||||
### Fixed
|
||||
- Ghost installation detection and cleanup in setup command (#75)
|
||||
|
||||
### Migration
|
||||
- Existing configs with `layout: "default"` automatically migrate to `lineLayout: "compact"`
|
||||
- Existing configs with `layout: "separators"` migrate to `lineLayout: "compact"` + `showSeparators: true`
|
||||
|
||||
---
|
||||
|
||||
## [0.0.5] - 2026-01-14
|
||||
|
||||
### Added
|
||||
- Native context percentage support for Claude Code v2.1.6+
|
||||
- Uses `used_percentage` field from stdin when available (accurate, matches `/context`)
|
||||
- Automatic fallback to manual calculation for older versions
|
||||
- Handles edge cases: NaN, negative values, values >100
|
||||
- `display.autocompactBuffer` config option (`'enabled'` | `'disabled'`, default: `'enabled'`)
|
||||
- `'enabled'`: Shows buffered % (matches `/context` when autocompact ON) - **default**
|
||||
- `'disabled'`: Shows raw % (matches `/context` when autocompact OFF)
|
||||
- EXDEV cross-device error detection for Linux plugin installation (#53)
|
||||
|
||||
### Changed
|
||||
- Context percentage now uses percentage-based buffer (22.5%) instead of hardcoded 45k tokens (#55)
|
||||
- Scales correctly for enterprise context windows (>200k)
|
||||
- Remove automatic PR review workflow (#67)
|
||||
|
||||
### Fixed
|
||||
- Git status: move `--no-optional-locks` to correct position as global git option (#65)
|
||||
- Prevent stale `index.lock` files during git operations (#63)
|
||||
- Exclude disabled MCP servers from count (#47)
|
||||
- Reconvert Date objects when reading from usage API cache (#45)
|
||||
|
||||
### Credits
|
||||
- Ideas from [#30](https://github.com/jarrodwatts/claude-hud/pull/30) ([@r-firpo](https://github.com/r-firpo)), [#43](https://github.com/jarrodwatts/claude-hud/pull/43) ([@yansircc](https://github.com/yansircc)), [#49](https://github.com/jarrodwatts/claude-hud/pull/49) ([@StephenJoshii](https://github.com/StephenJoshii)) informed the autocompact solution
|
||||
|
||||
### Dependencies
|
||||
- Bump @types/node from 25.0.3 to 25.0.6 (#61)
|
||||
|
||||
---
|
||||
|
||||
## [0.0.4] - 2026-01-07
|
||||
|
||||
### Added
|
||||
- Configuration system via `~/.claude/plugins/claude-hud/config.json`
|
||||
- Interactive `/claude-hud:configure` skill for in-Claude configuration
|
||||
- Usage API integration showing 5h/7d rate limits (Pro/Max/Team)
|
||||
- Git status with dirty indicator and ahead/behind counts
|
||||
- Configurable path levels (1-3 directory segments)
|
||||
- Layout options: default and separators
|
||||
- Display toggles for all HUD elements
|
||||
|
||||
### Fixed
|
||||
- Git status spacing: `main*↑2↓1` → `main* ↑2 ↓1`
|
||||
- Root path rendering: show `/` instead of empty
|
||||
- Windows path normalization
|
||||
|
||||
### Credits
|
||||
- Config system, layouts, path levels, git toggle by @Tsopic (#32)
|
||||
- Usage API, configure skill, bug fixes by @melon-hub (#34)
|
||||
|
||||
---
|
||||
|
||||
## [0.0.3] - 2025-01-06
|
||||
|
||||
### Added
|
||||
- Display git branch name in session line (#23)
|
||||
- Display project folder name in session line (#18)
|
||||
- Dynamic platform and runtime detection in setup command (#24)
|
||||
|
||||
### Changed
|
||||
- Remove redundant COMPACT warning at high context usage (#27)
|
||||
|
||||
### Fixed
|
||||
- Skip auto-review for fork PRs to prevent CI failures (#25)
|
||||
|
||||
### Dependencies
|
||||
- Bump @types/node from 20.19.27 to 25.0.3 (#2)
|
||||
|
||||
---
|
||||
|
||||
## [0.0.2] - 2025-01-04
|
||||
|
||||
### Security
|
||||
- Add CI workflow to build dist/ after merge - closes attack vector where malicious code could be injected via compiled output in PRs
|
||||
- Remove dist/ from git tracking - PRs now contain source only, CI handles compilation
|
||||
|
||||
### Fixed
|
||||
- Add 45k token autocompact buffer to context percentage calculation - now matches `/context` output accurately by accounting for Claude Code's reserved autocompact space
|
||||
- Fix CI caching with package-lock.json
|
||||
- Use Opus 4.5 for GitHub Actions code review
|
||||
|
||||
### Changed
|
||||
- Setup command now auto-detects installed plugin version (no manual path updates needed)
|
||||
- Setup prompts for optional GitHub star after successful configuration
|
||||
- Remove husky pre-commit hook (CI now handles dist/ compilation)
|
||||
|
||||
### Dependencies
|
||||
- Bump c8 from 9.1.0 to 10.1.3
|
||||
|
||||
---
|
||||
|
||||
## [0.0.1] - 2025-01-04
|
||||
|
||||
Initial release of Claude HUD as a Claude Code statusline plugin.
|
||||
|
||||
### Features
|
||||
- Real-time context usage monitoring with color-coded progress bar
|
||||
- Active tool tracking with completion counts
|
||||
- Running agent status with elapsed time
|
||||
- Todo progress display
|
||||
- Native token data from Claude Code stdin
|
||||
- Transcript parsing for tool/agent/todo activity
|
||||
395
skills/plugins/claude-hud/CLAUDE.README.md
Normal file
395
skills/plugins/claude-hud/CLAUDE.README.md
Normal file
@@ -0,0 +1,395 @@
|
||||
# Claude HUD
|
||||
|
||||
Real-time statusline showing context usage, active tools, running agents, and todo progress.
|
||||
|
||||
---
|
||||
|
||||
## For Humans
|
||||
|
||||
**Step 1: Add the marketplace**
|
||||
```
|
||||
/plugin marketplace add jarrodwatts/claude-hud
|
||||
```
|
||||
|
||||
**Step 2: Install the plugin**
|
||||
```
|
||||
/plugin install claude-hud
|
||||
```
|
||||
|
||||
**Step 3: Configure the statusline**
|
||||
```
|
||||
/claude-hud:setup
|
||||
```
|
||||
|
||||
Done. The HUD appears immediately — no restart needed.
|
||||
|
||||
---
|
||||
|
||||
## For LLM Agents
|
||||
|
||||
If you're an LLM agent helping a user install or work with Claude HUD, follow these steps.
|
||||
|
||||
<agent_workflow>
|
||||
|
||||
### Step 1: Check Prerequisites
|
||||
|
||||
Verify Claude Code version is 1.0.80 or later:
|
||||
```bash
|
||||
claude --version
|
||||
```
|
||||
|
||||
If not installed or outdated, direct user to https://claude.ai/code
|
||||
|
||||
### Step 2: Add the Marketplace
|
||||
|
||||
Run this command in Claude Code:
|
||||
```
|
||||
/plugin marketplace add jarrodwatts/claude-hud
|
||||
```
|
||||
|
||||
### Step 3: Install the Plugin
|
||||
|
||||
Run this command in Claude Code:
|
||||
```
|
||||
/plugin install claude-hud
|
||||
```
|
||||
|
||||
### Step 4: Configure the Statusline
|
||||
|
||||
Run this command in Claude Code:
|
||||
```
|
||||
/claude-hud:setup
|
||||
```
|
||||
|
||||
This adds the statusLine configuration to the user's settings.json.
|
||||
|
||||
### Step 5: Inform User
|
||||
|
||||
Tell the user:
|
||||
- Installation complete
|
||||
- The statusline appears immediately — no restart needed
|
||||
- It shows: context usage, active tools, running agents, and todo progress
|
||||
|
||||
### Step 6: Ask About Starring (Optional)
|
||||
|
||||
Ask the user: "Would you like to star the repository to support the project?"
|
||||
|
||||
Only if they explicitly agree, run:
|
||||
```bash
|
||||
gh repo star jarrodwatts/claude-hud
|
||||
```
|
||||
|
||||
Never run this automatically without user consent.
|
||||
|
||||
</agent_workflow>
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
Technical documentation for agents who need to understand, modify, or debug Claude HUD.
|
||||
|
||||
<plugin>
|
||||
<name>Claude HUD</name>
|
||||
<description>Real-time statusline showing context usage, active tools, running agents, and todo progress. Always visible below your input, zero config required.</description>
|
||||
<repository>github.com/jarrodwatts/claude-hud</repository>
|
||||
<license>MIT</license>
|
||||
</plugin>
|
||||
|
||||
<requirements>
|
||||
<runtime>Node.js 18+ or Bun</runtime>
|
||||
<claude_code>v1.0.80 or later</claude_code>
|
||||
<build>TypeScript 5, ES2022 target, NodeNext modules</build>
|
||||
</requirements>
|
||||
|
||||
<architecture>
|
||||
<overview>
|
||||
Claude HUD is a statusline plugin invoked by Claude Code every ~300ms.
|
||||
It reads data from two sources, renders up to 4 lines, and outputs to stdout.
|
||||
</overview>
|
||||
|
||||
<data_flow>
|
||||
Claude Code invokes the plugin →
|
||||
Plugin reads JSON from stdin (model, context, tokens) →
|
||||
Plugin parses transcript JSONL file (tools, agents, todos) →
|
||||
Plugin reads config files (MCPs, hooks, rules) →
|
||||
Plugin renders lines to stdout →
|
||||
Claude Code displays the statusline
|
||||
</data_flow>
|
||||
|
||||
<data_sources>
|
||||
<stdin_json description="Native accurate data from Claude Code">
|
||||
<field path="model.display_name">Current model name (Opus, Sonnet, Haiku)</field>
|
||||
<field path="context_window.current_usage.input_tokens">Current token count</field>
|
||||
<field path="context_window.context_window_size">Maximum context size</field>
|
||||
<field path="transcript_path">Path to session transcript JSONL file</field>
|
||||
<field path="cwd">Current working directory</field>
|
||||
</stdin_json>
|
||||
|
||||
<transcript_jsonl description="Parsed from transcript file">
|
||||
<item>tool_use blocks → tool name, target file, start time</item>
|
||||
<item>tool_result blocks → completion status, duration</item>
|
||||
<item>Running tools = tool_use without matching tool_result</item>
|
||||
<item>TodoWrite calls → current todo list</item>
|
||||
<item>Task calls → agent type, model, description</item>
|
||||
</transcript_jsonl>
|
||||
|
||||
<config_files description="Read from Claude configuration">
|
||||
<item>~/.claude/settings.json → mcpServers count, hooks count</item>
|
||||
<item>CLAUDE.md files in cwd and ancestors → rules count</item>
|
||||
<item>.mcp.json files → additional MCP count</item>
|
||||
</config_files>
|
||||
</data_sources>
|
||||
</architecture>
|
||||
|
||||
<file_structure>
|
||||
<directory name="src">
|
||||
<file name="index.ts" purpose="Entry point, orchestrates data flow">
|
||||
Reads stdin, parses transcript, counts configs, calls render.
|
||||
Exports main() for testing with dependency injection.
|
||||
</file>
|
||||
<file name="stdin.ts" purpose="Parse JSON from stdin">
|
||||
Reads and validates Claude Code's JSON input.
|
||||
Returns StdinData with model, context, transcript_path.
|
||||
</file>
|
||||
<file name="transcript.ts" purpose="Parse transcript JSONL">
|
||||
Parses the session transcript file line by line.
|
||||
Extracts tools, agents, todos, and session start time.
|
||||
Matches tool_use to tool_result by ID to calculate status.
|
||||
</file>
|
||||
<file name="config-reader.ts" purpose="Count configuration items">
|
||||
Counts CLAUDE.md files, rules, MCP servers, and hooks.
|
||||
Searches cwd, ~/.claude/, and project .claude/ directories.
|
||||
</file>
|
||||
<file name="config.ts" purpose="Load and validate user configuration">
|
||||
Reads config.json from ~/.claude/plugins/claude-hud/.
|
||||
Validates and merges user settings with defaults.
|
||||
Exports HudConfig interface and loadConfig function.
|
||||
</file>
|
||||
<file name="git.ts" purpose="Git repository status">
|
||||
Gets branch name, dirty state, and ahead/behind counts.
|
||||
Uses execFile with array args for safe command execution.
|
||||
</file>
|
||||
<file name="usage-api.ts" purpose="Fetch usage from Anthropic API">
|
||||
Reads OAuth credentials from ~/.claude/.credentials.json.
|
||||
Calls api.anthropic.com/api/oauth/usage endpoint (opt-in).
|
||||
Caches results (60s success, 15s failure).
|
||||
</file>
|
||||
<file name="types.ts" purpose="TypeScript interfaces">
|
||||
StdinData, ToolEntry, AgentEntry, TodoItem, TranscriptData, RenderContext.
|
||||
</file>
|
||||
</directory>
|
||||
|
||||
<directory name="src/render">
|
||||
<file name="index.ts" purpose="Main render coordinator">
|
||||
Calls each line renderer and outputs to stdout.
|
||||
Conditionally shows lines based on data presence.
|
||||
</file>
|
||||
<file name="session-line.ts" purpose="Line 1: Session info">
|
||||
Renders: [Model | Plan] █████░░░░░ 45% | project git:(branch) | 2 CLAUDE.md | 5h: 25% | ⏱️ 5m
|
||||
Context bar colors: green (<70%), yellow (70-85%), red (>85%).
|
||||
</file>
|
||||
<file name="tools-line.ts" purpose="Line 2: Tool activity">
|
||||
Renders: ◐ Edit: auth.ts | ✓ Read ×3 | ✓ Grep ×2
|
||||
Shows running tools with spinner, completed tools aggregated.
|
||||
</file>
|
||||
<file name="agents-line.ts" purpose="Line 3: Agent status">
|
||||
Renders: ◐ explore [haiku]: Finding auth code (2m 15s)
|
||||
Shows agent type, model, description, elapsed time.
|
||||
</file>
|
||||
<file name="todos-line.ts" purpose="Line 4: Todo progress">
|
||||
Renders: ▸ Fix authentication bug (2/5)
|
||||
Shows current in_progress task and completion count.
|
||||
</file>
|
||||
<file name="colors.ts" purpose="ANSI color helpers">
|
||||
Functions: green(), yellow(), red(), dim(), bold(), reset().
|
||||
Used for colorizing output based on status/thresholds.
|
||||
</file>
|
||||
</directory>
|
||||
</file_structure>
|
||||
|
||||
<output_format>
|
||||
<line number="1" name="session" always_shown="true">
|
||||
[Model | Plan] █████░░░░░ 45% | project git:(branch) | 2 CLAUDE.md | 5h: 25% | ⏱️ 5m
|
||||
</line>
|
||||
<line number="2" name="tools" shown_if="any tools used">
|
||||
◐ Edit: auth.ts | ✓ Read ×3 | ✓ Grep ×2
|
||||
</line>
|
||||
<line number="3" name="agents" shown_if="agents active">
|
||||
◐ explore [haiku]: Finding auth code (2m 15s)
|
||||
</line>
|
||||
<line number="4" name="todos" shown_if="todos exist">
|
||||
▸ Fix authentication bug (2/5)
|
||||
</line>
|
||||
</output_format>
|
||||
|
||||
<context_thresholds>
|
||||
<threshold range="0-70%" color="green" meaning="Healthy" />
|
||||
<threshold range="70-85%" color="yellow" meaning="Warning" />
|
||||
<threshold range="85%+" color="red" meaning="Critical, shows token breakdown" />
|
||||
</context_thresholds>
|
||||
|
||||
<plugin_configuration>
|
||||
<manifest>.claude-plugin/plugin.json</manifest>
|
||||
<manifest_content>
|
||||
{
|
||||
"name": "claude-hud",
|
||||
"description": "Real-time statusline HUD for Claude Code",
|
||||
"version": "0.0.1",
|
||||
"author": { "name": "Jarrod Watts", "url": "https://github.com/jarrodwatts" }
|
||||
}
|
||||
</manifest_content>
|
||||
<note>The plugin.json contains metadata only. statusLine is NOT a valid plugin.json field.</note>
|
||||
|
||||
<statusline_config>
|
||||
The /claude-hud:setup command adds statusLine to ~/.claude/settings.json with an auto-updating command that finds the latest installed version.
|
||||
Updates are automatic - no need to re-run setup after updating the plugin.
|
||||
</statusline_config>
|
||||
</plugin_configuration>
|
||||
|
||||
<development>
|
||||
<setup>
|
||||
git clone https://github.com/jarrodwatts/claude-hud
|
||||
cd claude-hud
|
||||
npm ci
|
||||
npm run build
|
||||
</setup>
|
||||
|
||||
<test_commands>
|
||||
npm test # Run all tests
|
||||
npm run build # Compile TypeScript to dist/
|
||||
</test_commands>
|
||||
|
||||
<manual_testing>
|
||||
# Test with sample stdin data:
|
||||
echo '{"model":{"display_name":"Opus"},"context_window":{"current_usage":{"input_tokens":45000},"context_window_size":200000}}' | node dist/index.js
|
||||
|
||||
# Test with transcript path:
|
||||
echo '{"model":{"display_name":"Sonnet"},"transcript_path":"/path/to/transcript.jsonl","context_window":{"current_usage":{"input_tokens":90000},"context_window_size":200000}}' | node dist/index.js
|
||||
</manual_testing>
|
||||
</development>
|
||||
|
||||
<customization>
|
||||
<extending description="How to add new features">
|
||||
<step>Add new data extraction in transcript.ts or stdin.ts</step>
|
||||
<step>Add new interface fields in types.ts</step>
|
||||
<step>Create new render file in src/render/ or modify existing</step>
|
||||
<step>Update src/render/index.ts to include new line</step>
|
||||
<step>Run npm run build and test</step>
|
||||
</extending>
|
||||
|
||||
<modifying_thresholds>
|
||||
Edit src/render/session-line.ts to change context threshold values.
|
||||
Look for the percentage checks that determine color coding.
|
||||
</modifying_thresholds>
|
||||
|
||||
<adding_new_line>
|
||||
1. Create src/render/new-line.ts with a render function
|
||||
2. Import and call it from src/render/index.ts
|
||||
3. Add any needed types to src/types.ts
|
||||
4. Add data extraction logic to transcript.ts if needed
|
||||
</adding_new_line>
|
||||
</customization>
|
||||
|
||||
<troubleshooting>
|
||||
<issue name="Statusline not appearing">
|
||||
<cause>Plugin not installed or statusLine not configured</cause>
|
||||
<solution>Run: /plugin marketplace add jarrodwatts/claude-hud</solution>
|
||||
<solution>Run: /plugin install claude-hud</solution>
|
||||
<solution>Run: /claude-hud:setup</solution>
|
||||
<solution>Ensure Claude Code is v1.0.80 or later</solution>
|
||||
</issue>
|
||||
|
||||
<issue name="Shows [claude-hud] Initializing...">
|
||||
<cause>No stdin data received (normal on first invocation)</cause>
|
||||
<solution>This is expected briefly on startup, should resolve automatically</solution>
|
||||
</issue>
|
||||
|
||||
<issue name="Context percentage seems wrong">
|
||||
<cause>Data comes directly from Claude Code - it's accurate</cause>
|
||||
<solution>The percentage is (input_tokens / context_window_size) * 100</solution>
|
||||
</issue>
|
||||
|
||||
<issue name="Tools/agents not showing">
|
||||
<cause>No tools used yet or transcript not being parsed</cause>
|
||||
<solution>Lines only appear when there's data to show</solution>
|
||||
</issue>
|
||||
|
||||
<issue name="Build errors">
|
||||
<solution>Ensure Node.js 18+ installed</solution>
|
||||
<solution>Run npm ci to reinstall dependencies</solution>
|
||||
<solution>Delete dist/ and node_modules/, then npm ci && npm run build</solution>
|
||||
</issue>
|
||||
</troubleshooting>
|
||||
|
||||
<key_interfaces>
|
||||
<interface name="StdinData">
|
||||
transcript_path?: string
|
||||
cwd?: string
|
||||
model?: { id?: string, display_name?: string }
|
||||
context_window?: { context_window_size?: number, current_usage?: { input_tokens?: number } }
|
||||
</interface>
|
||||
|
||||
<interface name="ToolEntry">
|
||||
id: string
|
||||
name: string
|
||||
target?: string
|
||||
status: 'running' | 'completed' | 'error'
|
||||
startTime: Date
|
||||
endTime?: Date
|
||||
</interface>
|
||||
|
||||
<interface name="AgentEntry">
|
||||
id: string
|
||||
type: string
|
||||
model?: string
|
||||
description?: string
|
||||
status: 'running' | 'completed'
|
||||
startTime: Date
|
||||
endTime?: Date
|
||||
</interface>
|
||||
|
||||
<interface name="TodoItem">
|
||||
content: string
|
||||
status: 'pending' | 'in_progress' | 'completed'
|
||||
</interface>
|
||||
|
||||
<interface name="RenderContext">
|
||||
stdin: StdinData
|
||||
transcript: TranscriptData
|
||||
claudeMdCount: number
|
||||
rulesCount: number
|
||||
mcpCount: number
|
||||
hooksCount: number
|
||||
sessionDuration: string
|
||||
</interface>
|
||||
</key_interfaces>
|
||||
|
||||
<task_instructions>
|
||||
<install>
|
||||
Follow the agent_workflow steps above.
|
||||
</install>
|
||||
|
||||
<modify>
|
||||
If the user wants to modify Claude HUD:
|
||||
1. Clone the repo or navigate to the plugin directory
|
||||
2. Make changes following the file_structure section
|
||||
3. Run npm run build to compile
|
||||
4. Restart Claude Code to see changes
|
||||
</modify>
|
||||
|
||||
<debug>
|
||||
If the user reports issues:
|
||||
1. Check troubleshooting section first
|
||||
2. Verify Claude Code version (needs v1.0.80+)
|
||||
3. Check if plugin is listed: claude /plugin list
|
||||
4. Test manually with echo command from development section
|
||||
</debug>
|
||||
|
||||
<understand>
|
||||
If the user asks how something works:
|
||||
1. Reference the architecture and data_flow sections
|
||||
2. Point to specific files in file_structure
|
||||
3. Explain the data sources and how they're combined
|
||||
</understand>
|
||||
</task_instructions>
|
||||
119
skills/plugins/claude-hud/CLAUDE.md
Normal file
119
skills/plugins/claude-hud/CLAUDE.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code when working with this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Claude HUD is a Claude Code plugin that displays a real-time multi-line statusline. It shows context health, tool activity, agent status, and todo progress.
|
||||
|
||||
## Build Commands
|
||||
|
||||
```bash
|
||||
npm ci # Install dependencies
|
||||
npm run build # Build TypeScript to dist/
|
||||
|
||||
# Test with sample stdin data
|
||||
echo '{"model":{"display_name":"Opus"},"context_window":{"current_usage":{"input_tokens":45000},"context_window_size":200000}}' | node dist/index.js
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
Claude Code → stdin JSON → parse → render lines → stdout → Claude Code displays
|
||||
↘ transcript_path → parse JSONL → tools/agents/todos
|
||||
```
|
||||
|
||||
**Key insight**: The statusline is invoked every ~300ms by Claude Code. Each invocation:
|
||||
1. Receives JSON via stdin (model, context, tokens - native accurate data)
|
||||
2. Parses the transcript JSONL file for tools, agents, and todos
|
||||
3. Renders multi-line output to stdout
|
||||
4. Claude Code displays all lines
|
||||
|
||||
### Data Sources
|
||||
|
||||
**Native from stdin JSON** (accurate, no estimation):
|
||||
- `model.display_name` - Current model
|
||||
- `context_window.current_usage` - Token counts
|
||||
- `context_window.context_window_size` - Max context
|
||||
- `transcript_path` - Path to session transcript
|
||||
|
||||
**From transcript JSONL parsing**:
|
||||
- `tool_use` blocks → tool name, input, start time
|
||||
- `tool_result` blocks → completion, duration
|
||||
- Running tools = `tool_use` without matching `tool_result`
|
||||
- `TodoWrite` calls → todo list
|
||||
- `Task` calls → agent info
|
||||
|
||||
**From config files**:
|
||||
- MCP count from `~/.claude/settings.json` (mcpServers)
|
||||
- Hooks count from `~/.claude/settings.json` (hooks)
|
||||
- Rules count from CLAUDE.md files
|
||||
|
||||
**From OAuth credentials** (`~/.claude/.credentials.json`, when `display.showUsage` enabled):
|
||||
- `claudeAiOauth.accessToken` - OAuth token for API calls
|
||||
- `claudeAiOauth.subscriptionType` - User's plan (Pro, Max, Team)
|
||||
|
||||
**From Anthropic Usage API** (`api.anthropic.com/api/oauth/usage`):
|
||||
- 5-hour and 7-day usage percentages
|
||||
- Reset timestamps (cached 60s success, 15s failure)
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── index.ts # Entry point
|
||||
├── stdin.ts # Parse Claude's JSON input
|
||||
├── transcript.ts # Parse transcript JSONL
|
||||
├── config-reader.ts # Read MCP/rules configs
|
||||
├── config.ts # Load/validate user config
|
||||
├── git.ts # Git status (branch, dirty, ahead/behind)
|
||||
├── usage-api.ts # Fetch usage from Anthropic API
|
||||
├── types.ts # TypeScript interfaces
|
||||
└── render/
|
||||
├── index.ts # Main render coordinator
|
||||
├── session-line.ts # Line 1: model, context, project, git, usage
|
||||
├── tools-line.ts # Line 2: tool activity
|
||||
├── agents-line.ts # Line 3: agent status
|
||||
├── todos-line.ts # Line 4: todo progress
|
||||
└── colors.ts # ANSI color helpers
|
||||
```
|
||||
|
||||
### Output Format
|
||||
|
||||
```
|
||||
[Opus | Pro] █████░░░░░ 45% | my-project git:(main) | 2 CLAUDE.md | 5h: 25% | ⏱️ 5m
|
||||
◐ Edit: auth.ts | ✓ Read ×3 | ✓ Grep ×2
|
||||
◐ explore [haiku]: Finding auth code (2m 15s)
|
||||
▸ Fix authentication bug (2/5)
|
||||
```
|
||||
|
||||
Lines are conditionally shown:
|
||||
- Line 1 (session): Always shown
|
||||
- Line 2 (tools): Shown if any tools used
|
||||
- Line 3 (agents): Shown only if agents active
|
||||
- Line 4 (todos): Shown only if todos exist
|
||||
|
||||
### Context Thresholds
|
||||
|
||||
| Threshold | Color | Action |
|
||||
|-----------|-------|--------|
|
||||
| <70% | Green | Normal |
|
||||
| 70-85% | Yellow | Warning |
|
||||
| >85% | Red | Show token breakdown |
|
||||
|
||||
## Plugin Configuration
|
||||
|
||||
The plugin manifest is in `.claude-plugin/plugin.json` (metadata only - name, description, version, author).
|
||||
|
||||
**StatusLine configuration** must be added to the user's `~/.claude/settings.json` via `/claude-hud:setup`.
|
||||
|
||||
The setup command adds an auto-updating command that finds the latest installed version at runtime.
|
||||
|
||||
Note: `statusLine` is NOT a valid plugin.json field. It must be configured in settings.json after plugin installation. Updates are automatic - no need to re-run setup.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Runtime**: Node.js 18+ or Bun
|
||||
- **Build**: TypeScript 5, ES2022 target, NodeNext modules
|
||||
31
skills/plugins/claude-hud/CODE_OF_CONDUCT.md
Normal file
31
skills/plugins/claude-hud/CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment include:
|
||||
|
||||
- Being respectful and considerate
|
||||
- Using welcoming and inclusive language
|
||||
- Accepting constructive feedback
|
||||
- Focusing on what is best for the community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- Harassment or discrimination
|
||||
- Trolling, insulting, or derogatory comments
|
||||
- Publishing others' private information without permission
|
||||
|
||||
## Enforcement
|
||||
|
||||
Community leaders are responsible for clarifying standards of acceptable behavior and may take appropriate action in response to unacceptable behavior.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the maintainer at: jarrodwttsyt@gmail.com.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 2.1.
|
||||
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
75
skills/plugins/claude-hud/CONTRIBUTING.md
Normal file
75
skills/plugins/claude-hud/CONTRIBUTING.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for contributing to Claude HUD. This repo is small and fast-moving, so we optimize for clarity and quick review.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1) Fork and clone the repo
|
||||
2) Create a branch
|
||||
3) Make your changes
|
||||
4) Run tests and update docs if needed
|
||||
5) Open a pull request
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
npm run build
|
||||
npm test
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
See `TESTING.md` for the full testing strategy, fixtures, and snapshot updates.
|
||||
|
||||
## Code Style
|
||||
|
||||
- Keep changes focused and small.
|
||||
- Prefer tests for behavior changes.
|
||||
- Avoid introducing dependencies unless necessary.
|
||||
|
||||
## Build Process
|
||||
|
||||
**Important**: PRs should only modify files in `src/` — do not include changes to `dist/`.
|
||||
|
||||
CI automatically builds and commits `dist/` after your PR is merged. This keeps PRs focused on source code and makes review easier.
|
||||
|
||||
```
|
||||
Your PR: src/ changes only → Merge → CI builds dist/ → Committed automatically
|
||||
```
|
||||
|
||||
## Pull Requests
|
||||
|
||||
- Describe the problem and the fix.
|
||||
- Include tests or explain why they are not needed.
|
||||
- Link issues when relevant.
|
||||
- Only modify `src/` files — CI handles `dist/` automatically.
|
||||
|
||||
## Releasing New Versions
|
||||
|
||||
When shipping a new version:
|
||||
|
||||
1. **Update version numbers** in all three files:
|
||||
- `package.json` → `"version": "X.Y.Z"`
|
||||
- `.claude-plugin/plugin.json` → `"version": "X.Y.Z"`
|
||||
- `.claude-plugin/marketplace.json` → `"version": "X.Y.Z"`
|
||||
|
||||
2. **Update CHANGELOG.md** with changes since last release
|
||||
|
||||
3. **Commit and merge** — CI builds dist/ automatically
|
||||
|
||||
### How Users Get Updates
|
||||
|
||||
Claude Code plugins support updates through the `/plugin` interface:
|
||||
|
||||
- **Update now** — Fetches latest from main branch, installs immediately
|
||||
- **Mark for update** — Stages update for later
|
||||
|
||||
Claude Code compares the `version` field in `plugin.json` against the installed version. Bumping the version number (e.g., 0.0.1 → 0.0.2) allows users to see an update is available.
|
||||
|
||||
### Version Strategy
|
||||
|
||||
We use semantic versioning (`MAJOR.MINOR.PATCH`):
|
||||
- **PATCH** (0.0.x): Bug fixes, minor improvements
|
||||
- **MINOR** (0.x.0): New features, non-breaking changes
|
||||
- **MAJOR** (x.0.0): Breaking changes
|
||||
21
skills/plugins/claude-hud/LICENSE
Normal file
21
skills/plugins/claude-hud/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Jarrod Watts
|
||||
|
||||
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.
|
||||
5
skills/plugins/claude-hud/MAINTAINERS.md
Normal file
5
skills/plugins/claude-hud/MAINTAINERS.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Maintainers
|
||||
|
||||
- Jarrod Watts (https://github.com/jarrodwatts)
|
||||
|
||||
If you are interested in becoming a maintainer, open an issue to start the conversation.
|
||||
291
skills/plugins/claude-hud/README.md
Normal file
291
skills/plugins/claude-hud/README.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# Claude HUD
|
||||
|
||||
A Claude Code plugin that shows what's happening — context usage, active tools, running agents, and todo progress. Always visible below your input.
|
||||
|
||||
[](LICENSE)
|
||||
[](https://github.com/jarrodwatts/claude-hud/stargazers)
|
||||
|
||||

|
||||
|
||||
## Install
|
||||
|
||||
Inside a Claude Code instance, run the following commands:
|
||||
|
||||
**Step 1: Add the marketplace**
|
||||
```
|
||||
/plugin marketplace add jarrodwatts/claude-hud
|
||||
```
|
||||
|
||||
**Step 2: Install the plugin**
|
||||
|
||||
<details>
|
||||
<summary><strong>⚠️ Linux users: Click here first</strong></summary>
|
||||
|
||||
On Linux, `/tmp` is often a separate filesystem (tmpfs), which causes plugin installation to fail with:
|
||||
```
|
||||
EXDEV: cross-device link not permitted
|
||||
```
|
||||
|
||||
**Fix**: Set TMPDIR before installing:
|
||||
```bash
|
||||
mkdir -p ~/.cache/tmp && TMPDIR=~/.cache/tmp claude
|
||||
```
|
||||
|
||||
Then run the install command below in that session. This is a [Claude Code platform limitation](https://github.com/anthropics/claude-code/issues/14799).
|
||||
|
||||
</details>
|
||||
|
||||
```
|
||||
/plugin install claude-hud
|
||||
```
|
||||
|
||||
**Step 3: Configure the statusline**
|
||||
```
|
||||
/claude-hud:setup
|
||||
```
|
||||
|
||||
Done! The HUD appears immediately — no restart needed.
|
||||
|
||||
---
|
||||
|
||||
## What is Claude HUD?
|
||||
|
||||
Claude HUD gives you better insights into what's happening in your Claude Code session.
|
||||
|
||||
| What You See | Why It Matters |
|
||||
|--------------|----------------|
|
||||
| **Project path** | Know which project you're in (configurable 1-3 directory levels) |
|
||||
| **Context health** | Know exactly how full your context window is before it's too late |
|
||||
| **Tool activity** | Watch Claude read, edit, and search files as it happens |
|
||||
| **Agent tracking** | See which subagents are running and what they're doing |
|
||||
| **Todo progress** | Track task completion in real-time |
|
||||
|
||||
## What Each Line Shows
|
||||
|
||||
### Session Info
|
||||
```
|
||||
[Opus | Pro] █████░░░░░ 45% | my-project git:(main) | 2 CLAUDE.md | 5h: 25% | ⏱️ 5m
|
||||
```
|
||||
- **Model** — Current model in use (shown first)
|
||||
- **Plan name** — Your subscription tier (Pro, Max, Team) when usage enabled
|
||||
- **Context bar** — Visual meter with color coding (green → yellow → red as it fills)
|
||||
- **Project path** — Configurable 1-3 directory levels (default: 1)
|
||||
- **Git branch** — Current branch name (configurable on/off)
|
||||
- **Config counts** — CLAUDE.md files, rules, MCPs, and hooks loaded
|
||||
- **Usage limits** — 5-hour rate limit percentage (opt-in, Pro/Max/Team only)
|
||||
- **Duration** — How long the session has been running
|
||||
|
||||
### Tool Activity
|
||||
```
|
||||
✓ TaskOutput ×2 | ✓ mcp_context7 ×1 | ✓ Glob ×1 | ✓ Skill ×1
|
||||
```
|
||||
- **Running tools** show a spinner with the target file
|
||||
- **Completed tools** aggregate by type with counts
|
||||
|
||||
### Agent Status
|
||||
```
|
||||
✓ Explore: Explore home directory structure (5s)
|
||||
✓ open-source-librarian: Research React hooks patterns (2s)
|
||||
```
|
||||
- **Agent type** and what it's working on
|
||||
- **Elapsed time** for each agent
|
||||
|
||||
### Todo Progress
|
||||
```
|
||||
✓ All todos complete (5/5)
|
||||
```
|
||||
- **Current task** or completion status
|
||||
- **Progress counter** (completed/total)
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
Claude HUD uses Claude Code's native **statusline API** — no separate window, no tmux required, works in any terminal.
|
||||
|
||||
```
|
||||
Claude Code → stdin JSON → claude-hud → stdout → displayed in your terminal
|
||||
↘ transcript JSONL (tools, agents, todos)
|
||||
```
|
||||
|
||||
**Key features:**
|
||||
- Native token data from Claude Code (not estimated)
|
||||
- Parses the transcript for tool/agent activity
|
||||
- Updates every ~300ms
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Customize your HUD anytime:
|
||||
|
||||
```
|
||||
/claude-hud:configure
|
||||
```
|
||||
|
||||
The guided flow walks you through customization — no manual editing needed:
|
||||
|
||||
- **First time setup**: Choose a preset (Full/Essential/Minimal), then fine-tune individual elements
|
||||
- **Customize anytime**: Toggle items on/off, adjust git display style, switch layouts
|
||||
- **Preview before saving**: See exactly how your HUD will look before committing changes
|
||||
|
||||
### Presets
|
||||
|
||||
| Preset | What's Shown |
|
||||
|--------|--------------|
|
||||
| **Full** | Everything enabled — tools, agents, todos, git, usage, duration |
|
||||
| **Essential** | Activity lines + git status, minimal info clutter |
|
||||
| **Minimal** | Core only — just model name and context bar |
|
||||
|
||||
After choosing a preset, you can turn individual elements on or off.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
You can also edit the config file directly at `~/.claude/plugins/claude-hud/config.json`.
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `layout` | string | `default` | Layout style: `default` or `separators` |
|
||||
| `pathLevels` | 1-3 | 1 | Directory levels to show in project path |
|
||||
| `gitStatus.enabled` | boolean | true | Show git branch in HUD |
|
||||
| `gitStatus.showDirty` | boolean | true | Show `*` for uncommitted changes |
|
||||
| `gitStatus.showAheadBehind` | boolean | false | Show `↑N ↓N` for ahead/behind remote |
|
||||
| `gitStatus.showFileStats` | boolean | false | Show file change counts `!M +A ✘D ?U` |
|
||||
| `display.showModel` | boolean | true | Show model name `[Opus]` |
|
||||
| `display.showContextBar` | boolean | true | Show visual context bar `████░░░░░░` |
|
||||
| `display.showConfigCounts` | boolean | true | Show CLAUDE.md, rules, MCPs, hooks counts |
|
||||
| `display.showDuration` | boolean | true | Show session duration `⏱️ 5m` |
|
||||
| `display.showUsage` | boolean | true | Show usage limits (Pro/Max/Team only) |
|
||||
| `display.showTokenBreakdown` | boolean | true | Show token details at high context (85%+) |
|
||||
| `display.showTools` | boolean | true | Show tools activity line |
|
||||
| `display.showAgents` | boolean | true | Show agents activity line |
|
||||
| `display.showTodos` | boolean | true | Show todos progress line |
|
||||
|
||||
### Usage Limits (Pro/Max/Team)
|
||||
|
||||
Usage display is **enabled by default** for Claude Pro, Max, and Team subscribers. It shows your rate limit consumption directly in the HUD.
|
||||
|
||||
When enabled, you'll see your 5-hour usage percentage. The 7-day percentage appears when above 80%:
|
||||
|
||||
```
|
||||
[Opus | Pro] █████░░░░░ 45% | my-project | 5h: 25% | 7d: 85%
|
||||
```
|
||||
|
||||
To disable usage display, set `display.showUsage` to `false` in your config.
|
||||
|
||||
**Requirements:**
|
||||
- Claude Pro, Max, or Team subscription (not available for API users)
|
||||
- OAuth credentials from Claude Code (created automatically when you log in)
|
||||
|
||||
**Troubleshooting:** If usage doesn't appear:
|
||||
- Ensure you're logged in with a Pro/Max/Team account (not API key)
|
||||
- Check `display.showUsage` is not set to `false` in config
|
||||
- API users see no usage display (they have pay-per-token, not rate limits)
|
||||
|
||||
### Layout Options
|
||||
|
||||
**Default layout** — All info on first line:
|
||||
```
|
||||
[Opus] ████░░░░░░ 42% | my-project git:(main) | 2 rules | ⏱️ 5m
|
||||
✓ Read ×3 | ✓ Edit ×1
|
||||
```
|
||||
|
||||
**Separators layout** — Visual separator below header when activity exists:
|
||||
```
|
||||
[Opus] ████░░░░░░ 42% | my-project git:(main) | 2 rules | ⏱️ 5m
|
||||
──────────────────────────────────────────────────────────────
|
||||
✓ Read ×3 | ✓ Edit ×1
|
||||
```
|
||||
|
||||
### Example Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"layout": "default",
|
||||
"pathLevels": 2,
|
||||
"gitStatus": {
|
||||
"enabled": true,
|
||||
"showDirty": true,
|
||||
"showAheadBehind": true,
|
||||
"showFileStats": true
|
||||
},
|
||||
"display": {
|
||||
"showModel": true,
|
||||
"showContextBar": true,
|
||||
"showConfigCounts": true,
|
||||
"showDuration": true,
|
||||
"showUsage": true,
|
||||
"showTokenBreakdown": true,
|
||||
"showTools": true,
|
||||
"showAgents": true,
|
||||
"showTodos": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Display Examples
|
||||
|
||||
**1 level (default):** `[Opus] 45% | my-project git:(main) | ...`
|
||||
|
||||
**2 levels:** `[Opus] 45% | apps/my-project git:(main) | ...`
|
||||
|
||||
**3 levels:** `[Opus] 45% | dev/apps/my-project git:(main) | ...`
|
||||
|
||||
**With dirty indicator:** `[Opus] 45% | my-project git:(main*) | ...`
|
||||
|
||||
**With ahead/behind:** `[Opus] 45% | my-project git:(main ↑2 ↓1) | ...`
|
||||
|
||||
**With file stats:** `[Opus] 45% | my-project git:(main* !3 +1 ?2) | ...`
|
||||
- `!` = modified files, `+` = added/staged, `✘` = deleted, `?` = untracked
|
||||
- Counts of 0 are omitted for cleaner display
|
||||
|
||||
**Minimal display (only context %):** Configure `showModel`, `showContextBar`, `showConfigCounts`, `showDuration` to `false`
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Config not applying?**
|
||||
- Check for JSON syntax errors: invalid JSON silently falls back to defaults
|
||||
- Ensure valid values: `pathLevels` must be 1, 2, or 3; `layout` must be `default` or `separators`
|
||||
- Delete config and run `/claude-hud:configure` to regenerate
|
||||
|
||||
**Git status missing?**
|
||||
- Verify you're in a git repository
|
||||
- Check `gitStatus.enabled` is not `false` in config
|
||||
|
||||
**Tool/agent/todo lines missing?**
|
||||
- These only appear when there's activity to show
|
||||
- Check `display.showTools`, `display.showAgents`, `display.showTodos` in config
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- Claude Code v1.0.80+
|
||||
- Node.js 18+ or Bun
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
git clone https://github.com/jarrodwatts/claude-hud
|
||||
cd claude-hud
|
||||
npm ci && npm run build
|
||||
npm test
|
||||
```
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE)
|
||||
|
||||
---
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#jarrodwatts/claude-hud&Date)
|
||||
24
skills/plugins/claude-hud/RELEASING.md
Normal file
24
skills/plugins/claude-hud/RELEASING.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Releasing
|
||||
|
||||
This project ships as a Claude Code plugin. Releases should include compiled `dist/` output.
|
||||
|
||||
## Release Checklist
|
||||
|
||||
1) Update versions:
|
||||
- `package.json`
|
||||
- `.claude-plugin/plugin.json`
|
||||
- `CHANGELOG.md`
|
||||
2) Build:
|
||||
```bash
|
||||
npm ci
|
||||
npm run build
|
||||
npm test
|
||||
npm run test:coverage
|
||||
```
|
||||
3) Verify plugin entrypoint:
|
||||
- `.claude-plugin/plugin.json` points to `dist/index.js`
|
||||
4) Commit and tag:
|
||||
- `git tag vX.Y.Z`
|
||||
5) Publish:
|
||||
- Push tag
|
||||
- Create GitHub release with notes from `CHANGELOG.md`
|
||||
12
skills/plugins/claude-hud/SECURITY.md
Normal file
12
skills/plugins/claude-hud/SECURITY.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security fixes are applied to the latest release series only.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues to: jarrodwttsyt@gmail.com
|
||||
|
||||
Include a clear description, reproduction steps, and any relevant logs or screenshots.
|
||||
We will acknowledge receipt within 5 business days and provide a timeline for a fix if applicable.
|
||||
16
skills/plugins/claude-hud/SUPPORT.md
Normal file
16
skills/plugins/claude-hud/SUPPORT.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Support Policy
|
||||
|
||||
This project is maintained on a best-effort basis.
|
||||
|
||||
## What We Support
|
||||
|
||||
- The latest release
|
||||
- Claude Code versions documented in `README.md`
|
||||
- Node.js 18+ or Bun
|
||||
|
||||
## How to Get Help
|
||||
|
||||
- Open a GitHub issue for bugs or feature requests
|
||||
- For security issues, see `SECURITY.md`
|
||||
|
||||
We cannot guarantee response times, but we will triage issues as time allows.
|
||||
73
skills/plugins/claude-hud/TESTING.md
Normal file
73
skills/plugins/claude-hud/TESTING.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Testing Strategy
|
||||
|
||||
This project is small, runs in a terminal, and is mostly deterministic. The testing strategy focuses on fast, reliable checks that validate core behavior and provide a safe merge gate for PRs.
|
||||
|
||||
## Goals
|
||||
|
||||
- Validate core logic (parsing, aggregation, formatting) deterministically.
|
||||
- Catch regressions in the HUD output without relying on manual review.
|
||||
- Keep test execution fast (<5s) to support frequent contributor runs.
|
||||
|
||||
## Test Layers
|
||||
|
||||
1) Unit tests (fast, deterministic)
|
||||
- Pure helpers: `getContextPercent`, `getModelName`, token/elapsed formatting.
|
||||
- Render helpers: string assembly and truncation behavior.
|
||||
- Transcript parsing: tool/agent/todo aggregation and session start detection.
|
||||
|
||||
2) Integration tests (CLI behavior)
|
||||
- Run the CLI with a sample stdin JSON and a fixture transcript.
|
||||
- Validate that the rendered output contains expected markers (model, percent, tool names).
|
||||
- Keep assertions resilient to minor formatting changes (avoid strict full-line matching).
|
||||
|
||||
3) Golden-output tests (near-term)
|
||||
- For known fixtures, compare the full output snapshot to catch subtle UI regressions.
|
||||
- Update snapshots only when intentional output changes are made.
|
||||
|
||||
## What to Test First
|
||||
|
||||
- Transcript parsing (tool use/result mapping, todo extraction).
|
||||
- Context percent calculation (including cache tokens).
|
||||
- Truncation and aggregation (tools/todos/agents display logic).
|
||||
- Malformed or partial input (bad JSON lines, missing fields).
|
||||
|
||||
## Fixtures
|
||||
|
||||
- Keep shared test data under `tests/fixtures/`.
|
||||
- Use small JSONL files that capture one behavior each (e.g., basic tool flow, agent lifecycle, todo updates).
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
This runs `npm run build` and then executes Node's built-in test runner.
|
||||
|
||||
To generate coverage:
|
||||
|
||||
```bash
|
||||
npm run test:coverage
|
||||
```
|
||||
|
||||
To update snapshots:
|
||||
|
||||
```bash
|
||||
npm run test:update-snapshots
|
||||
```
|
||||
|
||||
## CI Gate (recommended)
|
||||
|
||||
- `npm ci`
|
||||
- `npm run build`
|
||||
- `npm test`
|
||||
|
||||
The provided GitHub Actions workflow runs `npm run test:coverage` on Node 18 and 20.
|
||||
|
||||
These steps should be required in PR checks to ensure new changes do not regress existing behavior.
|
||||
|
||||
## Contributing Expectations
|
||||
|
||||
- Add or update tests for behavior changes.
|
||||
- Prefer unit tests for new helpers and integration tests for user-visible output changes.
|
||||
- Keep tests deterministic and avoid time-dependent assertions unless controlled.
|
||||
BIN
skills/plugins/claude-hud/claude-hud-preview-16-9.png
Normal file
BIN
skills/plugins/claude-hud/claude-hud-preview-16-9.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
BIN
skills/plugins/claude-hud/claude-hud-preview-5-2.png
Normal file
BIN
skills/plugins/claude-hud/claude-hud-preview-5-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 119 KiB |
256
skills/plugins/claude-hud/commands/configure.md
Normal file
256
skills/plugins/claude-hud/commands/configure.md
Normal file
@@ -0,0 +1,256 @@
|
||||
---
|
||||
description: Configure HUD display options (layout, presets, display elements)
|
||||
allowed-tools: Read, Write, AskUserQuestion
|
||||
---
|
||||
|
||||
# Configure Claude HUD
|
||||
|
||||
**FIRST**: Use the Read tool to load `~/.claude/plugins/claude-hud/config.json` if it exists.
|
||||
|
||||
Store current values and note whether config exists (determines which flow to use).
|
||||
|
||||
## Always On (Core Features)
|
||||
|
||||
These are always enabled and NOT configurable:
|
||||
- Model name `[Opus]`
|
||||
- Context bar `████░░░░░░ 45%`
|
||||
|
||||
---
|
||||
|
||||
## Two Flows Based on Config State
|
||||
|
||||
### Flow A: New User (no config)
|
||||
Questions: **Layout → Preset → Turn Off → Turn On**
|
||||
|
||||
### Flow B: Update Config (config exists)
|
||||
Questions: **Turn Off → Turn On → Git Style → Layout/Reset**
|
||||
|
||||
---
|
||||
|
||||
## Flow A: New User (4 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: 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)
|
||||
- "Git status" - git:(main*) branch indicator
|
||||
- "Config counts" - 2 CLAUDE.md | 4 rules
|
||||
- "Token breakdown" - (in: 45k, cache: 12k)
|
||||
- "Usage limits" - 5h: 25% | 7d: 10%
|
||||
- "Session duration" - ⏱️ 5m
|
||||
|
||||
### Q4: 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), Q4 shows "Nothing to enable - Full preset has everything!"
|
||||
If preset has all items OFF (Minimal), Q3 shows "Nothing to disable - Minimal preset is already minimal!"
|
||||
|
||||
---
|
||||
|
||||
## Flow B: Update Config (4 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)
|
||||
- "Git status" - git:(main*) branch indicator
|
||||
|
||||
If more than 4 items ON, show Activity items (Tools, Agents, Todos, Git) first.
|
||||
Info items (Counts, Tokens, Usage, 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)
|
||||
- "Usage limits" - 5h: 25% | 7d: 10%
|
||||
- "Session duration" - ⏱️ 5m
|
||||
|
||||
### 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** - show only 3 questions total, or replace with placeholder.
|
||||
|
||||
### 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
|
||||
|
||||
---
|
||||
|
||||
## Preset Definitions
|
||||
|
||||
**Full** (everything ON):
|
||||
- Activity: Tools ON, Agents ON, Todos ON
|
||||
- Info: Counts ON, Tokens ON, Usage ON, Duration 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
|
||||
- Git: ON (with dirty indicator)
|
||||
|
||||
**Minimal** (core only):
|
||||
- Activity: Tools OFF, Agents OFF, Todos OFF
|
||||
- Info: Counts OFF, Tokens OFF, Usage OFF, Duration OFF
|
||||
- Git: OFF
|
||||
|
||||
---
|
||||
|
||||
## Layout Mapping
|
||||
|
||||
| Option | Config |
|
||||
|--------|--------|
|
||||
| Expanded | `lineLayout: "expanded", showSeparators: false` |
|
||||
| Compact | `lineLayout: "compact", showSeparators: false` |
|
||||
| Compact + Separators | `lineLayout: "compact", showSeparators: true` |
|
||||
|
||||
---
|
||||
|
||||
## 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 |
|
||||
|---------|------------|
|
||||
| Tools activity | `display.showTools` |
|
||||
| Agents status | `display.showAgents` |
|
||||
| Todo progress | `display.showTodos` |
|
||||
| Git status | `gitStatus.enabled` |
|
||||
| Config counts | `display.showConfigCounts` |
|
||||
| Token breakdown | `display.showTokenBreakdown` |
|
||||
| Usage limits | `display.showUsage` |
|
||||
| Session duration | `display.showDuration` |
|
||||
|
||||
**Always true (not configurable):**
|
||||
- `display.showModel: true`
|
||||
- `display.showContextBar: true`
|
||||
|
||||
---
|
||||
|
||||
## Processing Logic
|
||||
|
||||
### For New Users (Flow A):
|
||||
1. Apply chosen preset as base
|
||||
2. Apply Turn Off selections (set those items to OFF)
|
||||
3. Apply Turn On selections (set those items to ON)
|
||||
4. Apply chosen layout
|
||||
|
||||
### For Returning Users (Flow B):
|
||||
1. Start from current config
|
||||
2. Apply Turn Off selections (set to OFF)
|
||||
3. Apply Turn On selections (set to ON)
|
||||
4. Apply Git Style selection (if shown)
|
||||
5. If "Reset to [preset]" selected, override with preset values
|
||||
6. If layout 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
|
||||
Git style: Branch + dirty
|
||||
Changes:
|
||||
- Usage limits: OFF → ON
|
||||
- Config counts: ON → OFF
|
||||
```
|
||||
|
||||
2. **Preview of HUD (Expanded layout):**
|
||||
```
|
||||
[Opus | Pro] ████░░░░░ 45% | ⏱️ 5m
|
||||
my-project git:(main*)
|
||||
2 CLAUDE.md | 4 rules | 3 MCPs
|
||||
5h: 25% (1h 30m)
|
||||
◐ Edit: file.ts | ✓ Read ×3
|
||||
▸ Fix auth bug (2/5)
|
||||
```
|
||||
|
||||
**Preview of HUD (Compact layout):**
|
||||
```
|
||||
[Opus | Pro] ████░░░░░ 45% | my-project git:(main*) | 2 CLAUDE.md | 5h: 25% | ⏱️ 5m
|
||||
◐ Edit: file.ts | ✓ Read ×3
|
||||
▸ Fix auth bug (2/5)
|
||||
```
|
||||
|
||||
3. **Confirm**: "Save these changes?"
|
||||
|
||||
---
|
||||
|
||||
## Write Configuration
|
||||
|
||||
Write to `~/.claude/plugins/claude-hud/config.json`.
|
||||
|
||||
Merge with existing config, preserving:
|
||||
- `pathLevels` (not in configure flow)
|
||||
- `display.usageThreshold` (advanced config)
|
||||
- `display.environmentThreshold` (advanced config)
|
||||
|
||||
**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."
|
||||
226
skills/plugins/claude-hud/commands/setup.md
Normal file
226
skills/plugins/claude-hud/commands/setup.md
Normal file
@@ -0,0 +1,226 @@
|
||||
---
|
||||
description: Configure claude-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?
|
||||
CACHE_EXISTS=$(ls -d ~/.claude/plugins/cache/claude-hud 2>/dev/null && echo "YES" || echo "NO")
|
||||
|
||||
# Check 2: Registry entry exists?
|
||||
REGISTRY_EXISTS=$(grep -q "claude-hud" ~/.claude/plugins/installed_plugins.json 2>/dev/null && echo "YES" || echo "NO")
|
||||
|
||||
# Check 3: Temp files left behind?
|
||||
TEMP_FILES=$(ls -d ~/.claude/plugins/cache/temp_local_* 2>/dev/null | head -1)
|
||||
|
||||
echo "Cache: $CACHE_EXISTS | Registry: $REGISTRY_EXISTS | Temp: ${TEMP_FILES:-none}"
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**:
|
||||
```powershell
|
||||
$cache = Test-Path "$env:USERPROFILE\.claude\plugins\cache\claude-hud"
|
||||
$registry = (Get-Content "$env:USERPROFILE\.claude\plugins\installed_plugins.json" -ErrorAction SilentlyContinue) -match "claude-hud"
|
||||
$temp = Get-ChildItem "$env:USERPROFILE\.claude\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
|
||||
# Remove orphaned cache
|
||||
rm -rf ~/.claude/plugins/cache/claude-hud
|
||||
|
||||
# Remove temp files from failed installs
|
||||
rm -rf ~/.claude/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/plugins/installed_plugins.json
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**:
|
||||
```powershell
|
||||
# Remove orphaned cache
|
||||
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\plugins\cache\claude-hud" -ErrorAction SilentlyContinue
|
||||
|
||||
# Remove temp files
|
||||
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\plugins\cache\temp_local_*" -ErrorAction SilentlyContinue
|
||||
|
||||
# Reset registry (removes ALL plugins - warn user first!)
|
||||
'{"version": 2, "plugins": {}}' | Set-Content "$env:USERPROFILE\.claude\plugins\installed_plugins.json"
|
||||
```
|
||||
|
||||
After cleanup, tell user to **restart Claude Code** and run `/plugin install claude-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 claude-hud
|
||||
```
|
||||
|
||||
This is a [Claude Code platform limitation](https://github.com/anthropics/claude-code/issues/14799).
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Detect Platform & Runtime
|
||||
|
||||
**macOS/Linux** (if `uname -s` returns "Darwin", "Linux", or a MINGW*/MSYS*/CYGWIN* variant):
|
||||
|
||||
> **Git Bash/MSYS2/Cygwin users on Windows**: Follow these macOS/Linux instructions, not the Windows section below. Your environment provides bash and Unix-like tools.
|
||||
|
||||
1. Get plugin path:
|
||||
```bash
|
||||
ls -td ~/.claude/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | head -1
|
||||
```
|
||||
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, tell user to install via `/plugin install claude-hud` first.
|
||||
|
||||
2. Get runtime absolute path (prefer bun for performance, fallback to node):
|
||||
```bash
|
||||
command -v bun 2>/dev/null || command -v node 2>/dev/null
|
||||
```
|
||||
|
||||
If empty, stop and tell user to install Node.js or Bun.
|
||||
|
||||
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:
|
||||
```bash
|
||||
basename {RUNTIME_PATH}
|
||||
```
|
||||
If result is "bun", use `src/index.ts` (bun has native TypeScript support). Otherwise use `dist/index.js` (pre-compiled).
|
||||
|
||||
5. Generate command (quotes around runtime path handle spaces):
|
||||
```
|
||||
bash -c '"{RUNTIME_PATH}" "$(ls -td ~/.claude/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | head -1){SOURCE}"'
|
||||
```
|
||||
|
||||
**Windows** (native PowerShell/cmd.exe - if `uname` command is not available):
|
||||
|
||||
1. Get plugin path:
|
||||
```powershell
|
||||
(Get-ChildItem "$env:USERPROFILE\.claude\plugins\cache\claude-hud\claude-hud" | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
|
||||
```
|
||||
If empty or errors, the plugin is not installed. Tell user to install via marketplace first.
|
||||
|
||||
2. Get runtime absolute path (prefer bun, fallback to node):
|
||||
```powershell
|
||||
if (Get-Command bun -ErrorAction SilentlyContinue) { (Get-Command bun).Source } elseif (Get-Command node -ErrorAction SilentlyContinue) { (Get-Command node).Source } else { Write-Error "Neither bun nor node found" }
|
||||
```
|
||||
|
||||
If neither found, stop and tell user to install Node.js or Bun.
|
||||
|
||||
3. Check if runtime is bun (by filename). If bun, use `src\index.ts`. Otherwise use `dist\index.js`.
|
||||
|
||||
4. Generate command (note: quotes around runtime path handle spaces in paths):
|
||||
```
|
||||
powershell -Command "& {$p=(Get-ChildItem $env:USERPROFILE\.claude\plugins\cache\claude-hud\claude-hud | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName; & '{RUNTIME_PATH}' (Join-Path $p '{SOURCE}')}"
|
||||
```
|
||||
|
||||
**WSL (Windows Subsystem for Linux)**: If running in WSL, use the macOS/Linux instructions. Ensure the plugin is installed in the Linux environment (`~/.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 3: Apply Configuration
|
||||
|
||||
Read the settings file and merge in the statusLine config, preserving all existing settings:
|
||||
- **macOS/Linux/Git Bash**: `~/.claude/settings.json`
|
||||
- **Windows (native PowerShell)**: `$env:USERPROFILE\.claude\settings.json`
|
||||
|
||||
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.
|
||||
|
||||
```json
|
||||
{
|
||||
"statusLine": {
|
||||
"type": "command",
|
||||
"command": "{GENERATED_COMMAND}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**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 `/claude-hud:setup` to verify the plugin is still installed.
|
||||
|
||||
## Step 4: Verify With User
|
||||
|
||||
Use AskUserQuestion:
|
||||
- Question: "The HUD should now appear below your input field. Is it working?"
|
||||
- Options: "Yes, I see it" / "No, something's wrong"
|
||||
|
||||
**If yes**: Ask the user if they'd like to ⭐ star the claude-hud repository on GitHub to support the project. If they agree and `gh` CLI is available, run: `gh api -X PUT /user/starred/jarrodwatts/claude-hud`. Only run the star command if they explicitly say yes.
|
||||
|
||||
**If no**: Debug systematically:
|
||||
|
||||
1. **Verify config was applied**:
|
||||
- Read settings file (`~/.claude/settings.json` or `$env:USERPROFILE\.claude\settings.json` on Windows)
|
||||
- Check statusLine.command exists and looks correct
|
||||
- If command contains a hardcoded version path (not using dynamic `ls -td` lookup), it may be a stale config from a previous setup
|
||||
|
||||
2. **Test the command manually** and capture error output:
|
||||
```bash
|
||||
{GENERATED_COMMAND} 2>&1
|
||||
```
|
||||
|
||||
3. **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 with `command -v bun` or `command -v node`, 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/plugins/cache/claude-hud/`
|
||||
- Solution: reinstall plugin via marketplace
|
||||
|
||||
**Windows: "bash not recognized"**:
|
||||
- Wrong command type for Windows
|
||||
- Solution: use the PowerShell command variant
|
||||
|
||||
**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/plugins/cache/claude-hud/`
|
||||
|
||||
4. **If still stuck**: Show the user the exact command that was generated and the error, so they can report it or debug further
|
||||
8
skills/plugins/claude-hud/dist/config-reader.d.ts
vendored
Normal file
8
skills/plugins/claude-hud/dist/config-reader.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export interface ConfigCounts {
|
||||
claudeMdCount: number;
|
||||
rulesCount: number;
|
||||
mcpCount: number;
|
||||
hooksCount: number;
|
||||
}
|
||||
export declare function countConfigs(cwd?: string): Promise<ConfigCounts>;
|
||||
//# sourceMappingURL=config-reader.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/config-reader.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/config-reader.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"config-reader.d.ts","sourceRoot":"","sources":["../src/config-reader.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAiFD,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAsGtE"}
|
||||
168
skills/plugins/claude-hud/dist/config-reader.js
vendored
Normal file
168
skills/plugins/claude-hud/dist/config-reader.js
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { createDebug } from './debug.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 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;
|
||||
}
|
||||
export async function countConfigs(cwd) {
|
||||
let claudeMdCount = 0;
|
||||
let rulesCount = 0;
|
||||
let hooksCount = 0;
|
||||
const homeDir = os.homedir();
|
||||
const claudeDir = path.join(homeDir, '.claude');
|
||||
// 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);
|
||||
// ~/.claude.json (additional user-scope MCPs)
|
||||
const userClaudeJson = path.join(homeDir, '.claude.json');
|
||||
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 ===
|
||||
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)
|
||||
if (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)
|
||||
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)
|
||||
const projectSettings = path.join(cwd, '.claude', 'settings.json');
|
||||
for (const name of getMcpServerNames(projectSettings)) {
|
||||
projectMcpServers.add(name);
|
||||
}
|
||||
hooksCount += countHooksInFile(projectSettings);
|
||||
// {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);
|
||||
// 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 };
|
||||
}
|
||||
//# sourceMappingURL=config-reader.js.map
|
||||
1
skills/plugins/claude-hud/dist/config-reader.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/config-reader.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
31
skills/plugins/claude-hud/dist/config.d.ts
vendored
Normal file
31
skills/plugins/claude-hud/dist/config.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
export type LineLayoutType = 'compact' | 'expanded';
|
||||
export type AutocompactBufferMode = 'enabled' | 'disabled';
|
||||
export interface HudConfig {
|
||||
lineLayout: LineLayoutType;
|
||||
showSeparators: boolean;
|
||||
pathLevels: 1 | 2 | 3;
|
||||
gitStatus: {
|
||||
enabled: boolean;
|
||||
showDirty: boolean;
|
||||
showAheadBehind: boolean;
|
||||
showFileStats: boolean;
|
||||
};
|
||||
display: {
|
||||
showModel: boolean;
|
||||
showContextBar: boolean;
|
||||
showConfigCounts: boolean;
|
||||
showDuration: boolean;
|
||||
showTokenBreakdown: boolean;
|
||||
showUsage: boolean;
|
||||
showTools: boolean;
|
||||
showAgents: boolean;
|
||||
showTodos: boolean;
|
||||
autocompactBuffer: AutocompactBufferMode;
|
||||
usageThreshold: number;
|
||||
environmentThreshold: number;
|
||||
};
|
||||
}
|
||||
export declare const DEFAULT_CONFIG: HudConfig;
|
||||
export declare function getConfigPath(): string;
|
||||
export declare function loadConfig(): Promise<HudConfig>;
|
||||
//# sourceMappingURL=config.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/config.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/config.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,CAAC;AAEpD,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,UAAU,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,cAAc,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtB,SAAS,EAAE;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,OAAO,CAAC;QACzB,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,OAAO,EAAE;QACP,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,EAAE,OAAO,CAAC;QACxB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,YAAY,EAAE,OAAO,CAAC;QACtB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,SAAS,EAAE,OAAO,CAAC;QACnB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC;QACnB,iBAAiB,EAAE,qBAAqB,CAAC;QACzC,cAAc,EAAE,MAAM,CAAC;QACvB,oBAAoB,EAAE,MAAM,CAAC;KAC9B,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,SAwB5B,CAAC;AAEF,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AA4GD,wBAAsB,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC,CAcrD"}
|
||||
137
skills/plugins/claude-hud/dist/config.js
vendored
Normal file
137
skills/plugins/claude-hud/dist/config.js
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
export const DEFAULT_CONFIG = {
|
||||
lineLayout: 'expanded',
|
||||
showSeparators: false,
|
||||
pathLevels: 1,
|
||||
gitStatus: {
|
||||
enabled: true,
|
||||
showDirty: true,
|
||||
showAheadBehind: false,
|
||||
showFileStats: false,
|
||||
},
|
||||
display: {
|
||||
showModel: true,
|
||||
showContextBar: true,
|
||||
showConfigCounts: true,
|
||||
showDuration: true,
|
||||
showTokenBreakdown: true,
|
||||
showUsage: true,
|
||||
showTools: true,
|
||||
showAgents: true,
|
||||
showTodos: true,
|
||||
autocompactBuffer: 'enabled',
|
||||
usageThreshold: 0,
|
||||
environmentThreshold: 0,
|
||||
},
|
||||
};
|
||||
export function getConfigPath() {
|
||||
const homeDir = os.homedir();
|
||||
return path.join(homeDir, '.claude', 'plugins', 'claude-hud', '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 migrateConfig(userConfig) {
|
||||
const migrated = { ...userConfig };
|
||||
if ('layout' in userConfig && !('lineLayout' in userConfig)) {
|
||||
if (userConfig.layout === 'separators') {
|
||||
migrated.lineLayout = 'compact';
|
||||
migrated.showSeparators = true;
|
||||
}
|
||||
else {
|
||||
migrated.lineLayout = 'compact';
|
||||
migrated.showSeparators = false;
|
||||
}
|
||||
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 mergeConfig(userConfig) {
|
||||
const migrated = migrateConfig(userConfig);
|
||||
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 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,
|
||||
};
|
||||
const display = {
|
||||
showModel: typeof migrated.display?.showModel === 'boolean'
|
||||
? migrated.display.showModel
|
||||
: DEFAULT_CONFIG.display.showModel,
|
||||
showContextBar: typeof migrated.display?.showContextBar === 'boolean'
|
||||
? migrated.display.showContextBar
|
||||
: DEFAULT_CONFIG.display.showContextBar,
|
||||
showConfigCounts: typeof migrated.display?.showConfigCounts === 'boolean'
|
||||
? migrated.display.showConfigCounts
|
||||
: DEFAULT_CONFIG.display.showConfigCounts,
|
||||
showDuration: typeof migrated.display?.showDuration === 'boolean'
|
||||
? migrated.display.showDuration
|
||||
: DEFAULT_CONFIG.display.showDuration,
|
||||
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,
|
||||
showTools: typeof migrated.display?.showTools === 'boolean'
|
||||
? migrated.display.showTools
|
||||
: DEFAULT_CONFIG.display.showTools,
|
||||
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,
|
||||
autocompactBuffer: validateAutocompactBuffer(migrated.display?.autocompactBuffer)
|
||||
? migrated.display.autocompactBuffer
|
||||
: DEFAULT_CONFIG.display.autocompactBuffer,
|
||||
usageThreshold: validateThreshold(migrated.display?.usageThreshold, 100),
|
||||
environmentThreshold: validateThreshold(migrated.display?.environmentThreshold, 100),
|
||||
};
|
||||
return { lineLayout, showSeparators, pathLevels, gitStatus, display };
|
||||
}
|
||||
export async function loadConfig() {
|
||||
const configPath = getConfigPath();
|
||||
try {
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
const content = fs.readFileSync(configPath, 'utf-8');
|
||||
const userConfig = JSON.parse(content);
|
||||
return mergeConfig(userConfig);
|
||||
}
|
||||
catch {
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=config.js.map
|
||||
1
skills/plugins/claude-hud/dist/config.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/config.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAgC9B,MAAM,CAAC,MAAM,cAAc,GAAc;IACvC,UAAU,EAAE,UAAU;IACtB,cAAc,EAAE,KAAK;IACrB,UAAU,EAAE,CAAC;IACb,SAAS,EAAE;QACT,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,IAAI;QACf,eAAe,EAAE,KAAK;QACtB,aAAa,EAAE,KAAK;KACrB;IACD,OAAO,EAAE;QACP,SAAS,EAAE,IAAI;QACf,cAAc,EAAE,IAAI;QACpB,gBAAgB,EAAE,IAAI;QACtB,YAAY,EAAE,IAAI;QAClB,kBAAkB,EAAE,IAAI;QACxB,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,IAAI;QACf,UAAU,EAAE,IAAI;QAChB,SAAS,EAAE,IAAI;QACf,iBAAiB,EAAE,SAAS;QAC5B,cAAc,EAAE,CAAC;QACjB,oBAAoB,EAAE,CAAC;KACxB;CACF,CAAC;AAEF,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;AAC/E,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACxC,OAAO,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACxC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,UAAU,CAAC;AACrD,CAAC;AAED,SAAS,yBAAyB,CAAC,KAAc;IAC/C,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,UAAU,CAAC;AACrD,CAAC;AAMD,SAAS,aAAa,CAAC,UAA6C;IAClE,MAAM,QAAQ,GAAG,EAAE,GAAG,UAAU,EAAuC,CAAC;IAExE,IAAI,QAAQ,IAAI,UAAU,IAAI,CAAC,CAAC,YAAY,IAAI,UAAU,CAAC,EAAE,CAAC;QAC5D,IAAI,UAAU,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YACvC,QAAQ,CAAC,UAAU,GAAG,SAAS,CAAC;YAChC,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,UAAU,GAAG,SAAS,CAAC;YAChC,QAAQ,CAAC,cAAc,GAAG,KAAK,CAAC;QAClC,CAAC;QACD,OAAO,QAAQ,CAAC,MAAM,CAAC;IACzB,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc,EAAE,GAAG,GAAG,GAAG;IAClD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,WAAW,CAAC,UAA8B;IACjD,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAE3C,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC;QACxD,CAAC,CAAC,QAAQ,CAAC,UAAU;QACrB,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC;IAE9B,MAAM,cAAc,GAAG,OAAO,QAAQ,CAAC,cAAc,KAAK,SAAS;QACjE,CAAC,CAAC,QAAQ,CAAC,cAAc;QACzB,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC;IAElC,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC;QACxD,CAAC,CAAC,QAAQ,CAAC,UAAU;QACrB,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC;IAE9B,MAAM,SAAS,GAAG;QAChB,OAAO,EAAE,OAAO,QAAQ,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS;YACvD,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO;YAC5B,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,OAAO;QACpC,SAAS,EAAE,OAAO,QAAQ,CAAC,SAAS,EAAE,SAAS,KAAK,SAAS;YAC3D,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS;YAC9B,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,SAAS;QACtC,eAAe,EAAE,OAAO,QAAQ,CAAC,SAAS,EAAE,eAAe,KAAK,SAAS;YACvE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,eAAe;YACpC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,eAAe;QAC5C,aAAa,EAAE,OAAO,QAAQ,CAAC,SAAS,EAAE,aAAa,KAAK,SAAS;YACnE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,aAAa;YAClC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,aAAa;KAC3C,CAAC;IAEF,MAAM,OAAO,GAAG;QACd,SAAS,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS;YACzD,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS;YAC5B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS;QACpC,cAAc,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,cAAc,KAAK,SAAS;YACnE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,cAAc;YACjC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,cAAc;QACzC,gBAAgB,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,gBAAgB,KAAK,SAAS;YACvE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB;YACnC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,gBAAgB;QAC3C,YAAY,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,YAAY,KAAK,SAAS;YAC/D,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY;YAC/B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,YAAY;QACvC,kBAAkB,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,kBAAkB,KAAK,SAAS;YAC3E,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,kBAAkB;YACrC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,kBAAkB;QAC7C,SAAS,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS;YACzD,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS;YAC5B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS;QACpC,SAAS,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS;YACzD,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS;YAC5B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS;QACpC,UAAU,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,UAAU,KAAK,SAAS;YAC3D,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU;YAC7B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU;QACrC,SAAS,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS;YACzD,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS;YAC5B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS;QACpC,iBAAiB,EAAE,yBAAyB,CAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;YAC/E,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,iBAAiB;YACpC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,iBAAiB;QAC5C,cAAc,EAAE,iBAAiB,CAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,CAAC;QACxE,oBAAoB,EAAE,iBAAiB,CAAC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,CAAC;KACrF,CAAC;IAEF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IAEnC,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,OAAO,cAAc,CAAC;QACxB,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAuB,CAAC;QAC7D,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC"}
|
||||
10
skills/plugins/claude-hud/dist/constants.d.ts
vendored
Normal file
10
skills/plugins/claude-hud/dist/constants.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Autocompact buffer percentage.
|
||||
*
|
||||
* NOTE: This value (22.5% = 45k/200k) is empirically derived from community
|
||||
* observations of Claude Code's autocompact behavior. It is NOT officially
|
||||
* documented by Anthropic and may change in future Claude Code versions.
|
||||
* If users report mismatches, this value may need adjustment.
|
||||
*/
|
||||
export declare const AUTOCOMPACT_BUFFER_PERCENT = 0.225;
|
||||
//# sourceMappingURL=constants.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/constants.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/constants.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,QAAQ,CAAC"}
|
||||
10
skills/plugins/claude-hud/dist/constants.js
vendored
Normal file
10
skills/plugins/claude-hud/dist/constants.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Autocompact buffer percentage.
|
||||
*
|
||||
* NOTE: This value (22.5% = 45k/200k) is empirically derived from community
|
||||
* observations of Claude Code's autocompact behavior. It is NOT officially
|
||||
* documented by Anthropic and may change in future Claude Code versions.
|
||||
* If users report mismatches, this value may need adjustment.
|
||||
*/
|
||||
export const AUTOCOMPACT_BUFFER_PERCENT = 0.225;
|
||||
//# sourceMappingURL=constants.js.map
|
||||
1
skills/plugins/claude-hud/dist/constants.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/constants.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,CAAC"}
|
||||
6
skills/plugins/claude-hud/dist/debug.d.ts
vendored
Normal file
6
skills/plugins/claude-hud/dist/debug.d.ts
vendored
Normal file
@@ -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
|
||||
1
skills/plugins/claude-hud/dist/debug.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/debug.d.ts.map
vendored
Normal file
@@ -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"}
|
||||
15
skills/plugins/claude-hud/dist/debug.js
vendored
Normal file
15
skills/plugins/claude-hud/dist/debug.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Shared debug logging utility
|
||||
// Enable via: DEBUG=claude-hud or DEBUG=*
|
||||
const DEBUG = process.env.DEBUG?.includes('claude-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(`[claude-hud:${namespace}] ${msg}`, ...args);
|
||||
}
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=debug.js.map
|
||||
1
skills/plugins/claude-hud/dist/debug.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/debug.js.map
vendored
Normal file
@@ -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"}
|
||||
16
skills/plugins/claude-hud/dist/git.d.ts
vendored
Normal file
16
skills/plugins/claude-hud/dist/git.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export interface FileStats {
|
||||
modified: number;
|
||||
added: number;
|
||||
deleted: number;
|
||||
untracked: number;
|
||||
}
|
||||
export interface GitStatus {
|
||||
branch: string;
|
||||
isDirty: boolean;
|
||||
ahead: number;
|
||||
behind: number;
|
||||
fileStats?: FileStats;
|
||||
}
|
||||
export declare function getGitBranch(cwd?: string): Promise<string | null>;
|
||||
export declare function getGitStatus(cwd?: string): Promise<GitStatus | null>;
|
||||
//# sourceMappingURL=git.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/git.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/git.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;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;CACvB;AAED,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAavE;AAED,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAqD1E"}
|
||||
86
skills/plugins/claude-hud/dist/git.js
vendored
Normal file
86
skills/plugins/claude-hud/dist/git.js
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import { execFile } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
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' });
|
||||
return stdout.trim() || null;
|
||||
}
|
||||
catch {
|
||||
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' });
|
||||
const branch = branchOut.trim();
|
||||
if (!branch)
|
||||
return null;
|
||||
// Check for dirty state and parse file stats
|
||||
let isDirty = false;
|
||||
let fileStats;
|
||||
try {
|
||||
const { stdout: statusOut } = await execFileAsync('git', ['--no-optional-locks', 'status', '--porcelain'], { cwd, timeout: 1000, encoding: 'utf8' });
|
||||
const trimmed = statusOut.trim();
|
||||
isDirty = trimmed.length > 0;
|
||||
if (isDirty) {
|
||||
fileStats = parseFileStats(trimmed);
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// Ignore errors, assume clean
|
||||
}
|
||||
// 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' });
|
||||
const parts = revOut.trim().split(/\s+/);
|
||||
if (parts.length === 2) {
|
||||
behind = parseInt(parts[0], 10) || 0;
|
||||
ahead = parseInt(parts[1], 10) || 0;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// No upstream or error, keep 0/0
|
||||
}
|
||||
return { branch, isDirty, ahead, behind, fileStats };
|
||||
}
|
||||
catch {
|
||||
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 };
|
||||
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++;
|
||||
}
|
||||
else if (index === 'D' || worktree === 'D') {
|
||||
stats.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++;
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
//# sourceMappingURL=git.js.map
|
||||
1
skills/plugins/claude-hud/dist/git.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/git.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
|
||||
21
skills/plugins/claude-hud/dist/index.d.ts
vendored
Normal file
21
skills/plugins/claude-hud/dist/index.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { readStdin } 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 { getUsage } from './usage-api.js';
|
||||
import { loadConfig } from './config.js';
|
||||
export type MainDeps = {
|
||||
readStdin: typeof readStdin;
|
||||
parseTranscript: typeof parseTranscript;
|
||||
countConfigs: typeof countConfigs;
|
||||
getGitStatus: typeof getGitStatus;
|
||||
getUsage: typeof getUsage;
|
||||
loadConfig: typeof loadConfig;
|
||||
render: typeof render;
|
||||
now: () => number;
|
||||
log: (...args: unknown[]) => void;
|
||||
};
|
||||
export declare function main(overrides?: Partial<MainDeps>): Promise<void>;
|
||||
export declare function formatSessionDuration(sessionStart?: Date, now?: () => number): string;
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/index.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,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,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,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,wBAAsB,IAAI,CAAC,SAAS,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwD3E;AAED,wBAAgB,qBAAqB,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,GAAG,GAAE,MAAM,MAAyB,GAAG,MAAM,CAcvG"}
|
||||
75
skills/plugins/claude-hud/dist/index.js
vendored
Normal file
75
skills/plugins/claude-hud/dist/index.js
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
import { readStdin } 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 { getUsage } from './usage-api.js';
|
||||
import { loadConfig } from './config.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
export async function main(overrides = {}) {
|
||||
const deps = {
|
||||
readStdin,
|
||||
parseTranscript,
|
||||
countConfigs,
|
||||
getGitStatus,
|
||||
getUsage,
|
||||
loadConfig,
|
||||
render,
|
||||
now: () => Date.now(),
|
||||
log: console.log,
|
||||
...overrides,
|
||||
};
|
||||
try {
|
||||
const stdin = await deps.readStdin();
|
||||
if (!stdin) {
|
||||
deps.log('[claude-hud] Initializing...');
|
||||
return;
|
||||
}
|
||||
const transcriptPath = stdin.transcript_path ?? '';
|
||||
const transcript = await deps.parseTranscript(transcriptPath);
|
||||
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = await deps.countConfigs(stdin.cwd);
|
||||
const config = await deps.loadConfig();
|
||||
const gitStatus = config.gitStatus.enabled
|
||||
? await deps.getGitStatus(stdin.cwd)
|
||||
: null;
|
||||
// Only fetch usage if enabled in config (replaces env var requirement)
|
||||
const usageData = config.display.showUsage !== false
|
||||
? await deps.getUsage()
|
||||
: null;
|
||||
const sessionDuration = formatSessionDuration(transcript.sessionStart, deps.now);
|
||||
const ctx = {
|
||||
stdin,
|
||||
transcript,
|
||||
claudeMdCount,
|
||||
rulesCount,
|
||||
mcpCount,
|
||||
hooksCount,
|
||||
sessionDuration,
|
||||
gitStatus,
|
||||
usageData,
|
||||
config,
|
||||
};
|
||||
deps.render(ctx);
|
||||
}
|
||||
catch (error) {
|
||||
deps.log('[claude-hud] Error:', error instanceof Error ? error.message : 'Unknown error');
|
||||
}
|
||||
}
|
||||
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`;
|
||||
}
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
void main();
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
skills/plugins/claude-hud/dist/index.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
||||
3
skills/plugins/claude-hud/dist/render/agents-line.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/agents-line.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../types.js';
|
||||
export declare function renderAgentsLine(ctx: RenderContext): string | null;
|
||||
//# sourceMappingURL=agents-line.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/agents-line.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/agents-line.d.ts.map
vendored
Normal file
@@ -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;AAG7D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAqBlE"}
|
||||
44
skills/plugins/claude-hud/dist/render/agents-line.js
vendored
Normal file
44
skills/plugins/claude-hud/dist/render/agents-line.js
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import { yellow, green, magenta, dim } from './colors.js';
|
||||
export function renderAgentsLine(ctx) {
|
||||
const { agents } = ctx.transcript;
|
||||
const runningAgents = agents.filter((a) => a.status === 'running');
|
||||
const recentCompleted = agents
|
||||
.filter((a) => a.status === 'completed')
|
||||
.slice(-2);
|
||||
const toShow = [...runningAgents, ...recentCompleted].slice(-3);
|
||||
if (toShow.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const lines = [];
|
||||
for (const agent of toShow) {
|
||||
lines.push(formatAgent(agent));
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
function formatAgent(agent) {
|
||||
const statusIcon = agent.status === 'running' ? yellow('◐') : green('✓');
|
||||
const type = magenta(agent.type);
|
||||
const model = agent.model ? dim(`[${agent.model}]`) : '';
|
||||
const desc = agent.description ? dim(`: ${truncateDesc(agent.description)}`) : '';
|
||||
const elapsed = formatElapsed(agent);
|
||||
return `${statusIcon} ${type}${model ? ` ${model}` : ''}${desc} ${dim(`(${elapsed})`)}`;
|
||||
}
|
||||
function truncateDesc(desc, maxLen = 40) {
|
||||
if (desc.length <= maxLen)
|
||||
return desc;
|
||||
return desc.slice(0, maxLen - 3) + '...';
|
||||
}
|
||||
function formatElapsed(agent) {
|
||||
const now = Date.now();
|
||||
const start = agent.startTime.getTime();
|
||||
const end = agent.endTime?.getTime() ?? now;
|
||||
const ms = end - start;
|
||||
if (ms < 1000)
|
||||
return '<1s';
|
||||
if (ms < 60000)
|
||||
return `${Math.round(ms / 1000)}s`;
|
||||
const mins = Math.floor(ms / 60000);
|
||||
const secs = Math.round((ms % 60000) / 1000);
|
||||
return `${mins}m ${secs}s`;
|
||||
}
|
||||
//# sourceMappingURL=agents-line.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/agents-line.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/agents-line.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"agents-line.js","sourceRoot":"","sources":["../../src/render/agents-line.ts"],"names":[],"mappings":"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"}
|
||||
10
skills/plugins/claude-hud/dist/render/colors.d.ts
vendored
Normal file
10
skills/plugins/claude-hud/dist/render/colors.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
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 getContextColor(percent: number): string;
|
||||
export declare function coloredBar(percent: number, width?: number): string;
|
||||
//# sourceMappingURL=colors.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/colors.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/colors.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/render/colors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,cAAY,CAAC;AAS/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,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,MAAM,CAKtE"}
|
||||
39
skills/plugins/claude-hud/dist/render/colors.js
vendored
Normal file
39
skills/plugins/claude-hud/dist/render/colors.js
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
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 function green(text) {
|
||||
return `${GREEN}${text}${RESET}`;
|
||||
}
|
||||
export function yellow(text) {
|
||||
return `${YELLOW}${text}${RESET}`;
|
||||
}
|
||||
export function red(text) {
|
||||
return `${RED}${text}${RESET}`;
|
||||
}
|
||||
export function cyan(text) {
|
||||
return `${CYAN}${text}${RESET}`;
|
||||
}
|
||||
export function magenta(text) {
|
||||
return `${MAGENTA}${text}${RESET}`;
|
||||
}
|
||||
export function dim(text) {
|
||||
return `${DIM}${text}${RESET}`;
|
||||
}
|
||||
export function getContextColor(percent) {
|
||||
if (percent >= 85)
|
||||
return RED;
|
||||
if (percent >= 70)
|
||||
return YELLOW;
|
||||
return GREEN;
|
||||
}
|
||||
export function coloredBar(percent, width = 10) {
|
||||
const filled = Math.round((percent / 100) * width);
|
||||
const empty = width - filled;
|
||||
const color = getContextColor(percent);
|
||||
return `${color}${'█'.repeat(filled)}${DIM}${'░'.repeat(empty)}${RESET}`;
|
||||
}
|
||||
//# sourceMappingURL=colors.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/colors.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/colors.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/render/colors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,SAAS,CAAC;AAE/B,MAAM,GAAG,GAAG,SAAS,CAAC;AACtB,MAAM,GAAG,GAAG,UAAU,CAAC;AACvB,MAAM,KAAK,GAAG,UAAU,CAAC;AACzB,MAAM,MAAM,GAAG,UAAU,CAAC;AAC1B,MAAM,OAAO,GAAG,UAAU,CAAC;AAC3B,MAAM,IAAI,GAAG,UAAU,CAAC;AAExB,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,OAAO,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,OAAO,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,GAAG,OAAO,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,OAAO,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC9B,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IACjC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,QAAgB,EAAE;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,EAAE,CAAC;AAC3E,CAAC"}
|
||||
3
skills/plugins/claude-hud/dist/render/index.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/index.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../types.js';
|
||||
export declare function render(ctx: RenderContext): void;
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/index.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuFjD,wBAAgB,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAuB/C"}
|
||||
83
skills/plugins/claude-hud/dist/render/index.js
vendored
Normal file
83
skills/plugins/claude-hud/dist/render/index.js
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
import { renderSessionLine } from './session-line.js';
|
||||
import { renderToolsLine } from './tools-line.js';
|
||||
import { renderAgentsLine } from './agents-line.js';
|
||||
import { renderTodosLine } from './todos-line.js';
|
||||
import { renderIdentityLine, renderProjectLine, renderEnvironmentLine, renderUsageLine, } from './lines/index.js';
|
||||
import { dim, RESET } from './colors.js';
|
||||
function visualLength(str) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
return str.replace(/\x1b\[[0-9;]*m/g, '').length;
|
||||
}
|
||||
function makeSeparator(length) {
|
||||
return dim('─'.repeat(Math.max(length, 20)));
|
||||
}
|
||||
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?.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 renderCompact(ctx) {
|
||||
const lines = [];
|
||||
const sessionLine = renderSessionLine(ctx);
|
||||
if (sessionLine) {
|
||||
lines.push(sessionLine);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
function renderExpanded(ctx) {
|
||||
const lines = [];
|
||||
const identityLine = renderIdentityLine(ctx);
|
||||
if (identityLine) {
|
||||
lines.push(identityLine);
|
||||
}
|
||||
const projectLine = renderProjectLine(ctx);
|
||||
if (projectLine) {
|
||||
lines.push(projectLine);
|
||||
}
|
||||
const environmentLine = renderEnvironmentLine(ctx);
|
||||
if (environmentLine) {
|
||||
lines.push(environmentLine);
|
||||
}
|
||||
const usageLine = renderUsageLine(ctx);
|
||||
if (usageLine) {
|
||||
lines.push(usageLine);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
export function render(ctx) {
|
||||
const lineLayout = ctx.config?.lineLayout ?? 'expanded';
|
||||
const showSeparators = ctx.config?.showSeparators ?? false;
|
||||
const headerLines = lineLayout === 'expanded'
|
||||
? renderExpanded(ctx)
|
||||
: renderCompact(ctx);
|
||||
const activityLines = collectActivityLines(ctx);
|
||||
const lines = [...headerLines];
|
||||
if (showSeparators && activityLines.length > 0) {
|
||||
const maxWidth = Math.max(...headerLines.map(visualLength), 20);
|
||||
lines.push(makeSeparator(maxWidth));
|
||||
}
|
||||
lines.push(...activityLines);
|
||||
for (const line of lines) {
|
||||
const outputLine = `${RESET}${line.replace(/ /g, '\u00A0')}`;
|
||||
console.log(outputLine);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/index.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"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"}
|
||||
3
skills/plugins/claude-hud/dist/render/lines/environment.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/lines/environment.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../../types.js';
|
||||
export declare function renderEnvironmentLine(ctx: RenderContext): string | null;
|
||||
//# sourceMappingURL=environment.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/environment.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/environment.d.ts.map
vendored
Normal file
@@ -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;AAGpD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAqCvE"}
|
||||
30
skills/plugins/claude-hud/dist/render/lines/environment.js
vendored
Normal file
30
skills/plugins/claude-hud/dist/render/lines/environment.js
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { dim } from '../colors.js';
|
||||
export function renderEnvironmentLine(ctx) {
|
||||
const display = ctx.config?.display;
|
||||
if (display?.showConfigCounts === false) {
|
||||
return null;
|
||||
}
|
||||
const totalCounts = ctx.claudeMdCount + ctx.rulesCount + ctx.mcpCount + ctx.hooksCount;
|
||||
const threshold = display?.environmentThreshold ?? 0;
|
||||
if (totalCounts === 0 || totalCounts < threshold) {
|
||||
return null;
|
||||
}
|
||||
const parts = [];
|
||||
if (ctx.claudeMdCount > 0) {
|
||||
parts.push(`${ctx.claudeMdCount} CLAUDE.md`);
|
||||
}
|
||||
if (ctx.rulesCount > 0) {
|
||||
parts.push(`${ctx.rulesCount} rules`);
|
||||
}
|
||||
if (ctx.mcpCount > 0) {
|
||||
parts.push(`${ctx.mcpCount} MCPs`);
|
||||
}
|
||||
if (ctx.hooksCount > 0) {
|
||||
parts.push(`${ctx.hooksCount} hooks`);
|
||||
}
|
||||
if (parts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return dim(parts.join(' | '));
|
||||
}
|
||||
//# sourceMappingURL=environment.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/environment.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/environment.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../../src/render/lines/environment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,MAAM,UAAU,qBAAqB,CAAC,GAAkB;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAEpC,IAAI,OAAO,EAAE,gBAAgB,KAAK,KAAK,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC;IACvF,MAAM,SAAS,GAAG,OAAO,EAAE,oBAAoB,IAAI,CAAC,CAAC;IAErD,IAAI,WAAW,KAAK,CAAC,IAAI,WAAW,GAAG,SAAS,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,GAAG,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,aAAa,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,UAAU,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,UAAU,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAChC,CAAC"}
|
||||
3
skills/plugins/claude-hud/dist/render/lines/identity.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/lines/identity.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../../types.js';
|
||||
export declare function renderIdentityLine(ctx: RenderContext): string;
|
||||
//# sourceMappingURL=identity.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/identity.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/identity.d.ts.map
vendored
Normal file
@@ -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;AAMpD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA6C7D"}
|
||||
53
skills/plugins/claude-hud/dist/render/lines/identity.js
vendored
Normal file
53
skills/plugins/claude-hud/dist/render/lines/identity.js
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import { getContextPercent, getBufferedPercent, getModelName } from '../../stdin.js';
|
||||
import { coloredBar, cyan, dim, getContextColor, RESET } from '../colors.js';
|
||||
const DEBUG = process.env.DEBUG?.includes('claude-hud') || process.env.DEBUG === '*';
|
||||
export function renderIdentityLine(ctx) {
|
||||
const model = getModelName(ctx.stdin);
|
||||
const rawPercent = getContextPercent(ctx.stdin);
|
||||
const bufferedPercent = getBufferedPercent(ctx.stdin);
|
||||
const autocompactMode = ctx.config?.display?.autocompactBuffer ?? 'enabled';
|
||||
const percent = autocompactMode === 'disabled' ? rawPercent : bufferedPercent;
|
||||
if (DEBUG && autocompactMode === 'disabled') {
|
||||
console.error(`[claude-hud:context] autocompactBuffer=disabled, showing raw ${rawPercent}% (buffered would be ${bufferedPercent}%)`);
|
||||
}
|
||||
const bar = coloredBar(percent);
|
||||
const display = ctx.config?.display;
|
||||
const parts = [];
|
||||
const planName = display?.showUsage !== false ? ctx.usageData?.planName : undefined;
|
||||
const modelDisplay = planName ? `${model} | ${planName}` : model;
|
||||
if (display?.showModel !== false && display?.showContextBar !== false) {
|
||||
parts.push(`${cyan(`[${modelDisplay}]`)} ${bar} ${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
else if (display?.showModel !== false) {
|
||||
parts.push(`${cyan(`[${modelDisplay}]`)} ${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
else if (display?.showContextBar !== false) {
|
||||
parts.push(`${bar} ${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
else {
|
||||
parts.push(`${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
if (display?.showDuration !== false && ctx.sessionDuration) {
|
||||
parts.push(dim(`⏱️ ${ctx.sessionDuration}`));
|
||||
}
|
||||
let line = parts.join(' | ');
|
||||
if (display?.showTokenBreakdown !== false && percent >= 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 += dim(` (in: ${input}, cache: ${cache})`);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
function formatTokens(n) {
|
||||
if (n >= 1000000) {
|
||||
return `${(n / 1000000).toFixed(1)}M`;
|
||||
}
|
||||
if (n >= 1000) {
|
||||
return `${(n / 1000).toFixed(0)}k`;
|
||||
}
|
||||
return n.toString();
|
||||
}
|
||||
//# sourceMappingURL=identity.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/identity.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/identity.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../src/render/lines/identity.ts"],"names":[],"mappings":"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"}
|
||||
5
skills/plugins/claude-hud/dist/render/lines/index.d.ts
vendored
Normal file
5
skills/plugins/claude-hud/dist/render/lines/index.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export { renderIdentityLine } from './identity.js';
|
||||
export { renderProjectLine } from './project.js';
|
||||
export { renderEnvironmentLine } from './environment.js';
|
||||
export { renderUsageLine } from './usage.js';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/index.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/index.d.ts.map
vendored
Normal file
@@ -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,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
||||
5
skills/plugins/claude-hud/dist/render/lines/index.js
vendored
Normal file
5
skills/plugins/claude-hud/dist/render/lines/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export { renderIdentityLine } from './identity.js';
|
||||
export { renderProjectLine } from './project.js';
|
||||
export { renderEnvironmentLine } from './environment.js';
|
||||
export { renderUsageLine } from './usage.js';
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/index.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/index.js.map
vendored
Normal file
@@ -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,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
||||
3
skills/plugins/claude-hud/dist/render/lines/project.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/lines/project.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../../types.js';
|
||||
export declare function renderProjectLine(ctx: RenderContext): string | null;
|
||||
//# sourceMappingURL=project.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/project.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/project.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/render/lines/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA6CnE"}
|
||||
44
skills/plugins/claude-hud/dist/render/lines/project.js
vendored
Normal file
44
skills/plugins/claude-hud/dist/render/lines/project.js
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import { cyan, magenta, yellow } from '../colors.js';
|
||||
export function renderProjectLine(ctx) {
|
||||
if (!ctx.stdin.cwd) {
|
||||
return null;
|
||||
}
|
||||
const segments = ctx.stdin.cwd.split(/[/\\]/).filter(Boolean);
|
||||
const pathLevels = ctx.config?.pathLevels ?? 1;
|
||||
const projectPath = segments.length > 0 ? segments.slice(-pathLevels).join('/') : '/';
|
||||
let gitPart = '';
|
||||
const gitConfig = ctx.config?.gitStatus;
|
||||
const showGit = gitConfig?.enabled ?? true;
|
||||
if (showGit && ctx.gitStatus) {
|
||||
const gitParts = [ctx.gitStatus.branch];
|
||||
if ((gitConfig?.showDirty ?? true) && ctx.gitStatus.isDirty) {
|
||||
gitParts.push('*');
|
||||
}
|
||||
if (gitConfig?.showAheadBehind) {
|
||||
if (ctx.gitStatus.ahead > 0) {
|
||||
gitParts.push(` ↑${ctx.gitStatus.ahead}`);
|
||||
}
|
||||
if (ctx.gitStatus.behind > 0) {
|
||||
gitParts.push(` ↓${ctx.gitStatus.behind}`);
|
||||
}
|
||||
}
|
||||
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 = ` ${magenta('git:(')}${cyan(gitParts.join(''))}${magenta(')')}`;
|
||||
}
|
||||
return `${yellow(projectPath)}${gitPart}`;
|
||||
}
|
||||
//# sourceMappingURL=project.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/project.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/project.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/render/lines/project.ts"],"names":[],"mappings":"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"}
|
||||
3
skills/plugins/claude-hud/dist/render/lines/usage.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/lines/usage.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../../types.js';
|
||||
export declare function renderUsageLine(ctx: RenderContext): string | null;
|
||||
//# sourceMappingURL=usage.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/usage.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/usage.d.ts.map
vendored
Normal file
@@ -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;AAIpD,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA2CjE"}
|
||||
59
skills/plugins/claude-hud/dist/render/lines/usage.js
vendored
Normal file
59
skills/plugins/claude-hud/dist/render/lines/usage.js
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
import { isLimitReached } from '../../types.js';
|
||||
import { red, yellow, dim, getContextColor, RESET } from '../colors.js';
|
||||
export function renderUsageLine(ctx) {
|
||||
const display = ctx.config?.display;
|
||||
if (display?.showUsage === false) {
|
||||
return null;
|
||||
}
|
||||
if (!ctx.usageData?.planName) {
|
||||
return null;
|
||||
}
|
||||
if (ctx.usageData.apiUnavailable) {
|
||||
return yellow(`usage: ⚠`);
|
||||
}
|
||||
if (isLimitReached(ctx.usageData)) {
|
||||
const resetTime = ctx.usageData.fiveHour === 100
|
||||
? formatResetTime(ctx.usageData.fiveHourResetAt)
|
||||
: formatResetTime(ctx.usageData.sevenDayResetAt);
|
||||
return red(`⚠ Limit reached${resetTime ? ` (resets ${resetTime})` : ''}`);
|
||||
}
|
||||
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 fiveHourDisplay = formatUsagePercent(ctx.usageData.fiveHour);
|
||||
const fiveHourReset = formatResetTime(ctx.usageData.fiveHourResetAt);
|
||||
const fiveHourPart = fiveHourReset
|
||||
? `5h: ${fiveHourDisplay} (${fiveHourReset})`
|
||||
: `5h: ${fiveHourDisplay}`;
|
||||
if (sevenDay !== null && sevenDay >= 80) {
|
||||
const sevenDayDisplay = formatUsagePercent(sevenDay);
|
||||
return `${fiveHourPart} | 7d: ${sevenDayDisplay}`;
|
||||
}
|
||||
return fiveHourPart;
|
||||
}
|
||||
function formatUsagePercent(percent) {
|
||||
if (percent === null) {
|
||||
return dim('--');
|
||||
}
|
||||
const color = getContextColor(percent);
|
||||
return `${color}${percent}%${RESET}`;
|
||||
}
|
||||
function formatResetTime(resetAt) {
|
||||
if (!resetAt)
|
||||
return '';
|
||||
const now = new Date();
|
||||
const diffMs = resetAt.getTime() - now.getTime();
|
||||
if (diffMs <= 0)
|
||||
return '';
|
||||
const diffMins = Math.ceil(diffMs / 60000);
|
||||
if (diffMins < 60)
|
||||
return `${diffMins}m`;
|
||||
const hours = Math.floor(diffMins / 60);
|
||||
const mins = diffMins % 60;
|
||||
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
||||
}
|
||||
//# sourceMappingURL=usage.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/lines/usage.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/lines/usage.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../../src/render/lines/usage.ts"],"names":[],"mappings":"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"}
|
||||
7
skills/plugins/claude-hud/dist/render/session-line.d.ts
vendored
Normal file
7
skills/plugins/claude-hud/dist/render/session-line.d.ts
vendored
Normal file
@@ -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
|
||||
1
skills/plugins/claude-hud/dist/render/session-line.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/session-line.d.ts.map
vendored
Normal file
@@ -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;AAOjD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA6J5D"}
|
||||
181
skills/plugins/claude-hud/dist/render/session-line.js
vendored
Normal file
181
skills/plugins/claude-hud/dist/render/session-line.js
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
import { isLimitReached } from '../types.js';
|
||||
import { getContextPercent, getBufferedPercent, getModelName } from '../stdin.js';
|
||||
import { coloredBar, cyan, dim, magenta, red, yellow, getContextColor, RESET } from './colors.js';
|
||||
const DEBUG = process.env.DEBUG?.includes('claude-hud') || process.env.DEBUG === '*';
|
||||
/**
|
||||
* Renders the full session line (model + context bar + project + git + counts + usage + duration).
|
||||
* Used for compact layout mode.
|
||||
*/
|
||||
export function renderSessionLine(ctx) {
|
||||
const model = getModelName(ctx.stdin);
|
||||
const rawPercent = getContextPercent(ctx.stdin);
|
||||
const bufferedPercent = getBufferedPercent(ctx.stdin);
|
||||
const autocompactMode = ctx.config?.display?.autocompactBuffer ?? 'enabled';
|
||||
const percent = autocompactMode === 'disabled' ? rawPercent : bufferedPercent;
|
||||
if (DEBUG && autocompactMode === 'disabled') {
|
||||
console.error(`[claude-hud:context] autocompactBuffer=disabled, showing raw ${rawPercent}% (buffered would be ${bufferedPercent}%)`);
|
||||
}
|
||||
const bar = coloredBar(percent);
|
||||
const parts = [];
|
||||
const display = ctx.config?.display;
|
||||
// Model and context bar (FIRST)
|
||||
// Plan name only shows if showUsage is enabled (respects hybrid toggle)
|
||||
const planName = display?.showUsage !== false ? ctx.usageData?.planName : undefined;
|
||||
const modelDisplay = planName ? `${model} | ${planName}` : model;
|
||||
if (display?.showModel !== false && display?.showContextBar !== false) {
|
||||
parts.push(`${cyan(`[${modelDisplay}]`)} ${bar} ${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
else if (display?.showModel !== false) {
|
||||
parts.push(`${cyan(`[${modelDisplay}]`)} ${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
else if (display?.showContextBar !== false) {
|
||||
parts.push(`${bar} ${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
else {
|
||||
parts.push(`${getContextColor(percent)}${percent}%${RESET}`);
|
||||
}
|
||||
// Project path (SECOND)
|
||||
if (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('/') : '/';
|
||||
// Build git status string
|
||||
let gitPart = '';
|
||||
const gitConfig = ctx.config?.gitStatus;
|
||||
const showGit = gitConfig?.enabled ?? true;
|
||||
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 = ` ${magenta('git:(')}${cyan(gitParts.join(''))}${magenta(')')}`;
|
||||
}
|
||||
parts.push(`${yellow(projectPath)}${gitPart}`);
|
||||
}
|
||||
// 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(dim(`${ctx.claudeMdCount} CLAUDE.md`));
|
||||
}
|
||||
if (ctx.rulesCount > 0) {
|
||||
parts.push(dim(`${ctx.rulesCount} rules`));
|
||||
}
|
||||
if (ctx.mcpCount > 0) {
|
||||
parts.push(dim(`${ctx.mcpCount} MCPs`));
|
||||
}
|
||||
if (ctx.hooksCount > 0) {
|
||||
parts.push(dim(`${ctx.hooksCount} hooks`));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Usage limits display (shown when enabled in config, respects usageThreshold)
|
||||
if (display?.showUsage !== false && ctx.usageData?.planName) {
|
||||
if (ctx.usageData.apiUnavailable) {
|
||||
parts.push(yellow(`usage: ⚠`));
|
||||
}
|
||||
else if (isLimitReached(ctx.usageData)) {
|
||||
const resetTime = ctx.usageData.fiveHour === 100
|
||||
? formatResetTime(ctx.usageData.fiveHourResetAt)
|
||||
: formatResetTime(ctx.usageData.sevenDayResetAt);
|
||||
parts.push(red(`⚠ Limit reached${resetTime ? ` (resets ${resetTime})` : ''}`));
|
||||
}
|
||||
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 (effectiveUsage >= usageThreshold) {
|
||||
const fiveHourDisplay = formatUsagePercent(fiveHour);
|
||||
const fiveHourReset = formatResetTime(ctx.usageData.fiveHourResetAt);
|
||||
const fiveHourPart = fiveHourReset
|
||||
? `5h: ${fiveHourDisplay} (${fiveHourReset})`
|
||||
: `5h: ${fiveHourDisplay}`;
|
||||
if (sevenDay !== null && sevenDay >= 80) {
|
||||
const sevenDayDisplay = formatUsagePercent(sevenDay);
|
||||
parts.push(`${fiveHourPart} | 7d: ${sevenDayDisplay}`);
|
||||
}
|
||||
else {
|
||||
parts.push(fiveHourPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Session duration
|
||||
if (display?.showDuration !== false && ctx.sessionDuration) {
|
||||
parts.push(dim(`⏱️ ${ctx.sessionDuration}`));
|
||||
}
|
||||
let line = parts.join(' | ');
|
||||
// Token breakdown at high context
|
||||
if (display?.showTokenBreakdown !== false && percent >= 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 += dim(` (in: ${input}, cache: ${cache})`);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
function formatTokens(n) {
|
||||
if (n >= 1000000) {
|
||||
return `${(n / 1000000).toFixed(1)}M`;
|
||||
}
|
||||
if (n >= 1000) {
|
||||
return `${(n / 1000).toFixed(0)}k`;
|
||||
}
|
||||
return n.toString();
|
||||
}
|
||||
function formatUsagePercent(percent) {
|
||||
if (percent === null) {
|
||||
return dim('--');
|
||||
}
|
||||
const color = getContextColor(percent);
|
||||
return `${color}${percent}%${RESET}`;
|
||||
}
|
||||
function formatResetTime(resetAt) {
|
||||
if (!resetAt)
|
||||
return '';
|
||||
const now = new Date();
|
||||
const diffMs = resetAt.getTime() - now.getTime();
|
||||
if (diffMs <= 0)
|
||||
return '';
|
||||
const diffMins = Math.ceil(diffMs / 60000);
|
||||
if (diffMins < 60)
|
||||
return `${diffMins}m`;
|
||||
const hours = Math.floor(diffMins / 60);
|
||||
const mins = diffMins % 60;
|
||||
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
||||
}
|
||||
//# sourceMappingURL=session-line.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/session-line.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/session-line.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
skills/plugins/claude-hud/dist/render/todos-line.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/todos-line.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../types.js';
|
||||
export declare function renderTodosLine(ctx: RenderContext): string | null;
|
||||
//# sourceMappingURL=todos-line.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/todos-line.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/todos-line.d.ts.map
vendored
Normal file
@@ -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;AAGjD,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAsBjE"}
|
||||
25
skills/plugins/claude-hud/dist/render/todos-line.js
vendored
Normal file
25
skills/plugins/claude-hud/dist/render/todos-line.js
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import { yellow, green, dim } from './colors.js';
|
||||
export function renderTodosLine(ctx) {
|
||||
const { todos } = ctx.transcript;
|
||||
if (!todos || todos.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const inProgress = todos.find((t) => t.status === 'in_progress');
|
||||
const completed = todos.filter((t) => t.status === 'completed').length;
|
||||
const total = todos.length;
|
||||
if (!inProgress) {
|
||||
if (completed === total && total > 0) {
|
||||
return `${green('✓')} All todos complete ${dim(`(${completed}/${total})`)}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const content = truncateContent(inProgress.content);
|
||||
const progress = dim(`(${completed}/${total})`);
|
||||
return `${yellow('▸')} ${content} ${progress}`;
|
||||
}
|
||||
function truncateContent(content, maxLen = 50) {
|
||||
if (content.length <= maxLen)
|
||||
return content;
|
||||
return content.slice(0, maxLen - 3) + '...';
|
||||
}
|
||||
//# sourceMappingURL=todos-line.js.map
|
||||
1
skills/plugins/claude-hud/dist/render/todos-line.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/todos-line.js.map
vendored
Normal file
@@ -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,GAAG,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,UAAU,eAAe,CAAC,GAAkB;IAChD,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;IAEjC,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,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;IACjE,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IACvE,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,uBAAuB,GAAG,CAAC,IAAI,SAAS,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC9E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,SAAS,IAAI,KAAK,GAAG,CAAC,CAAC;IAEhD,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;AACjD,CAAC;AAED,SAAS,eAAe,CAAC,OAAe,EAAE,SAAiB,EAAE;IAC3D,IAAI,OAAO,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,OAAO,CAAC;IAC7C,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;AAC9C,CAAC"}
|
||||
3
skills/plugins/claude-hud/dist/render/tools-line.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/render/tools-line.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { RenderContext } from '../types.js';
|
||||
export declare function renderToolsLine(ctx: RenderContext): string | null;
|
||||
//# sourceMappingURL=tools-line.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/render/tools-line.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/tools-line.d.ts.map
vendored
Normal file
@@ -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;AAGjD,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAoCjE"}
|
||||
43
skills/plugins/claude-hud/dist/render/tools-line.js
vendored
Normal file
43
skills/plugins/claude-hud/dist/render/tools-line.js
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import { yellow, green, cyan, dim } from './colors.js';
|
||||
export function renderToolsLine(ctx) {
|
||||
const { tools } = ctx.transcript;
|
||||
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) : '';
|
||||
parts.push(`${yellow('◐')} ${cyan(tool.name)}${target ? dim(`: ${target}`) : ''}`);
|
||||
}
|
||||
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])
|
||||
.slice(0, 4);
|
||||
for (const [name, count] of sortedTools) {
|
||||
parts.push(`${green('✓')} ${name} ${dim(`×${count}`)}`);
|
||||
}
|
||||
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
|
||||
1
skills/plugins/claude-hud/dist/render/tools-line.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/render/tools-line.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"tools-line.js","sourceRoot":"","sources":["../../src/render/tools-line.ts"],"names":[],"mappings":"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"}
|
||||
6
skills/plugins/claude-hud/dist/stdin.d.ts
vendored
Normal file
6
skills/plugins/claude-hud/dist/stdin.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { StdinData } from './types.js';
|
||||
export declare function readStdin(): Promise<StdinData | null>;
|
||||
export declare function getContextPercent(stdin: StdinData): number;
|
||||
export declare function getBufferedPercent(stdin: StdinData): number;
|
||||
export declare function getModelName(stdin: StdinData): string;
|
||||
//# sourceMappingURL=stdin.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/stdin.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/stdin.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"stdin.d.ts","sourceRoot":"","sources":["../src/stdin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,wBAAsB,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAoB3D;AAuBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAe1D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAiB3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAErD"}
|
||||
72
skills/plugins/claude-hud/dist/stdin.js
vendored
Normal file
72
skills/plugins/claude-hud/dist/stdin.js
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
import { AUTOCOMPACT_BUFFER_PERCENT } from './constants.js';
|
||||
export async function readStdin() {
|
||||
if (process.stdin.isTTY) {
|
||||
return null;
|
||||
}
|
||||
const chunks = [];
|
||||
try {
|
||||
process.stdin.setEncoding('utf8');
|
||||
for await (const chunk of process.stdin) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
const raw = chunks.join('');
|
||||
if (!raw.trim()) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
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.
|
||||
*/
|
||||
function getNativePercent(stdin) {
|
||||
const nativePercent = stdin.context_window?.used_percentage;
|
||||
if (typeof nativePercent === 'number' && !Number.isNaN(nativePercent)) {
|
||||
return Math.min(100, Math.max(0, Math.round(nativePercent)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
export function getContextPercent(stdin) {
|
||||
// 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) {
|
||||
// Prefer native percentage (v2.1.6+) - accurate and matches /context
|
||||
// Native percentage already accounts for context correctly, no buffer needed
|
||||
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);
|
||||
const buffer = size * AUTOCOMPACT_BUFFER_PERCENT;
|
||||
return Math.min(100, Math.round(((totalTokens + buffer) / size) * 100));
|
||||
}
|
||||
export function getModelName(stdin) {
|
||||
return stdin.model?.display_name ?? stdin.model?.id ?? 'Unknown';
|
||||
}
|
||||
//# sourceMappingURL=stdin.js.map
|
||||
1
skills/plugins/claude-hud/dist/stdin.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/stdin.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"stdin.js","sourceRoot":"","sources":["../src/stdin.ts"],"names":[],"mappings":"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"}
|
||||
3
skills/plugins/claude-hud/dist/transcript.d.ts
vendored
Normal file
3
skills/plugins/claude-hud/dist/transcript.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { TranscriptData } from './types.js';
|
||||
export declare function parseTranscript(transcriptPath: string): Promise<TranscriptData>;
|
||||
//# sourceMappingURL=transcript.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/transcript.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/transcript.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,YAAY,CAAC;AAkBlF,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAyCrF"}
|
||||
113
skills/plugins/claude-hud/dist/transcript.js
vendored
Normal file
113
skills/plugins/claude-hud/dist/transcript.js
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
import * as fs from 'fs';
|
||||
import * as readline from 'readline';
|
||||
export async function parseTranscript(transcriptPath) {
|
||||
const result = {
|
||||
tools: [],
|
||||
agents: [],
|
||||
todos: [],
|
||||
};
|
||||
if (!transcriptPath || !fs.existsSync(transcriptPath)) {
|
||||
return result;
|
||||
}
|
||||
const toolMap = new Map();
|
||||
const agentMap = new Map();
|
||||
let latestTodos = [];
|
||||
try {
|
||||
const fileStream = fs.createReadStream(transcriptPath);
|
||||
const rl = readline.createInterface({
|
||||
input: fileStream,
|
||||
crlfDelay: Infinity,
|
||||
});
|
||||
for await (const line of rl) {
|
||||
if (!line.trim())
|
||||
continue;
|
||||
try {
|
||||
const entry = JSON.parse(line);
|
||||
processEntry(entry, toolMap, agentMap, latestTodos, result);
|
||||
}
|
||||
catch {
|
||||
// Skip malformed lines
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// Return partial results on error
|
||||
}
|
||||
result.tools = Array.from(toolMap.values()).slice(-20);
|
||||
result.agents = Array.from(agentMap.values()).slice(-10);
|
||||
result.todos = latestTodos;
|
||||
return result;
|
||||
}
|
||||
function processEntry(entry, toolMap, agentMap, latestTodos, result) {
|
||||
const timestamp = entry.timestamp ? new Date(entry.timestamp) : new Date();
|
||||
if (!result.sessionStart && entry.timestamp) {
|
||||
result.sessionStart = 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 toolEntry = {
|
||||
id: block.id,
|
||||
name: block.name,
|
||||
target: extractTarget(block.name, block.input),
|
||||
status: 'running',
|
||||
startTime: timestamp,
|
||||
};
|
||||
if (block.name === 'Task') {
|
||||
const input = block.input;
|
||||
const agentEntry = {
|
||||
id: block.id,
|
||||
type: input?.subagent_type ?? 'unknown',
|
||||
model: input?.model ?? undefined,
|
||||
description: input?.description ?? undefined,
|
||||
status: 'running',
|
||||
startTime: timestamp,
|
||||
};
|
||||
agentMap.set(block.id, agentEntry);
|
||||
}
|
||||
else if (block.name === 'TodoWrite') {
|
||||
const input = block.input;
|
||||
if (input?.todos && Array.isArray(input.todos)) {
|
||||
latestTodos.length = 0;
|
||||
latestTodos.push(...input.todos);
|
||||
}
|
||||
}
|
||||
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.status = 'completed';
|
||||
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 'Bash':
|
||||
const cmd = input.command;
|
||||
return cmd?.slice(0, 30) + (cmd?.length > 30 ? '...' : '');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
//# sourceMappingURL=transcript.js.map
|
||||
1
skills/plugins/claude-hud/dist/transcript.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/transcript.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAmBrC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,cAAsB;IAC1D,MAAM,MAAM,GAAmB;QAC7B,KAAK,EAAE,EAAE;QACT,MAAM,EAAE,EAAE;QACV,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,IAAI,CAAC,cAAc,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACtD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC/C,IAAI,WAAW,GAAe,EAAE,CAAC;IAEjC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;QACvD,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;YAClC,KAAK,EAAE,UAAU;YACjB,SAAS,EAAE,QAAQ;SACpB,CAAC,CAAC;QAEH,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE,SAAS;YAE3B,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;gBACjD,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,uBAAuB;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;IAED,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC;IAE3B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CACnB,KAAqB,EACrB,OAA+B,EAC/B,QAAiC,EACjC,WAAuB,EACvB,MAAsB;IAEtB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAE3E,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QAC5C,MAAM,CAAC,YAAY,GAAG,SAAS,CAAC;IAClC,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC;IACvC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO;IAEhD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACxD,MAAM,SAAS,GAAc;gBAC3B,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC;gBAC9C,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,SAAS;aACrB,CAAC;YAEF,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAgC,CAAC;gBACrD,MAAM,UAAU,GAAe;oBAC7B,EAAE,EAAE,KAAK,CAAC,EAAE;oBACZ,IAAI,EAAG,KAAK,EAAE,aAAwB,IAAI,SAAS;oBACnD,KAAK,EAAG,KAAK,EAAE,KAAgB,IAAI,SAAS;oBAC5C,WAAW,EAAG,KAAK,EAAE,WAAsB,IAAI,SAAS;oBACxD,MAAM,EAAE,SAAS;oBACjB,SAAS,EAAE,SAAS;iBACrB,CAAC;gBACF,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YACrC,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAA+B,CAAC;gBACpD,IAAI,KAAK,EAAE,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC/C,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;oBACvB,WAAW,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC;gBACrD,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YAC3B,CAAC;YAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC9C,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC;gBAC3B,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,KAA+B;IACtE,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAE7B,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,MAAM;YACT,OAAQ,KAAK,CAAC,SAAoB,IAAK,KAAK,CAAC,IAAe,CAAC;QAC/D,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,OAAiB,CAAC;QACjC,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,OAAiB,CAAC;QACjC,KAAK,MAAM;YACT,MAAM,GAAG,GAAG,KAAK,CAAC,OAAiB,CAAC;YACpC,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
||||
75
skills/plugins/claude-hud/dist/types.d.ts
vendored
Normal file
75
skills/plugins/claude-hud/dist/types.d.ts
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { HudConfig } from './config.js';
|
||||
import type { GitStatus } from './git.js';
|
||||
export interface StdinData {
|
||||
transcript_path?: string;
|
||||
cwd?: string;
|
||||
model?: {
|
||||
id?: string;
|
||||
display_name?: string;
|
||||
};
|
||||
context_window?: {
|
||||
context_window_size?: number;
|
||||
current_usage?: {
|
||||
input_tokens?: number;
|
||||
cache_creation_input_tokens?: number;
|
||||
cache_read_input_tokens?: number;
|
||||
} | null;
|
||||
used_percentage?: number | null;
|
||||
remaining_percentage?: number | 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;
|
||||
}
|
||||
export interface TodoItem {
|
||||
content: string;
|
||||
status: 'pending' | 'in_progress' | 'completed';
|
||||
}
|
||||
/** Usage window data from the OAuth API */
|
||||
export interface UsageWindow {
|
||||
utilization: number | null;
|
||||
resetAt: Date | null;
|
||||
}
|
||||
export interface UsageData {
|
||||
planName: string | null;
|
||||
fiveHour: number | null;
|
||||
sevenDay: number | null;
|
||||
fiveHourResetAt: Date | null;
|
||||
sevenDayResetAt: Date | null;
|
||||
apiUnavailable?: boolean;
|
||||
}
|
||||
/** Check if usage limit is reached (either window at 100%) */
|
||||
export declare function isLimitReached(data: UsageData): boolean;
|
||||
export interface TranscriptData {
|
||||
tools: ToolEntry[];
|
||||
agents: AgentEntry[];
|
||||
todos: TodoItem[];
|
||||
sessionStart?: Date;
|
||||
}
|
||||
export interface RenderContext {
|
||||
stdin: StdinData;
|
||||
transcript: TranscriptData;
|
||||
claudeMdCount: number;
|
||||
rulesCount: number;
|
||||
mcpCount: number;
|
||||
hooksCount: number;
|
||||
sessionDuration: string;
|
||||
gitStatus: GitStatus | null;
|
||||
usageData: UsageData | null;
|
||||
config: HudConfig;
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/types.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/types.d.ts.map
vendored
Normal file
@@ -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,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,aAAa,CAAC,EAAE;YACd,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,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;CACH;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;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;CACjD;AAED,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;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,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAEvD;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,IAAI,CAAC;CACrB;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,MAAM,EAAE,SAAS,CAAC;CACnB"}
|
||||
5
skills/plugins/claude-hud/dist/types.js
vendored
Normal file
5
skills/plugins/claude-hud/dist/types.js
vendored
Normal file
@@ -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
|
||||
1
skills/plugins/claude-hud/dist/types.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/types.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA8DA,8DAA8D;AAC9D,MAAM,UAAU,cAAc,CAAC,IAAe;IAC5C,OAAO,IAAI,CAAC,QAAQ,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC;AACxD,CAAC"}
|
||||
32
skills/plugins/claude-hud/dist/usage-api.d.ts
vendored
Normal file
32
skills/plugins/claude-hud/dist/usage-api.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
};
|
||||
}
|
||||
export type UsageApiDeps = {
|
||||
homeDir: () => string;
|
||||
fetchApi: (accessToken: string) => Promise<UsageApiResponse | null>;
|
||||
now: () => number;
|
||||
readKeychain: (now: number, homeDir: string) => {
|
||||
accessToken: string;
|
||||
subscriptionType: string;
|
||||
} | null;
|
||||
};
|
||||
/**
|
||||
* 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: 60s for success, 15s for failures.
|
||||
*/
|
||||
export declare function getUsage(overrides?: Partial<UsageApiDeps>): Promise<UsageData | null>;
|
||||
export declare function clearCache(homeDir?: string): void;
|
||||
//# sourceMappingURL=usage-api.d.ts.map
|
||||
1
skills/plugins/claude-hud/dist/usage-api.d.ts.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/usage-api.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"usage-api.d.ts","sourceRoot":"","sources":["../src/usage-api.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAe5C,UAAU,gBAAgB;IACxB,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AA8DD,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACpE,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC1G,CAAC;AASF;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,SAAS,GAAE,OAAO,CAAC,YAAY,CAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAkE/F;AA8PD,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAWjD"}
|
||||
370
skills/plugins/claude-hud/dist/usage-api.js
vendored
Normal file
370
skills/plugins/claude-hud/dist/usage-api.js
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as https from 'https';
|
||||
import { execFileSync } from 'child_process';
|
||||
import { createDebug } from './debug.js';
|
||||
const debug = createDebug('usage');
|
||||
// File-based cache (HUD runs as new process each render, so in-memory cache won't persist)
|
||||
const CACHE_TTL_MS = 60_000; // 60 seconds
|
||||
const CACHE_FAILURE_TTL_MS = 15_000; // 15 seconds for failed requests
|
||||
const KEYCHAIN_TIMEOUT_MS = 5000;
|
||||
const KEYCHAIN_BACKOFF_MS = 60_000; // Backoff on keychain failures to avoid re-prompting
|
||||
function getCachePath(homeDir) {
|
||||
return path.join(homeDir, '.claude', 'plugins', 'claude-hud', '.usage-cache.json');
|
||||
}
|
||||
function readCache(homeDir, now) {
|
||||
try {
|
||||
const cachePath = getCachePath(homeDir);
|
||||
if (!fs.existsSync(cachePath))
|
||||
return null;
|
||||
const content = fs.readFileSync(cachePath, 'utf8');
|
||||
const cache = JSON.parse(content);
|
||||
// Check TTL - use shorter TTL for failure results
|
||||
const ttl = cache.data.apiUnavailable ? CACHE_FAILURE_TTL_MS : CACHE_TTL_MS;
|
||||
if (now - cache.timestamp >= ttl)
|
||||
return null;
|
||||
// JSON.stringify converts Date to ISO string, so we need to reconvert on read.
|
||||
// new Date() handles both Date objects and ISO strings safely.
|
||||
const data = cache.data;
|
||||
if (data.fiveHourResetAt) {
|
||||
data.fiveHourResetAt = new Date(data.fiveHourResetAt);
|
||||
}
|
||||
if (data.sevenDayResetAt) {
|
||||
data.sevenDayResetAt = new Date(data.sevenDayResetAt);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function writeCache(homeDir, data, timestamp) {
|
||||
try {
|
||||
const cachePath = getCachePath(homeDir);
|
||||
const cacheDir = path.dirname(cachePath);
|
||||
if (!fs.existsSync(cacheDir)) {
|
||||
fs.mkdirSync(cacheDir, { recursive: true });
|
||||
}
|
||||
const cache = { data, timestamp };
|
||||
fs.writeFileSync(cachePath, JSON.stringify(cache), 'utf8');
|
||||
}
|
||||
catch {
|
||||
// Ignore cache write failures
|
||||
}
|
||||
}
|
||||
const defaultDeps = {
|
||||
homeDir: () => os.homedir(),
|
||||
fetchApi: fetchUsageApi,
|
||||
now: () => Date.now(),
|
||||
readKeychain: readKeychainCredentials,
|
||||
};
|
||||
/**
|
||||
* 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: 60s for success, 15s for failures.
|
||||
*/
|
||||
export async function getUsage(overrides = {}) {
|
||||
const deps = { ...defaultDeps, ...overrides };
|
||||
const now = deps.now();
|
||||
const homeDir = deps.homeDir();
|
||||
// Check file-based cache first
|
||||
const cached = readCache(homeDir, now);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
try {
|
||||
const credentials = readCredentials(homeDir, now, deps.readKeychain);
|
||||
if (!credentials) {
|
||||
return null;
|
||||
}
|
||||
const { accessToken, subscriptionType } = credentials;
|
||||
// Determine plan name from subscriptionType
|
||||
const planName = getPlanName(subscriptionType);
|
||||
if (!planName) {
|
||||
// API user, no usage limits to show
|
||||
return null;
|
||||
}
|
||||
// Fetch usage from API
|
||||
const apiResponse = await deps.fetchApi(accessToken);
|
||||
if (!apiResponse) {
|
||||
// API call failed, cache the failure to prevent retry storms
|
||||
const failureResult = {
|
||||
planName,
|
||||
fiveHour: null,
|
||||
sevenDay: null,
|
||||
fiveHourResetAt: null,
|
||||
sevenDayResetAt: null,
|
||||
apiUnavailable: true,
|
||||
};
|
||||
writeCache(homeDir, failureResult, now);
|
||||
return failureResult;
|
||||
}
|
||||
// Parse response - API returns 0-100 percentage directly
|
||||
// Clamp to 0-100 and handle NaN/Infinity
|
||||
const fiveHour = parseUtilization(apiResponse.five_hour?.utilization);
|
||||
const sevenDay = parseUtilization(apiResponse.seven_day?.utilization);
|
||||
const fiveHourResetAt = parseDate(apiResponse.five_hour?.resets_at);
|
||||
const sevenDayResetAt = parseDate(apiResponse.seven_day?.resets_at);
|
||||
const result = {
|
||||
planName,
|
||||
fiveHour,
|
||||
sevenDay,
|
||||
fiveHourResetAt,
|
||||
sevenDayResetAt,
|
||||
};
|
||||
// Write to file cache
|
||||
writeCache(homeDir, result, now);
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
debug('getUsage failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get path for keychain failure backoff cache.
|
||||
* Separate from usage cache to track keychain-specific failures.
|
||||
*/
|
||||
function getKeychainBackoffPath(homeDir) {
|
||||
return path.join(homeDir, '.claude', 'plugins', 'claude-hud', '.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
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Read credentials from macOS Keychain.
|
||||
* Claude Code 2.x stores OAuth credentials in the macOS Keychain under "Claude Code-credentials".
|
||||
* 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 {
|
||||
// Read from macOS Keychain using security command
|
||||
// Security: Use execFileSync with absolute path and args array (no shell)
|
||||
const keychainData = execFileSync('/usr/bin/security', ['find-generic-password', '-s', 'Claude Code-credentials', '-w'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: KEYCHAIN_TIMEOUT_MS }).trim();
|
||||
if (!keychainData) {
|
||||
return null;
|
||||
}
|
||||
const data = JSON.parse(keychainData);
|
||||
return parseCredentialsData(data, now);
|
||||
}
|
||||
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/.credentials.json
|
||||
*/
|
||||
function readFileCredentials(homeDir, now) {
|
||||
const credentialsPath = path.join(homeDir, '.claude', '.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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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 fileCreds = readFileCredentials(homeDir, now);
|
||||
if (fileCreds?.subscriptionType) {
|
||||
debug('Using keychain token with file subscriptionType');
|
||||
return {
|
||||
accessToken: keychainCreds.accessToken,
|
||||
subscriptionType: fileCreds.subscriptionType,
|
||||
};
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
/** 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;
|
||||
}
|
||||
function fetchUsageApi(accessToken) {
|
||||
return new Promise((resolve) => {
|
||||
const options = {
|
||||
hostname: 'api.anthropic.com',
|
||||
path: '/api/oauth/usage',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`,
|
||||
'anthropic-beta': 'oauth-2025-04-20',
|
||||
'User-Agent': 'claude-hud/1.0',
|
||||
},
|
||||
timeout: 5000,
|
||||
};
|
||||
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);
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
resolve(parsed);
|
||||
}
|
||||
catch (error) {
|
||||
debug('Failed to parse API response:', error);
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', (error) => {
|
||||
debug('API request error:', error);
|
||||
resolve(null);
|
||||
});
|
||||
req.on('timeout', () => {
|
||||
debug('API request timeout');
|
||||
req.destroy();
|
||||
resolve(null);
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
// Export for testing
|
||||
export function clearCache(homeDir) {
|
||||
if (homeDir) {
|
||||
try {
|
||||
const cachePath = getCachePath(homeDir);
|
||||
if (fs.existsSync(cachePath)) {
|
||||
fs.unlinkSync(cachePath);
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=usage-api.js.map
|
||||
1
skills/plugins/claude-hud/dist/usage-api.js.map
vendored
Normal file
1
skills/plugins/claude-hud/dist/usage-api.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user