@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,236 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import { createDebug } from './debug.js';
5
+ import { getClaudeConfigDir, getClaudeConfigJsonPath } from './claude-config-dir.js';
6
+
7
+ const debug = createDebug('config');
8
+
9
+ export interface ConfigCounts {
10
+ claudeMdCount: number;
11
+ rulesCount: number;
12
+ mcpCount: number;
13
+ hooksCount: number;
14
+ }
15
+
16
+ // Valid keys for disabled MCP arrays in config files
17
+ type DisabledMcpKey = 'disabledMcpServers' | 'disabledMcpjsonServers';
18
+
19
+ function getMcpServerNames(filePath: string): Set<string> {
20
+ if (!fs.existsSync(filePath)) return new Set();
21
+ try {
22
+ const content = fs.readFileSync(filePath, 'utf8');
23
+ const config = JSON.parse(content);
24
+ if (config.mcpServers && typeof config.mcpServers === 'object') {
25
+ return new Set(Object.keys(config.mcpServers));
26
+ }
27
+ } catch (error) {
28
+ debug(`Failed to read MCP servers from ${filePath}:`, error);
29
+ }
30
+ return new Set();
31
+ }
32
+
33
+ function getDisabledMcpServers(filePath: string, key: DisabledMcpKey): Set<string> {
34
+ if (!fs.existsSync(filePath)) return new Set();
35
+ try {
36
+ const content = fs.readFileSync(filePath, 'utf8');
37
+ const config = JSON.parse(content);
38
+ if (Array.isArray(config[key])) {
39
+ const validNames = config[key].filter((s: unknown) => typeof s === 'string');
40
+ if (validNames.length !== config[key].length) {
41
+ debug(`${key} in ${filePath} contains non-string values, ignoring them`);
42
+ }
43
+ return new Set(validNames);
44
+ }
45
+ } catch (error) {
46
+ debug(`Failed to read ${key} from ${filePath}:`, error);
47
+ }
48
+ return new Set();
49
+ }
50
+
51
+ function countMcpServersInFile(filePath: string, excludeFrom?: string): number {
52
+ const servers = getMcpServerNames(filePath);
53
+ if (excludeFrom) {
54
+ const exclude = getMcpServerNames(excludeFrom);
55
+ for (const name of exclude) {
56
+ servers.delete(name);
57
+ }
58
+ }
59
+ return servers.size;
60
+ }
61
+
62
+ function countHooksInFile(filePath: string): number {
63
+ if (!fs.existsSync(filePath)) return 0;
64
+ try {
65
+ const content = fs.readFileSync(filePath, 'utf8');
66
+ const config = JSON.parse(content);
67
+ if (config.hooks && typeof config.hooks === 'object') {
68
+ return Object.keys(config.hooks).length;
69
+ }
70
+ } catch (error) {
71
+ debug(`Failed to read hooks from ${filePath}:`, error);
72
+ }
73
+ return 0;
74
+ }
75
+
76
+ function countRulesInDir(rulesDir: string): number {
77
+ if (!fs.existsSync(rulesDir)) return 0;
78
+ let count = 0;
79
+ try {
80
+ const entries = fs.readdirSync(rulesDir, { withFileTypes: true });
81
+ for (const entry of entries) {
82
+ const fullPath = path.join(rulesDir, entry.name);
83
+ if (entry.isDirectory()) {
84
+ count += countRulesInDir(fullPath);
85
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
86
+ count++;
87
+ }
88
+ }
89
+ } catch (error) {
90
+ debug(`Failed to read rules from ${rulesDir}:`, error);
91
+ }
92
+ return count;
93
+ }
94
+
95
+ function normalizePathForComparison(inputPath: string): string {
96
+ let normalized = path.normalize(path.resolve(inputPath));
97
+ const root = path.parse(normalized).root;
98
+ while (normalized.length > root.length && normalized.endsWith(path.sep)) {
99
+ normalized = normalized.slice(0, -1);
100
+ }
101
+ return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
102
+ }
103
+
104
+ function pathsReferToSameLocation(pathA: string, pathB: string): boolean {
105
+ if (normalizePathForComparison(pathA) === normalizePathForComparison(pathB)) {
106
+ return true;
107
+ }
108
+
109
+ if (!fs.existsSync(pathA) || !fs.existsSync(pathB)) {
110
+ return false;
111
+ }
112
+
113
+ try {
114
+ const realPathA = fs.realpathSync.native(pathA);
115
+ const realPathB = fs.realpathSync.native(pathB);
116
+ return normalizePathForComparison(realPathA) === normalizePathForComparison(realPathB);
117
+ } catch {
118
+ return false;
119
+ }
120
+ }
121
+
122
+ export async function countConfigs(cwd?: string): Promise<ConfigCounts> {
123
+ let claudeMdCount = 0;
124
+ let rulesCount = 0;
125
+ let hooksCount = 0;
126
+
127
+ const homeDir = os.homedir();
128
+ const claudeDir = getClaudeConfigDir(homeDir);
129
+
130
+ // Collect all MCP servers across scopes, then subtract disabled ones
131
+ const userMcpServers = new Set<string>();
132
+ const projectMcpServers = new Set<string>();
133
+
134
+ // === USER SCOPE ===
135
+
136
+ // ~/.claude/CLAUDE.md
137
+ if (fs.existsSync(path.join(claudeDir, 'CLAUDE.md'))) {
138
+ claudeMdCount++;
139
+ }
140
+
141
+ // ~/.claude/rules/*.md
142
+ rulesCount += countRulesInDir(path.join(claudeDir, 'rules'));
143
+
144
+ // ~/.claude/settings.json (MCPs and hooks)
145
+ const userSettings = path.join(claudeDir, 'settings.json');
146
+ for (const name of getMcpServerNames(userSettings)) {
147
+ userMcpServers.add(name);
148
+ }
149
+ hooksCount += countHooksInFile(userSettings);
150
+
151
+ // {CLAUDE_CONFIG_DIR}.json (additional user-scope MCPs)
152
+ const userClaudeJson = getClaudeConfigJsonPath(homeDir);
153
+ for (const name of getMcpServerNames(userClaudeJson)) {
154
+ userMcpServers.add(name);
155
+ }
156
+
157
+ // Get disabled user-scope MCPs from ~/.claude.json
158
+ const disabledUserMcps = getDisabledMcpServers(userClaudeJson, 'disabledMcpServers');
159
+ for (const name of disabledUserMcps) {
160
+ userMcpServers.delete(name);
161
+ }
162
+
163
+ // === PROJECT SCOPE ===
164
+
165
+ // Avoid double-counting when project .claude directory is the same location as user scope.
166
+ const projectClaudeDir = cwd ? path.join(cwd, '.claude') : null;
167
+ const projectClaudeOverlapsUserScope = projectClaudeDir
168
+ ? pathsReferToSameLocation(projectClaudeDir, claudeDir)
169
+ : false;
170
+
171
+ if (cwd) {
172
+ // {cwd}/CLAUDE.md
173
+ if (fs.existsSync(path.join(cwd, 'CLAUDE.md'))) {
174
+ claudeMdCount++;
175
+ }
176
+
177
+ // {cwd}/CLAUDE.local.md
178
+ if (fs.existsSync(path.join(cwd, 'CLAUDE.local.md'))) {
179
+ claudeMdCount++;
180
+ }
181
+
182
+ // {cwd}/.claude/CLAUDE.md (alternative location, skip when it is user scope)
183
+ if (!projectClaudeOverlapsUserScope && fs.existsSync(path.join(cwd, '.claude', 'CLAUDE.md'))) {
184
+ claudeMdCount++;
185
+ }
186
+
187
+ // {cwd}/.claude/CLAUDE.local.md
188
+ if (fs.existsSync(path.join(cwd, '.claude', 'CLAUDE.local.md'))) {
189
+ claudeMdCount++;
190
+ }
191
+
192
+ // {cwd}/.claude/rules/*.md (recursive)
193
+ // Skip when it overlaps with user-scope rules.
194
+ if (!projectClaudeOverlapsUserScope) {
195
+ rulesCount += countRulesInDir(path.join(cwd, '.claude', 'rules'));
196
+ }
197
+
198
+ // {cwd}/.mcp.json (project MCP config) - tracked separately for disabled filtering
199
+ const mcpJsonServers = getMcpServerNames(path.join(cwd, '.mcp.json'));
200
+
201
+ // {cwd}/.claude/settings.json (project settings)
202
+ // Skip when it overlaps with user-scope settings.
203
+ const projectSettings = path.join(cwd, '.claude', 'settings.json');
204
+ if (!projectClaudeOverlapsUserScope) {
205
+ for (const name of getMcpServerNames(projectSettings)) {
206
+ projectMcpServers.add(name);
207
+ }
208
+ hooksCount += countHooksInFile(projectSettings);
209
+ }
210
+
211
+ // {cwd}/.claude/settings.local.json (local project settings)
212
+ const localSettings = path.join(cwd, '.claude', 'settings.local.json');
213
+ for (const name of getMcpServerNames(localSettings)) {
214
+ projectMcpServers.add(name);
215
+ }
216
+ hooksCount += countHooksInFile(localSettings);
217
+
218
+ // Get disabled .mcp.json servers from settings.local.json
219
+ const disabledMcpJsonServers = getDisabledMcpServers(localSettings, 'disabledMcpjsonServers');
220
+ for (const name of disabledMcpJsonServers) {
221
+ mcpJsonServers.delete(name);
222
+ }
223
+
224
+ // Add remaining .mcp.json servers to project set
225
+ for (const name of mcpJsonServers) {
226
+ projectMcpServers.add(name);
227
+ }
228
+ }
229
+
230
+ // Total MCP count = user servers + project servers
231
+ // Note: Deduplication only occurs within each scope, not across scopes.
232
+ // A server with the same name in both user and project scope counts as 2 (separate configs).
233
+ const mcpCount = userMcpServers.size + projectMcpServers.size;
234
+
235
+ return { claudeMdCount, rulesCount, mcpCount, hooksCount };
236
+ }