@sooneocean/claude-hud 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/LICENSE +21 -0
  4. package/README.md +379 -0
  5. package/commands/configure.md +361 -0
  6. package/commands/export.md +43 -0
  7. package/commands/health.md +61 -0
  8. package/commands/setup.md +287 -0
  9. package/commands/theme.md +31 -0
  10. package/dist/alert.d.ts +31 -0
  11. package/dist/alert.d.ts.map +1 -0
  12. package/dist/alert.js +53 -0
  13. package/dist/alert.js.map +1 -0
  14. package/dist/burn-rate.d.ts +4 -0
  15. package/dist/burn-rate.d.ts.map +1 -0
  16. package/dist/burn-rate.js +36 -0
  17. package/dist/burn-rate.js.map +1 -0
  18. package/dist/cache.d.ts +6 -0
  19. package/dist/cache.d.ts.map +1 -0
  20. package/dist/cache.js +47 -0
  21. package/dist/cache.js.map +1 -0
  22. package/dist/claude-config-dir.d.ts +4 -0
  23. package/dist/claude-config-dir.d.ts.map +1 -0
  24. package/dist/claude-config-dir.js +24 -0
  25. package/dist/claude-config-dir.js.map +1 -0
  26. package/dist/config-io.d.ts +6 -0
  27. package/dist/config-io.d.ts.map +1 -0
  28. package/dist/config-io.js +27 -0
  29. package/dist/config-io.js.map +1 -0
  30. package/dist/config-reader.d.ts +8 -0
  31. package/dist/config-reader.d.ts.map +1 -0
  32. package/dist/config-reader.js +204 -0
  33. package/dist/config-reader.js.map +1 -0
  34. package/dist/config.d.ts +94 -0
  35. package/dist/config.d.ts.map +1 -0
  36. package/dist/config.js +358 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/constants.d.ts +11 -0
  39. package/dist/constants.d.ts.map +1 -0
  40. package/dist/constants.js +11 -0
  41. package/dist/constants.js.map +1 -0
  42. package/dist/cost-tracker.d.ts +9 -0
  43. package/dist/cost-tracker.d.ts.map +1 -0
  44. package/dist/cost-tracker.js +46 -0
  45. package/dist/cost-tracker.js.map +1 -0
  46. package/dist/debug.d.ts +6 -0
  47. package/dist/debug.d.ts.map +1 -0
  48. package/dist/debug.js +15 -0
  49. package/dist/debug.js.map +1 -0
  50. package/dist/extra-cmd.d.ts +20 -0
  51. package/dist/extra-cmd.d.ts.map +1 -0
  52. package/dist/extra-cmd.js +112 -0
  53. package/dist/extra-cmd.js.map +1 -0
  54. package/dist/git.d.ts +16 -0
  55. package/dist/git.d.ts.map +1 -0
  56. package/dist/git.js +94 -0
  57. package/dist/git.js.map +1 -0
  58. package/dist/health-check.d.ts +12 -0
  59. package/dist/health-check.d.ts.map +1 -0
  60. package/dist/health-check.js +37 -0
  61. package/dist/health-check.js.map +1 -0
  62. package/dist/index.d.ts +24 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +198 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/providers/agent-teams-provider.d.ts +10 -0
  67. package/dist/providers/agent-teams-provider.d.ts.map +1 -0
  68. package/dist/providers/agent-teams-provider.js +57 -0
  69. package/dist/providers/agent-teams-provider.js.map +1 -0
  70. package/dist/providers/agw-provider.d.ts +10 -0
  71. package/dist/providers/agw-provider.d.ts.map +1 -0
  72. package/dist/providers/agw-provider.js +49 -0
  73. package/dist/providers/agw-provider.js.map +1 -0
  74. package/dist/providers/index.d.ts +14 -0
  75. package/dist/providers/index.d.ts.map +1 -0
  76. package/dist/providers/index.js +25 -0
  77. package/dist/providers/index.js.map +1 -0
  78. package/dist/render/agents-line.d.ts +3 -0
  79. package/dist/render/agents-line.d.ts.map +1 -0
  80. package/dist/render/agents-line.js +40 -0
  81. package/dist/render/agents-line.js.map +1 -0
  82. package/dist/render/alert-line.d.ts +3 -0
  83. package/dist/render/alert-line.d.ts.map +1 -0
  84. package/dist/render/alert-line.js +11 -0
  85. package/dist/render/alert-line.js.map +1 -0
  86. package/dist/render/colors.d.ts +39 -0
  87. package/dist/render/colors.d.ts.map +1 -0
  88. package/dist/render/colors.js +109 -0
  89. package/dist/render/colors.js.map +1 -0
  90. package/dist/render/framework-line.d.ts +3 -0
  91. package/dist/render/framework-line.d.ts.map +1 -0
  92. package/dist/render/framework-line.js +32 -0
  93. package/dist/render/framework-line.js.map +1 -0
  94. package/dist/render/index.d.ts +3 -0
  95. package/dist/render/index.d.ts.map +1 -0
  96. package/dist/render/index.js +435 -0
  97. package/dist/render/index.js.map +1 -0
  98. package/dist/render/lines/environment.d.ts +3 -0
  99. package/dist/render/lines/environment.d.ts.map +1 -0
  100. package/dist/render/lines/environment.js +30 -0
  101. package/dist/render/lines/environment.js.map +1 -0
  102. package/dist/render/lines/identity.d.ts +3 -0
  103. package/dist/render/lines/identity.d.ts.map +1 -0
  104. package/dist/render/lines/identity.js +93 -0
  105. package/dist/render/lines/identity.js.map +1 -0
  106. package/dist/render/lines/index.d.ts +5 -0
  107. package/dist/render/lines/index.d.ts.map +1 -0
  108. package/dist/render/lines/index.js +5 -0
  109. package/dist/render/lines/index.js.map +1 -0
  110. package/dist/render/lines/project.d.ts +3 -0
  111. package/dist/render/lines/project.d.ts.map +1 -0
  112. package/dist/render/lines/project.js +100 -0
  113. package/dist/render/lines/project.js.map +1 -0
  114. package/dist/render/lines/usage.d.ts +3 -0
  115. package/dist/render/lines/usage.d.ts.map +1 -0
  116. package/dist/render/lines/usage.js +65 -0
  117. package/dist/render/lines/usage.js.map +1 -0
  118. package/dist/render/session-line.d.ts +7 -0
  119. package/dist/render/session-line.d.ts.map +1 -0
  120. package/dist/render/session-line.js +227 -0
  121. package/dist/render/session-line.js.map +1 -0
  122. package/dist/render/todos-line.d.ts +3 -0
  123. package/dist/render/todos-line.d.ts.map +1 -0
  124. package/dist/render/todos-line.js +29 -0
  125. package/dist/render/todos-line.js.map +1 -0
  126. package/dist/render/tools-line.d.ts +3 -0
  127. package/dist/render/tools-line.d.ts.map +1 -0
  128. package/dist/render/tools-line.js +45 -0
  129. package/dist/render/tools-line.js.map +1 -0
  130. package/dist/session-history.d.ts +15 -0
  131. package/dist/session-history.d.ts.map +1 -0
  132. package/dist/session-history.js +46 -0
  133. package/dist/session-history.js.map +1 -0
  134. package/dist/session-stats.d.ts +11 -0
  135. package/dist/session-stats.d.ts.map +1 -0
  136. package/dist/session-stats.js +48 -0
  137. package/dist/session-stats.js.map +1 -0
  138. package/dist/speed-tracker.d.ts +7 -0
  139. package/dist/speed-tracker.d.ts.map +1 -0
  140. package/dist/speed-tracker.js +34 -0
  141. package/dist/speed-tracker.js.map +1 -0
  142. package/dist/stdin.d.ts +9 -0
  143. package/dist/stdin.d.ts.map +1 -0
  144. package/dist/stdin.js +142 -0
  145. package/dist/stdin.js.map +1 -0
  146. package/dist/themes.d.ts +10 -0
  147. package/dist/themes.d.ts.map +1 -0
  148. package/dist/themes.js +81 -0
  149. package/dist/themes.js.map +1 -0
  150. package/dist/transcript.d.ts +3 -0
  151. package/dist/transcript.d.ts.map +1 -0
  152. package/dist/transcript.js +221 -0
  153. package/dist/transcript.js.map +1 -0
  154. package/dist/types.d.ts +124 -0
  155. package/dist/types.d.ts.map +1 -0
  156. package/dist/types.js +5 -0
  157. package/dist/types.js.map +1 -0
  158. package/dist/usage-api.d.ts +62 -0
  159. package/dist/usage-api.d.ts.map +1 -0
  160. package/dist/usage-api.js +908 -0
  161. package/dist/usage-api.js.map +1 -0
  162. package/dist/utils/format.d.ts +9 -0
  163. package/dist/utils/format.d.ts.map +1 -0
  164. package/dist/utils/format.js +75 -0
  165. package/dist/utils/format.js.map +1 -0
  166. package/dist/utils/terminal.d.ts +5 -0
  167. package/dist/utils/terminal.d.ts.map +1 -0
  168. package/dist/utils/terminal.js +42 -0
  169. package/dist/utils/terminal.js.map +1 -0
  170. package/package.json +36 -0
  171. package/src/alert.ts +75 -0
  172. package/src/burn-rate.ts +45 -0
  173. package/src/cache.ts +57 -0
  174. package/src/claude-config-dir.ts +27 -0
  175. package/src/config-io.ts +26 -0
  176. package/src/config-reader.ts +236 -0
  177. package/src/config.ts +496 -0
  178. package/src/constants.ts +10 -0
  179. package/src/cost-tracker.ts +53 -0
  180. package/src/debug.ts +16 -0
  181. package/src/extra-cmd.ts +125 -0
  182. package/src/git.ts +126 -0
  183. package/src/health-check.ts +50 -0
  184. package/src/index.ts +234 -0
  185. package/src/providers/agent-teams-provider.ts +56 -0
  186. package/src/providers/agw-provider.ts +47 -0
  187. package/src/providers/index.ts +27 -0
  188. package/src/render/agents-line.ts +51 -0
  189. package/src/render/alert-line.ts +11 -0
  190. package/src/render/colors.ts +145 -0
  191. package/src/render/framework-line.ts +34 -0
  192. package/src/render/index.ts +512 -0
  193. package/src/render/lines/environment.ts +41 -0
  194. package/src/render/lines/identity.ts +109 -0
  195. package/src/render/lines/index.ts +4 -0
  196. package/src/render/lines/project.ts +113 -0
  197. package/src/render/lines/usage.ts +79 -0
  198. package/src/render/session-line.ts +253 -0
  199. package/src/render/todos-line.ts +35 -0
  200. package/src/render/tools-line.ts +58 -0
  201. package/src/session-history.ts +62 -0
  202. package/src/session-stats.ts +65 -0
  203. package/src/speed-tracker.ts +51 -0
  204. package/src/stdin.ts +169 -0
  205. package/src/themes.ts +90 -0
  206. package/src/transcript.ts +268 -0
  207. package/src/types.ts +146 -0
  208. package/src/usage-api.ts +1090 -0
  209. package/src/utils/format.ts +79 -0
  210. package/src/utils/terminal.ts +46 -0
@@ -0,0 +1,287 @@
1
+ ---
2
+ description: Configure claude-hud as your statusline
3
+ allowed-tools: Bash, Read, Edit, AskUserQuestion
4
+ ---
5
+
6
+ **Note**: Placeholders like `{RUNTIME_PATH}`, `{SOURCE}`, and `{GENERATED_COMMAND}` should be substituted with actual detected values.
7
+
8
+ ## Step 0: Detect Ghost Installation (Run First)
9
+
10
+ Check for inconsistent plugin state that can occur after failed installations:
11
+
12
+ **macOS/Linux**:
13
+ ```bash
14
+ # Check 1: Cache exists?
15
+ CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
16
+ CACHE_EXISTS=$(ls -d "$CLAUDE_DIR/plugins/cache/claude-hud" 2>/dev/null && echo "YES" || echo "NO")
17
+
18
+ # Check 2: Registry entry exists?
19
+ REGISTRY_EXISTS=$(grep -q "claude-hud" "$CLAUDE_DIR/plugins/installed_plugins.json" 2>/dev/null && echo "YES" || echo "NO")
20
+
21
+ # Check 3: Temp files left behind?
22
+ TEMP_FILES=$(ls -d "$CLAUDE_DIR/plugins/cache/temp_local_"* 2>/dev/null | head -1)
23
+
24
+ echo "Cache: $CACHE_EXISTS | Registry: $REGISTRY_EXISTS | Temp: ${TEMP_FILES:-none}"
25
+ ```
26
+
27
+ **Windows (PowerShell)**:
28
+ ```powershell
29
+ $cache = Test-Path "$env:USERPROFILE\.claude\plugins\cache\claude-hud"
30
+ $registry = (Get-Content "$env:USERPROFILE\.claude\plugins\installed_plugins.json" -ErrorAction SilentlyContinue) -match "claude-hud"
31
+ $temp = Get-ChildItem "$env:USERPROFILE\.claude\plugins\cache\temp_local_*" -ErrorAction SilentlyContinue
32
+ Write-Host "Cache: $cache | Registry: $registry | Temp: $($temp.Count) files"
33
+ ```
34
+
35
+ ### Interpreting Results
36
+
37
+ | Cache | Registry | Meaning | Action |
38
+ |-------|----------|---------|--------|
39
+ | YES | YES | Normal install (may still be broken) | Continue to Step 1 |
40
+ | YES | NO | Ghost install - cache orphaned | Clean up cache |
41
+ | NO | YES | Ghost install - registry stale | Clean up registry |
42
+ | NO | NO | Not installed | Continue to Step 1 |
43
+
44
+ If **temp files exist**, a previous install was interrupted. Clean them up.
45
+
46
+ ### Cleanup Commands
47
+
48
+ If ghost installation detected, ask user if they want to reset. If yes:
49
+
50
+ **macOS/Linux**:
51
+ ```bash
52
+ CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
53
+
54
+ # Remove orphaned cache
55
+ rm -rf "$CLAUDE_DIR/plugins/cache/claude-hud"
56
+
57
+ # Remove temp files from failed installs
58
+ rm -rf "$CLAUDE_DIR/plugins/cache/temp_local_"*
59
+
60
+ # Reset registry (removes ALL plugins - warn user first!)
61
+ # Only run if user confirms they have no other plugins they want to keep:
62
+ echo '{"version": 2, "plugins": {}}' > "$CLAUDE_DIR/plugins/installed_plugins.json"
63
+ ```
64
+
65
+ **Windows (PowerShell)**:
66
+ ```powershell
67
+ # Remove orphaned cache
68
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\plugins\cache\claude-hud" -ErrorAction SilentlyContinue
69
+
70
+ # Remove temp files
71
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\plugins\cache\temp_local_*" -ErrorAction SilentlyContinue
72
+
73
+ # Reset registry (removes ALL plugins - warn user first!)
74
+ '{"version": 2, "plugins": {}}' | Set-Content "$env:USERPROFILE\.claude\plugins\installed_plugins.json"
75
+ ```
76
+
77
+ After cleanup, tell user to **restart Claude Code** and run `/plugin install claude-hud` again.
78
+
79
+ ### Linux: Cross-Device Filesystem Check
80
+
81
+ **On Linux only**, if install keeps failing, check for EXDEV issue:
82
+ ```bash
83
+ [ "$(df --output=source ~ /tmp 2>/dev/null | tail -2 | uniq | wc -l)" = "2" ] && echo "CROSS_DEVICE"
84
+ ```
85
+
86
+ If this outputs `CROSS_DEVICE`, `/tmp` and home are on different filesystems. This causes `EXDEV: cross-device link not permitted` during installation. Workaround:
87
+ ```bash
88
+ mkdir -p ~/.cache/tmp && TMPDIR=~/.cache/tmp claude /plugin install claude-hud
89
+ ```
90
+
91
+ This is a [Claude Code platform limitation](https://github.com/anthropics/claude-code/issues/14799).
92
+
93
+ ---
94
+
95
+ ## Step 1: Detect Platform, Shell, and Runtime
96
+
97
+ **IMPORTANT**: Use the environment context values (`Platform:` and `Shell:`), not `uname -s` or ad-hoc checks. The Bash tool may report MINGW/MSYS on Windows, so branch only by the context values.
98
+
99
+ | Platform | Shell | Command Format |
100
+ |----------|-------|----------------|
101
+ | `darwin` | any | bash (macOS instructions) |
102
+ | `linux` | any | bash (Linux instructions) |
103
+ | `win32` | `bash` (Git Bash, MSYS2) | bash (use macOS/Linux instructions) |
104
+ | `win32` | `powershell`, `pwsh`, or `cmd` | PowerShell (use Windows instructions) |
105
+
106
+ ---
107
+
108
+ **macOS/Linux** (Platform: `darwin` or `linux`):
109
+
110
+ 1. Get plugin path (sorted by dotted numeric version, not modification time):
111
+ ```bash
112
+ ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | awk -F/ '{ print $(NF-1) "\t" $0 }' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2-
113
+ ```
114
+ 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.
115
+
116
+ 2. Get runtime absolute path (prefer bun for performance, fallback to node):
117
+ ```bash
118
+ command -v bun 2>/dev/null || command -v node 2>/dev/null
119
+ ```
120
+
121
+ If empty, stop and tell user to install Node.js or Bun.
122
+
123
+ 3. Verify the runtime exists:
124
+ ```bash
125
+ ls -la {RUNTIME_PATH}
126
+ ```
127
+ If it doesn't exist, re-detect or ask user to verify their installation.
128
+
129
+ 4. Determine source file based on runtime:
130
+ ```bash
131
+ basename {RUNTIME_PATH}
132
+ ```
133
+ If result is "bun", use `src/index.ts` (bun has native TypeScript support). Otherwise use `dist/index.js` (pre-compiled).
134
+
135
+ 5. Generate command (quotes around runtime path handle spaces):
136
+ ```
137
+ bash -c 'plugin_dir=$(ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | awk -F/ '"'"'{ print $(NF-1) "\t" $0 }'"'"' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2-); exec "{RUNTIME_PATH}" "${plugin_dir}{SOURCE}"'
138
+ ```
139
+
140
+ **Windows** (Platform: `win32`):
141
+
142
+ Choose instructions by `Shell:` value before running any commands:
143
+ - `Shell: bash` -> use the macOS/Linux section above (same command format).
144
+ - `Shell: powershell`, `pwsh`, or `cmd` -> use the Windows PowerShell section below.
145
+ - Any other shell value -> stop and ask the user which shell launched Claude Code.
146
+
147
+ 1. Get plugin path:
148
+ ```powershell
149
+ (Get-ChildItem "$env:USERPROFILE\.claude\plugins\cache\claude-hud\claude-hud" -Directory | Where-Object { $_.Name -match '^\d+(\.\d+)+$' } | Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1).FullName
150
+ ```
151
+ If empty or errors, the plugin is not installed. Tell user to install via marketplace first.
152
+
153
+ 2. Get runtime absolute path (prefer bun, fallback to node):
154
+ ```powershell
155
+ 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" }
156
+ ```
157
+
158
+ If neither found, stop and tell user to install Node.js or Bun.
159
+
160
+ 3. Check if runtime is bun (by filename). If bun, use `src\index.ts`. Otherwise use `dist\index.js`.
161
+
162
+ 4. Generate command (note: quotes around runtime path handle spaces in paths):
163
+ ```
164
+ powershell -Command "& {$p=(Get-ChildItem $env:USERPROFILE\.claude\plugins\cache\claude-hud\claude-hud -Directory | Where-Object { $_.Name -match '^\d+(\.\d+)+$' } | Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1).FullName; & '{RUNTIME_PATH}' (Join-Path $p '{SOURCE}')}"
165
+ ```
166
+
167
+ **WSL (Windows Subsystem for Linux)**: If running in WSL, use the macOS/Linux instructions. Ensure the plugin is installed in the Linux environment (`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/...`), not the Windows side.
168
+
169
+ ## Step 2: Test Command
170
+
171
+ Run the generated command. It should produce output (the HUD lines) within a few seconds.
172
+
173
+ - If it errors, do not proceed to Step 3.
174
+ - If it hangs for more than a few seconds, cancel and debug.
175
+ - This test catches issues like broken runtime binaries, missing plugins, or path problems.
176
+
177
+ ## Step 3: Apply Configuration
178
+
179
+ Read the settings file and merge in the statusLine config, preserving all existing settings:
180
+ - **Platform `darwin` or `linux`, or Platform `win32` + Shell `bash`**: `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json`
181
+ - **Platform `win32` + Shell `powershell`, `pwsh`, or `cmd`**: `$env:USERPROFILE\.claude\settings.json`
182
+
183
+ If the file doesn't exist, create it. If it contains invalid JSON, report the error and do not overwrite.
184
+ If a write fails with `File has been unexpectedly modified`, re-read the file and retry the merge once.
185
+
186
+ ```json
187
+ {
188
+ "statusLine": {
189
+ "type": "command",
190
+ "command": "{GENERATED_COMMAND}"
191
+ }
192
+ }
193
+ ```
194
+
195
+
196
+ After successfully writing the config, tell the user:
197
+
198
+ > ✅ Config written. **Please restart Claude Code now** — quit and run `claude` again in your terminal.
199
+ > Once restarted, run `/claude-hud:setup` again to complete Step 4 and verify the HUD is working.
200
+
201
+ **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.
202
+
203
+ ## Step 4: Optional Features
204
+
205
+ After the statusLine is applied, ask the user if they'd like to enable additional HUD features beyond the default 2-line display.
206
+
207
+ Use AskUserQuestion:
208
+ - header: "Extras"
209
+ - question: "Enable any optional HUD features? (all hidden by default)"
210
+ - multiSelect: true
211
+ - options:
212
+ - "Tools activity" — Shows running/completed tools (◐ Edit: file.ts | ✓ Read ×3)
213
+ - "Agents & Todos" — Shows subagent status and todo progress
214
+ - "Session info" — Shows session duration and config counts (CLAUDE.md, rules, MCPs)
215
+ - "Session name" — Shows session slug or custom title from /rename
216
+ - "Custom line" — Display a custom phrase in the HUD
217
+
218
+ **If user selects any options**, write `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/claude-hud/config.json` (create directories if needed):
219
+
220
+ | Selection | Config keys |
221
+ |-----------|------------|
222
+ | Tools activity | `display.showTools: true` |
223
+ | Agents & Todos | `display.showAgents: true, display.showTodos: true` |
224
+ | Session info | `display.showDuration: true, display.showConfigCounts: true` |
225
+ | Session name | `display.showSessionName: true` |
226
+ | Custom line | `display.customLine: "<user's text>"` — ask user for the text (max 80 chars) |
227
+
228
+ Merge with existing config if the file already exists. Only write keys the user selected — don't write `false` for unselected items (defaults handle that).
229
+
230
+ **If user selects nothing** (or picks "Other" and says skip/none), do not create a config file. The defaults are fine.
231
+
232
+ ---
233
+
234
+ ## Step 5: Verify & Finish
235
+
236
+ **First, confirm the user has restarted Claude Code** since Step 3 wrote the config. If they haven't, ask them to restart before proceeding — the HUD cannot appear in the same session where setup was run.
237
+
238
+ Use AskUserQuestion:
239
+ - Question: "Setup complete! The HUD should appear below your input field. Is it working?"
240
+ - Options: "Yes, it's working" / "No, something's wrong"
241
+
242
+ **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.
243
+
244
+ **If no**: Debug systematically:
245
+
246
+ 1. **Restart Claude Code** (most common cause on macOS):
247
+ - The statusLine config requires a restart to take effect
248
+ - Quit Claude Code completely and run `claude` again, then re-run `/claude-hud:setup` to verify
249
+ - If you've already restarted, continue below
250
+
251
+ 2. **Verify config was applied**:
252
+ - Read settings file (`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json` or `$env:USERPROFILE\.claude\settings.json` on Windows)
253
+ - Check statusLine.command exists and looks correct
254
+ - If command contains a hardcoded version path (not using the dynamic version-lookup command), it may be a stale config from a previous setup
255
+
256
+ 3. **Test the command manually** and capture error output:
257
+ ```bash
258
+ {GENERATED_COMMAND} 2>&1
259
+ ```
260
+
261
+ 4. **Common issues to check**:
262
+
263
+ **"command not found" or empty output**:
264
+ - Runtime path might be wrong: `ls -la {RUNTIME_PATH}`
265
+ - On macOS with mise/nvm/asdf: the absolute path may have changed after a runtime update
266
+ - Symlinks may be stale: `command -v node` often returns a symlink that can break after version updates
267
+ - 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
268
+
269
+ **"No such file or directory" for plugin**:
270
+ - Plugin might not be installed: `ls "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/cache/claude-hud/"`
271
+ - Solution: reinstall plugin via marketplace
272
+
273
+ **Windows shell mismatch (for example, "bash not recognized")**:
274
+ - Command format does not match `Platform:` + `Shell:`
275
+ - Solution: re-run Step 1 branch logic and use the matching variant
276
+
277
+ **Windows: PowerShell execution policy error**:
278
+ - Run: `Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned`
279
+
280
+ **Permission denied**:
281
+ - Runtime not executable: `chmod +x {RUNTIME_PATH}`
282
+
283
+ **WSL confusion**:
284
+ - If using WSL, ensure plugin is installed in Linux environment, not Windows
285
+ - Check: `ls "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/cache/claude-hud/"`
286
+
287
+ 5. **If still stuck**: Show the user the exact command that was generated and the error, so they can report it or debug further
@@ -0,0 +1,31 @@
1
+ ---
2
+ description: Switch Claude HUD color theme
3
+ allowed-tools: Read, Write, AskUserQuestion
4
+ ---
5
+
6
+ # Switch Color Theme
7
+
8
+ ## Step 1: Show Current Theme
9
+
10
+ Read `~/.claude/plugins/claude-hud/config.json` and check the `theme` field.
11
+
12
+ ## Step 2: Choose Theme
13
+
14
+ Use AskUserQuestion:
15
+ - header: "Color Theme"
16
+ - question: "Choose a color theme (current: {currentTheme})"
17
+ - options:
18
+ - "Default" — Standard green/blue/yellow/red
19
+ - "Catppuccin Mocha" — Warm pastel dark theme (#a6e3a1, #89b4fa, #f9e2af, #f38ba8)
20
+ - "Dracula" — Purple-accented dark (#50fa7b, #8be9fd, #f1fa8c, #ff5555)
21
+ - "Nord" — Cool blue-toned (#a3be8c, #81a1c1, #ebcb8b, #bf616a)
22
+ - "Catppuccin Latte" — Light terminal variant (#40a02b, #1e66f5, #df8e1d, #d20f39)
23
+ - "Nord Light" — Light terminal variant (#a3be8c, #5e81ac, #ebcb8b, #bf616a)
24
+
25
+ ## Step 3: Apply
26
+
27
+ Read existing config, merge in `"theme": "<chosen>"`, write back.
28
+
29
+ Preserve all other config values.
30
+
31
+ Say "Theme changed to {name}! The HUD will reflect the change immediately."
@@ -0,0 +1,31 @@
1
+ import type { Alert, AlertAction } from './types.js';
2
+ interface AlertConfig {
3
+ context: {
4
+ warningThreshold: number;
5
+ criticalThreshold: number;
6
+ actions: AlertAction;
7
+ };
8
+ usage5h: {
9
+ warningThreshold: number;
10
+ criticalThreshold: number;
11
+ actions: AlertAction;
12
+ };
13
+ usage7d: {
14
+ warningThreshold: number;
15
+ actions: AlertAction;
16
+ };
17
+ }
18
+ interface EvaluateInput {
19
+ contextPercent: number;
20
+ usage5hPercent: number;
21
+ usage7dPercent: number;
22
+ estimatedCallsRemaining: number | null;
23
+ usageResetTime: string | null;
24
+ alertConfig: AlertConfig;
25
+ cacheDir: string;
26
+ }
27
+ export declare function evaluateAlerts(input: EvaluateInput): Alert[];
28
+ export declare function sendNotification(title: string, message: string): void;
29
+ export declare function shouldBell(alerts: Alert[], cacheDir: string): boolean;
30
+ export {};
31
+ //# sourceMappingURL=alert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../src/alert.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErD,UAAU,WAAW;IACnB,OAAO,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;IACvF,OAAO,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;IACvF,OAAO,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;CAC7D;AAED,UAAU,aAAa;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,EAAE,CAyB5D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAUrE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAarE"}
package/dist/alert.js ADDED
@@ -0,0 +1,53 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { readCache, writeCache } from './cache.js';
3
+ const BELL_STATE_KEY = 'alert-bell-state';
4
+ export function evaluateAlerts(input) {
5
+ const { contextPercent, usage5hPercent, usage7dPercent, estimatedCallsRemaining, usageResetTime, alertConfig } = input;
6
+ const alerts = [];
7
+ if (contextPercent >= alertConfig.context.criticalThreshold) {
8
+ const callsHint = estimatedCallsRemaining != null ? ` — ~${estimatedCallsRemaining} calls` : '';
9
+ alerts.push({ type: 'context-critical', message: `Context ${contextPercent}%${callsHint}`, actions: alertConfig.context.actions });
10
+ }
11
+ else if (contextPercent >= alertConfig.context.warningThreshold) {
12
+ const callsHint = estimatedCallsRemaining != null ? ` — ~${estimatedCallsRemaining} calls to autocompact` : '';
13
+ alerts.push({ type: 'context-warning', message: `Context ${contextPercent}%${callsHint}`, actions: alertConfig.context.actions });
14
+ }
15
+ if (usage5hPercent >= alertConfig.usage5h.criticalThreshold) {
16
+ const resetHint = usageResetTime ? ` — resets ${usageResetTime}` : '';
17
+ alerts.push({ type: 'usage-5h-critical', message: `Usage ${usage5hPercent}%${resetHint}`, actions: alertConfig.usage5h.actions });
18
+ }
19
+ else if (usage5hPercent >= alertConfig.usage5h.warningThreshold) {
20
+ const resetHint = usageResetTime ? ` — resets ${usageResetTime}` : '';
21
+ alerts.push({ type: 'usage-5h-warning', message: `Usage ${usage5hPercent}%${resetHint}`, actions: alertConfig.usage5h.actions });
22
+ }
23
+ if (usage7dPercent >= alertConfig.usage7d.warningThreshold) {
24
+ alerts.push({ type: 'usage-7d-warning', message: `7d Usage ${usage7dPercent}%`, actions: alertConfig.usage7d.actions });
25
+ }
26
+ return alerts;
27
+ }
28
+ export function sendNotification(title, message) {
29
+ if (process.platform !== 'darwin')
30
+ return;
31
+ try {
32
+ execFile('osascript', [
33
+ '-e', `display notification "${message.replace(/"/g, '\\"')}" with title "${title.replace(/"/g, '\\"')}"`
34
+ ], { timeout: 2000 });
35
+ }
36
+ catch {
37
+ // Silent fail — notification is best-effort
38
+ }
39
+ }
40
+ export function shouldBell(alerts, cacheDir) {
41
+ const bellAlerts = alerts.filter(a => a.actions.bell);
42
+ if (bellAlerts.length === 0)
43
+ return false;
44
+ const prevState = readCache(BELL_STATE_KEY, 24 * 60 * 60 * 1000, cacheDir) ?? [];
45
+ const currentTypes = bellAlerts.map(a => a.type);
46
+ const newTypes = currentTypes.filter(t => !prevState.includes(t));
47
+ if (newTypes.length > 0) {
48
+ writeCache(BELL_STATE_KEY, currentTypes, cacheDir);
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ //# sourceMappingURL=alert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.js","sourceRoot":"","sources":["../src/alert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { BurnRate } from './types.js';
2
+ export declare function recordTokenSnapshot(tokens: number, cacheDir: string, timestamp?: number): void;
3
+ export declare function calculateBurnRate(currentTokens: number, contextWindowSize: number, cacheDir: string): BurnRate | null;
4
+ //# sourceMappingURL=burn-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"burn-rate.d.ts","sourceRoot":"","sources":["../src/burn-rate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAY3C,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAO9F;AAED,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAsBrH"}
@@ -0,0 +1,36 @@
1
+ import { readCache, writeCache } from './cache.js';
2
+ const CACHE_KEY = 'burn-rate-snapshots';
3
+ const WINDOW_MS = 5 * 60 * 1000; // 5 minutes
4
+ const MIN_DATA_MS = 60 * 1000; // 60s minimum before showing
5
+ const SNAPSHOT_TTL = 10 * 60 * 1000; // 10 min cache TTL
6
+ export function recordTokenSnapshot(tokens, cacheDir, timestamp) {
7
+ const now = timestamp ?? Date.now();
8
+ const snapshots = readCache(CACHE_KEY, SNAPSHOT_TTL, cacheDir) ?? [];
9
+ snapshots.push({ tokens, timestamp: now });
10
+ const cutoff = now - WINDOW_MS;
11
+ const trimmed = snapshots.filter(s => s.timestamp >= cutoff);
12
+ writeCache(CACHE_KEY, trimmed, cacheDir);
13
+ }
14
+ export function calculateBurnRate(currentTokens, contextWindowSize, cacheDir) {
15
+ if (!Number.isFinite(currentTokens) || currentTokens <= 0)
16
+ return null;
17
+ if (!Number.isFinite(contextWindowSize) || contextWindowSize <= 0)
18
+ return null;
19
+ const snapshots = readCache(CACHE_KEY, SNAPSHOT_TTL, cacheDir);
20
+ if (!snapshots || snapshots.length < 2)
21
+ return null;
22
+ const oldest = snapshots[0];
23
+ const newest = snapshots[snapshots.length - 1];
24
+ const elapsedMs = newest.timestamp - oldest.timestamp;
25
+ if (elapsedMs < MIN_DATA_MS)
26
+ return null;
27
+ const tokenDelta = newest.tokens - oldest.tokens;
28
+ if (tokenDelta <= 0)
29
+ return null;
30
+ const tokensPerMinute = Math.round((tokenDelta / elapsedMs) * 60000);
31
+ const remaining = contextWindowSize - currentTokens;
32
+ const avgPerCall = tokenDelta / (snapshots.length - 1);
33
+ const estimatedCallsRemaining = avgPerCall > 0 ? Math.floor(remaining / avgPerCall) : 0;
34
+ return { tokensPerMinute, estimatedCallsRemaining };
35
+ }
36
+ //# sourceMappingURL=burn-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"burn-rate.js","sourceRoot":"","sources":["../src/burn-rate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAQnD,MAAM,SAAS,GAAG,qBAAqB,CAAC;AACxC,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAC7C,MAAM,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC,CAAG,6BAA6B;AAC9D,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,mBAAmB;AAExD,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,QAAgB,EAAE,SAAkB;IACtF,MAAM,GAAG,GAAG,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,SAAS,CAAkB,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtF,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;IAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;IAC7D,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,aAAqB,EAAE,iBAAyB,EAAE,QAAgB;IAClG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,iBAAiB,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/E,MAAM,SAAS,GAAG,SAAS,CAAkB,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;IAChF,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAEtD,IAAI,SAAS,GAAG,WAAW;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACjD,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,iBAAiB,GAAG,aAAa,CAAC;IACpD,MAAM,UAAU,GAAG,UAAU,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvD,MAAM,uBAAuB,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAExF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function readCache<T>(key: string, ttlMs: number, cacheDir: string, mtime?: number): T | null;
2
+ export declare function writeCache<T>(key: string, data: T, cacheDir: string, mtime?: number): void;
3
+ export declare function readLatency(cacheDir: string): number | null;
4
+ export declare function writeLatency(latencyMs: number, cacheDir: string): void;
5
+ export declare function getDefaultCacheDir(): string;
6
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AA8BA,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAOnG;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAI1F;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEtE;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C"}
package/dist/cache.js ADDED
@@ -0,0 +1,47 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ function getCacheFile(cacheDir) {
4
+ return path.join(cacheDir, 'cache.json');
5
+ }
6
+ function loadStore(cacheDir) {
7
+ try {
8
+ const file = getCacheFile(cacheDir);
9
+ if (!fs.existsSync(file))
10
+ return {};
11
+ return JSON.parse(fs.readFileSync(file, 'utf-8'));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ function saveStore(cacheDir, store) {
18
+ fs.mkdirSync(cacheDir, { recursive: true });
19
+ fs.writeFileSync(getCacheFile(cacheDir), JSON.stringify(store));
20
+ }
21
+ export function readCache(key, ttlMs, cacheDir, mtime) {
22
+ const store = loadStore(cacheDir);
23
+ const entry = store[key];
24
+ if (!entry)
25
+ return null;
26
+ if (Date.now() - entry.timestamp > ttlMs)
27
+ return null;
28
+ if (mtime !== undefined && entry.mtime !== mtime)
29
+ return null;
30
+ return entry.data;
31
+ }
32
+ export function writeCache(key, data, cacheDir, mtime) {
33
+ const store = loadStore(cacheDir);
34
+ store[key] = { data, timestamp: Date.now(), mtime };
35
+ saveStore(cacheDir, store);
36
+ }
37
+ export function readLatency(cacheDir) {
38
+ return readCache('api-latency-ms', 60000, cacheDir);
39
+ }
40
+ export function writeLatency(latencyMs, cacheDir) {
41
+ writeCache('api-latency-ms', latencyMs, cacheDir);
42
+ }
43
+ export function getDefaultCacheDir() {
44
+ const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(process.env.HOME || '', '.claude');
45
+ return path.join(configDir, 'plugins', 'claude-hud', '.cache');
46
+ }
47
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B,SAAS,YAAY,CAAC,QAAgB;IACpC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB;IACjC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB,EAAE,KAAiB;IACpD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,SAAS,CAAI,GAAW,EAAE,KAAa,EAAE,QAAgB,EAAE,KAAc;IACvF,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAA8B,CAAC;IACtD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAC9D,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,UAAU,CAAI,GAAW,EAAE,IAAO,EAAE,QAAgB,EAAE,KAAc;IAClF,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAClC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC;IACpD,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,SAAS,CAAS,gBAAgB,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,QAAgB;IAC9D,UAAU,CAAC,gBAAgB,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC;IAChG,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function getClaudeConfigDir(homeDir: string): string;
2
+ export declare function getClaudeConfigJsonPath(homeDir: string): string;
3
+ export declare function getHudPluginDir(homeDir: string): string;
4
+ //# sourceMappingURL=claude-config-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-config-dir.d.ts","sourceRoot":"","sources":["../src/claude-config-dir.ts"],"names":[],"mappings":"AAYA,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,24 @@
1
+ import * as path from 'node:path';
2
+ function expandHomeDirPrefix(inputPath, homeDir) {
3
+ if (inputPath === '~') {
4
+ return homeDir;
5
+ }
6
+ if (inputPath.startsWith('~/') || inputPath.startsWith('~\\')) {
7
+ return path.join(homeDir, inputPath.slice(2));
8
+ }
9
+ return inputPath;
10
+ }
11
+ export function getClaudeConfigDir(homeDir) {
12
+ const envConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim();
13
+ if (!envConfigDir) {
14
+ return path.join(homeDir, '.claude');
15
+ }
16
+ return path.resolve(expandHomeDirPrefix(envConfigDir, homeDir));
17
+ }
18
+ export function getClaudeConfigJsonPath(homeDir) {
19
+ return `${getClaudeConfigDir(homeDir)}.json`;
20
+ }
21
+ export function getHudPluginDir(homeDir) {
22
+ return path.join(getClaudeConfigDir(homeDir), 'plugins', 'claude-hud');
23
+ }
24
+ //# sourceMappingURL=claude-config-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-config-dir.js","sourceRoot":"","sources":["../src/claude-config-dir.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,SAAS,mBAAmB,CAAC,SAAiB,EAAE,OAAe;IAC7D,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAC3D,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function exportConfig(): string;
2
+ export declare function importConfig(jsonString: string): {
3
+ success: boolean;
4
+ error?: string;
5
+ };
6
+ //# sourceMappingURL=config-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-io.d.ts","sourceRoot":"","sources":["../src/config-io.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAarF"}
@@ -0,0 +1,27 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { getConfigPath } from './config.js';
4
+ export function exportConfig() {
5
+ try {
6
+ return fs.readFileSync(getConfigPath(), 'utf-8');
7
+ }
8
+ catch {
9
+ return '{}';
10
+ }
11
+ }
12
+ export function importConfig(jsonString) {
13
+ try {
14
+ const parsed = JSON.parse(jsonString);
15
+ if (typeof parsed !== 'object' || parsed === null) {
16
+ return { success: false, error: 'Invalid config: must be a JSON object' };
17
+ }
18
+ const dir = path.dirname(getConfigPath());
19
+ fs.mkdirSync(dir, { recursive: true });
20
+ fs.writeFileSync(getConfigPath(), JSON.stringify(parsed, null, 2));
21
+ return { success: true };
22
+ }
23
+ catch (e) {
24
+ return { success: false, error: `Parse error: ${e.message}` };
25
+ }
26
+ }
27
+ //# sourceMappingURL=config-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-io.js","sourceRoot":"","sources":["../src/config-io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACtC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAClD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,uCAAuC,EAAE,CAAC;QAC5E,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;QAC1C,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAiB,CAAW,CAAC,OAAO,EAAE,EAAE,CAAC;IAC3E,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface ConfigCounts {
2
+ claudeMdCount: number;
3
+ rulesCount: number;
4
+ mcpCount: number;
5
+ hooksCount: number;
6
+ }
7
+ export declare function countConfigs(cwd?: string): Promise<ConfigCounts>;
8
+ //# sourceMappingURL=config-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-reader.d.ts","sourceRoot":"","sources":["../src/config-reader.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AA4GD,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAkHtE"}