aiblueprint-cli 1.4.12 → 1.4.13

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 (54) hide show
  1. package/claude-code-config/scripts/.claude/commands/fix-on-my-computer.md +87 -0
  2. package/claude-code-config/scripts/command-validator/CLAUDE.md +112 -0
  3. package/claude-code-config/scripts/command-validator/src/__tests__/validator.test.ts +62 -111
  4. package/claude-code-config/scripts/command-validator/src/cli.ts +5 -3
  5. package/claude-code-config/scripts/command-validator/src/lib/security-rules.ts +3 -4
  6. package/claude-code-config/scripts/command-validator/src/lib/types.ts +1 -0
  7. package/claude-code-config/scripts/command-validator/src/lib/validator.ts +47 -317
  8. package/claude-code-config/scripts/statusline/CLAUDE.md +29 -7
  9. package/claude-code-config/scripts/statusline/README.md +89 -1
  10. package/claude-code-config/scripts/statusline/defaults.json +75 -0
  11. package/claude-code-config/scripts/statusline/src/index.ts +101 -24
  12. package/claude-code-config/scripts/statusline/src/lib/config-types.ts +100 -0
  13. package/claude-code-config/scripts/statusline/src/lib/config.ts +21 -0
  14. package/claude-code-config/scripts/statusline/src/lib/context.ts +32 -11
  15. package/claude-code-config/scripts/statusline/src/lib/formatters.ts +360 -22
  16. package/claude-code-config/scripts/statusline/src/lib/git.ts +100 -0
  17. package/claude-code-config/scripts/statusline/src/lib/render-pure.ts +177 -0
  18. package/claude-code-config/scripts/statusline/src/lib/types.ts +11 -0
  19. package/claude-code-config/scripts/statusline/statusline.config.json +93 -0
  20. package/claude-code-config/skills/claude-memory/SKILL.md +689 -0
  21. package/claude-code-config/skills/claude-memory/references/comprehensive-example.md +175 -0
  22. package/claude-code-config/skills/claude-memory/references/project-patterns.md +334 -0
  23. package/claude-code-config/skills/claude-memory/references/prompting-techniques.md +411 -0
  24. package/claude-code-config/skills/claude-memory/references/section-templates.md +347 -0
  25. package/claude-code-config/skills/create-slash-commands/SKILL.md +1110 -0
  26. package/claude-code-config/skills/create-slash-commands/references/arguments.md +273 -0
  27. package/claude-code-config/skills/create-slash-commands/references/patterns.md +947 -0
  28. package/claude-code-config/skills/create-slash-commands/references/prompt-examples.md +656 -0
  29. package/claude-code-config/skills/create-slash-commands/references/tool-restrictions.md +389 -0
  30. package/claude-code-config/skills/create-subagents/SKILL.md +425 -0
  31. package/claude-code-config/skills/create-subagents/references/context-management.md +567 -0
  32. package/claude-code-config/skills/create-subagents/references/debugging-agents.md +714 -0
  33. package/claude-code-config/skills/create-subagents/references/error-handling-and-recovery.md +502 -0
  34. package/claude-code-config/skills/create-subagents/references/evaluation-and-testing.md +374 -0
  35. package/claude-code-config/skills/create-subagents/references/orchestration-patterns.md +591 -0
  36. package/claude-code-config/skills/create-subagents/references/subagents.md +599 -0
  37. package/claude-code-config/skills/create-subagents/references/writing-subagent-prompts.md +513 -0
  38. package/package.json +1 -1
  39. package/claude-code-config/commands/apex.md +0 -109
  40. package/claude-code-config/commands/tasks/run-task.md +0 -220
  41. package/claude-code-config/commands/utils/watch-ci.md +0 -47
  42. package/claude-code-config/scripts/command-validator/biome.json +0 -29
  43. package/claude-code-config/scripts/command-validator/bun.lockb +0 -0
  44. package/claude-code-config/scripts/command-validator/package.json +0 -27
  45. package/claude-code-config/scripts/command-validator/vitest.config.ts +0 -7
  46. package/claude-code-config/scripts/hook-post-file.ts +0 -162
  47. package/claude-code-config/scripts/statusline/biome.json +0 -34
  48. package/claude-code-config/scripts/statusline/bun.lockb +0 -0
  49. package/claude-code-config/scripts/statusline/fixtures/test-input.json +0 -25
  50. package/claude-code-config/scripts/statusline/package.json +0 -19
  51. package/claude-code-config/scripts/statusline/statusline.config.ts +0 -25
  52. package/claude-code-config/scripts/statusline/test.ts +0 -20
  53. package/claude-code-config/scripts/validate-command.js +0 -712
  54. package/claude-code-config/scripts/validate-command.readme.md +0 -283
@@ -1,360 +1,90 @@
1
- import { SAFE_COMMANDS, SECURITY_RULES } from "./security-rules";
2
1
  import type { ValidationResult } from "./types";
3
2
 
3
+ const DANGEROUS_COMMANDS = [
4
+ "sudo",
5
+ "su",
6
+ "chmod",
7
+ "chown",
8
+ "dd",
9
+ "mkfs",
10
+ "fdisk",
11
+ "kill",
12
+ "killall",
13
+ ];
14
+
4
15
  export class CommandValidator {
5
- validate(command: string, toolName = "Unknown"): ValidationResult {
16
+ validate(command: string, _toolName = "Unknown"): ValidationResult {
6
17
  const result: ValidationResult = {
7
18
  isValid: true,
8
19
  severity: "LOW",
9
20
  violations: [],
10
21
  sanitizedCommand: command,
22
+ action: "allow",
11
23
  };
12
24
 
13
25
  if (!command || typeof command !== "string") {
14
26
  result.isValid = false;
15
27
  result.violations.push("Invalid command format");
28
+ result.action = "deny";
16
29
  return result;
17
30
  }
18
31
 
19
- if (command.length > 2000) {
20
- result.isValid = false;
21
- result.severity = "MEDIUM";
22
- result.violations.push("Command too long (potential buffer overflow)");
23
- return result;
24
- }
25
-
26
- if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]/.test(command)) {
27
- result.isValid = false;
28
- result.severity = "HIGH";
29
- result.violations.push("Binary or encoded content detected");
30
- return result;
31
- }
32
-
33
- const normalizedCmd = command.trim().toLowerCase();
34
- const cmdParts = normalizedCmd.split(/\s+/);
35
- const mainCommand = cmdParts[0].split("/").pop() || "";
36
-
37
- if (mainCommand === "source" || mainCommand === "python") {
38
- return result;
39
- }
40
-
41
- for (const pattern of SECURITY_RULES.DANGEROUS_PATTERNS) {
42
- if (pattern.test(command)) {
43
- result.isValid = false;
44
- result.severity = "CRITICAL";
45
- result.violations.push(`Dangerous pattern detected: ${pattern.source}`);
46
- }
47
- }
48
-
49
- if (SECURITY_RULES.CRITICAL_COMMANDS.includes(mainCommand)) {
32
+ // rm -rf → DENY (blocked completely)
33
+ if (this.containsRmRf(command)) {
50
34
  result.isValid = false;
51
35
  result.severity = "CRITICAL";
52
- result.violations.push(`Critical dangerous command: ${mainCommand}`);
53
- }
54
-
55
- if (SECURITY_RULES.PRIVILEGE_COMMANDS.includes(mainCommand)) {
56
- result.isValid = false;
57
- result.severity = "HIGH";
58
- result.violations.push(`Privilege escalation command: ${mainCommand}`);
59
- }
60
-
61
- if (SECURITY_RULES.NETWORK_COMMANDS.includes(mainCommand)) {
62
- result.isValid = false;
63
- result.severity = "HIGH";
64
- result.violations.push(`Network/remote access command: ${mainCommand}`);
65
- }
66
-
67
- if (SECURITY_RULES.SYSTEM_COMMANDS.includes(mainCommand)) {
68
- result.isValid = false;
69
- result.severity = "HIGH";
70
- result.violations.push(`System manipulation command: ${mainCommand}`);
71
- }
72
-
73
- if (/rm\s+.*-rf\s/.test(command)) {
74
- const isRmRfSafe = this.isRmRfCommandSafe(command);
75
- if (!isRmRfSafe) {
76
- result.isValid = false;
77
- result.severity = "CRITICAL";
78
- result.violations.push("rm -rf command targeting unsafe path");
79
- }
80
- }
81
-
82
- if (SAFE_COMMANDS.includes(mainCommand) && result.violations.length === 0) {
83
- return result;
84
- }
85
-
86
- if (command.includes("&&")) {
87
- const chainedCommands = this.splitCommandChain(command);
88
- let allSafe = true;
89
- for (const chainedCmd of chainedCommands) {
90
- const trimmedCmd = chainedCmd.trim();
91
- const cmdParts = trimmedCmd.split(/\s+/);
92
- const mainCommand = cmdParts[0];
93
-
94
- if (
95
- mainCommand === "source" ||
96
- mainCommand === "python" ||
97
- SAFE_COMMANDS.includes(mainCommand)
98
- ) {
99
- continue;
100
- }
101
-
102
- const chainResult = this.validateSingleCommand(trimmedCmd, toolName);
103
- if (!chainResult.isValid) {
104
- result.isValid = false;
105
- result.severity = chainResult.severity;
106
- result.violations.push(
107
- `Chained command violation: ${trimmedCmd} - ${chainResult.violations.join(", ")}`,
108
- );
109
- allSafe = false;
110
- }
111
- }
112
- if (allSafe) {
113
- return result;
114
- }
115
- }
116
-
117
- if (command.includes(";") || command.includes("||")) {
118
- const chainedCommands = this.splitCommandChain(command);
119
- for (const chainedCmd of chainedCommands) {
120
- const chainResult = this.validateSingleCommand(
121
- chainedCmd.trim(),
122
- toolName,
123
- );
124
- if (!chainResult.isValid) {
125
- result.isValid = false;
126
- result.severity = chainResult.severity;
127
- result.violations.push(
128
- `Chained command violation: ${chainedCmd.trim()} - ${chainResult.violations.join(", ")}`,
129
- );
130
- }
131
- }
36
+ result.violations.push("rm -rf is forbidden - use trash instead");
37
+ result.action = "deny";
132
38
  return result;
133
39
  }
134
40
 
135
- for (const path of SECURITY_RULES.PROTECTED_PATHS) {
136
- if (command.includes(path)) {
137
- if (
138
- path === "/dev/" &&
139
- (command.includes("/dev/null") ||
140
- command.includes("/dev/stderr") ||
141
- command.includes("/dev/stdout"))
142
- ) {
143
- continue;
144
- }
145
-
146
- const cmdStart = command.trim();
147
- let isSafeExecutable = false;
148
- for (const safePath of SECURITY_RULES.SAFE_EXECUTABLE_PATHS) {
149
- if (cmdStart.startsWith(safePath)) {
150
- isSafeExecutable = true;
151
- break;
152
- }
153
- }
154
-
155
- const pathIndex = command.indexOf(path);
156
- const beforePath = command.substring(0, pathIndex);
157
- const redirectBeforePath = />\s*$/.test(beforePath.trim());
158
-
159
- if (!isSafeExecutable && redirectBeforePath) {
160
- result.isValid = false;
161
- result.severity = "HIGH";
162
- result.violations.push(
163
- `Dangerous operation on protected path: ${path}`,
164
- );
165
- }
166
- }
167
- }
168
-
169
- return result;
170
- }
171
-
172
- validateSingleCommand(
173
- command: string,
174
- _toolName = "Unknown",
175
- ): ValidationResult {
176
- const result: ValidationResult = {
177
- isValid: true,
178
- severity: "LOW",
179
- violations: [],
180
- sanitizedCommand: command,
181
- };
182
-
183
- if (!command || typeof command !== "string") {
184
- result.isValid = false;
185
- result.violations.push("Invalid command format");
186
- return result;
187
- }
188
-
189
- if (command.length > 2000) {
190
- result.isValid = false;
191
- result.severity = "MEDIUM";
192
- result.violations.push("Command too long (potential buffer overflow)");
193
- return result;
194
- }
195
-
196
- if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]/.test(command)) {
41
+ // Other dangerous commands → ASK (ask for permission)
42
+ const dangerousCmd = this.containsDangerousCommand(command);
43
+ if (dangerousCmd) {
197
44
  result.isValid = false;
198
45
  result.severity = "HIGH";
199
- result.violations.push("Binary or encoded content detected");
200
- return result;
201
- }
202
-
203
- const normalizedCmd = command.trim().toLowerCase();
204
- const cmdParts = normalizedCmd.split(/\s+/);
205
- const mainCommand = cmdParts[0].split("/").pop() || "";
206
-
207
- if (mainCommand === "source" || mainCommand === "python") {
46
+ result.violations.push(`Potentially dangerous command: ${dangerousCmd}`);
47
+ result.action = "ask";
208
48
  return result;
209
49
  }
210
50
 
211
- for (const pattern of SECURITY_RULES.DANGEROUS_PATTERNS) {
212
- if (pattern.test(command)) {
213
- result.isValid = false;
214
- result.severity = "CRITICAL";
215
- result.violations.push(`Dangerous pattern detected: ${pattern.source}`);
216
- }
217
- }
218
-
219
- if (SECURITY_RULES.CRITICAL_COMMANDS.includes(mainCommand)) {
220
- result.isValid = false;
221
- result.severity = "CRITICAL";
222
- result.violations.push(`Critical dangerous command: ${mainCommand}`);
223
- }
224
-
225
- if (SECURITY_RULES.PRIVILEGE_COMMANDS.includes(mainCommand)) {
226
- result.isValid = false;
227
- result.severity = "HIGH";
228
- result.violations.push(`Privilege escalation command: ${mainCommand}`);
229
- }
230
-
231
- if (SECURITY_RULES.NETWORK_COMMANDS.includes(mainCommand)) {
232
- result.isValid = false;
233
- result.severity = "HIGH";
234
- result.violations.push(`Network/remote access command: ${mainCommand}`);
235
- }
236
-
237
- if (SECURITY_RULES.SYSTEM_COMMANDS.includes(mainCommand)) {
238
- result.isValid = false;
239
- result.severity = "HIGH";
240
- result.violations.push(`System manipulation command: ${mainCommand}`);
241
- }
242
-
243
- if (/rm\s+.*-rf\s/.test(command)) {
244
- const isRmRfSafe = this.isRmRfCommandSafe(command);
245
- if (!isRmRfSafe) {
246
- result.isValid = false;
247
- result.severity = "CRITICAL";
248
- result.violations.push("rm -rf command targeting unsafe path");
249
- }
250
- }
251
-
252
- if (SAFE_COMMANDS.includes(mainCommand) && result.violations.length === 0) {
253
- return result;
254
- }
255
-
256
- for (const path of SECURITY_RULES.PROTECTED_PATHS) {
257
- if (command.includes(path)) {
258
- if (
259
- path === "/dev/" &&
260
- (command.includes("/dev/null") ||
261
- command.includes("/dev/stderr") ||
262
- command.includes("/dev/stdout"))
263
- ) {
264
- continue;
265
- }
266
-
267
- const cmdStart = command.trim();
268
- let isSafeExecutable = false;
269
- for (const safePath of SECURITY_RULES.SAFE_EXECUTABLE_PATHS) {
270
- if (cmdStart.startsWith(safePath)) {
271
- isSafeExecutable = true;
272
- break;
273
- }
274
- }
275
-
276
- const pathIndex = command.indexOf(path);
277
- const beforePath = command.substring(0, pathIndex);
278
- const redirectBeforePath = />\s*$/.test(beforePath.trim());
279
-
280
- if (!isSafeExecutable && redirectBeforePath) {
281
- result.isValid = false;
282
- result.severity = "HIGH";
283
- result.violations.push(
284
- `Dangerous operation on protected path: ${path}`,
285
- );
286
- }
287
- }
288
- }
289
-
290
51
  return result;
291
52
  }
292
53
 
293
- splitCommandChain(command: string): string[] {
294
- const commands: string[] = [];
295
- let current = "";
296
- let inQuotes = false;
297
- let quoteChar = "";
298
-
299
- for (let i = 0; i < command.length; i++) {
300
- const char = command[i];
301
- const nextChar = command[i + 1];
54
+ containsRmRf(command: string): boolean {
55
+ // Check for rm -rf in any form (rm -rf, rm -fr, rm -r -f, etc.)
56
+ const rmRfPatterns = [
57
+ /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*)\s/i,
58
+ /\brm\s+-r\s+-f\s/i,
59
+ /\brm\s+-f\s+-r\s/i,
60
+ ];
302
61
 
303
- if ((char === '"' || char === "'") && !inQuotes) {
304
- inQuotes = true;
305
- quoteChar = char;
306
- current += char;
307
- } else if (char === quoteChar && inQuotes) {
308
- inQuotes = false;
309
- quoteChar = "";
310
- current += char;
311
- } else if (inQuotes) {
312
- current += char;
313
- } else if (char === "&" && nextChar === "&") {
314
- commands.push(current.trim());
315
- current = "";
316
- i++;
317
- } else if (char === "|" && nextChar === "|") {
318
- commands.push(current.trim());
319
- current = "";
320
- i++;
321
- } else if (char === ";") {
322
- commands.push(current.trim());
323
- current = "";
324
- } else {
325
- current += char;
62
+ for (const pattern of rmRfPatterns) {
63
+ if (pattern.test(command)) {
64
+ return true;
326
65
  }
327
66
  }
328
67
 
329
- if (current.trim()) {
330
- commands.push(current.trim());
331
- }
332
-
333
- return commands.filter((cmd) => cmd.length > 0);
68
+ return false;
334
69
  }
335
70
 
336
- isRmRfCommandSafe(command: string): boolean {
337
- const rmRfMatch = command.match(/rm\s+.*-rf\s+([^\s;&|]+)/);
338
- if (!rmRfMatch) {
339
- return false;
340
- }
341
-
342
- const targetPath = rmRfMatch[1];
71
+ containsDangerousCommand(command: string): string | null {
72
+ const normalizedCmd = command.trim().toLowerCase();
73
+ const parts = normalizedCmd.split(/\s+/);
74
+ const mainCommand = parts[0].split("/").pop() || "";
343
75
 
344
- if (targetPath === "/" || targetPath.endsWith("/")) {
345
- return false;
76
+ if (DANGEROUS_COMMANDS.includes(mainCommand)) {
77
+ return mainCommand;
346
78
  }
347
79
 
348
- for (const safePath of SECURITY_RULES.SAFE_RM_PATHS) {
349
- if (targetPath.startsWith(safePath)) {
350
- return true;
80
+ // Check in chained commands
81
+ for (const dangerous of DANGEROUS_COMMANDS) {
82
+ const pattern = new RegExp(`\\b${dangerous}\\b`, "i");
83
+ if (pattern.test(command)) {
84
+ return dangerous;
351
85
  }
352
86
  }
353
87
 
354
- if (!targetPath.startsWith("/")) {
355
- return true;
356
- }
357
-
358
- return false;
88
+ return null;
359
89
  }
360
90
  }
@@ -7,6 +7,7 @@ Clean, type-safe statusline implementation for Claude Code using Bun + TypeScrip
7
7
  ## Project Setup & Configuration
8
8
 
9
9
  ### Dependencies
10
+
10
11
  - **Bun**: Runtime (uses `$` for shell commands)
11
12
  - **@biomejs/biome**: Linting & formatting
12
13
  - **TypeScript**: Type safety
@@ -29,16 +30,12 @@ Add to `~/.claude/settings.json`:
29
30
 
30
31
  ### Authentication
31
32
 
32
- OAuth token storage (platform-specific):
33
+ OAuth token stored in macOS Keychain:
33
34
 
34
- **macOS**: Stored in Keychain
35
35
  - **Service**: `Claude Code-credentials`
36
- - **Access**: `security find-generic-password -s "Claude Code-credentials" -w`
37
-
38
- **Windows & Linux**: Stored in file system
39
- - **Location**: `~/.claude/.credentials.json`
40
36
  - **Format**: JSON with `claudeAiOauth.accessToken`
41
37
  - **Token type**: `sk-ant-oat01-...` (OAuth token, not API key)
38
+ - **Access**: `security find-generic-password -s "Claude Code-credentials" -w`
42
39
 
43
40
  ## Architecture
44
41
 
@@ -78,6 +75,7 @@ Claude Code Hook → stdin JSON → index.ts
78
75
  ## Component Specifications
79
76
 
80
77
  ### Context Calculation (`lib/context.ts`)
78
+
81
79
  - **Purpose**: Calculate token usage from Claude Code transcript files
82
80
  - **Algorithm**: Parses `.jsonl` transcript, finds most recent main-chain entry
83
81
  - **Tokens counted**: `input_tokens + cache_read_input_tokens + cache_creation_input_tokens`
@@ -85,6 +83,7 @@ Claude Code Hook → stdin JSON → index.ts
85
83
  - **Output**: `{ tokens: number, percentage: number }` (0-100% of 200k context)
86
84
 
87
85
  ### Usage Limits (`lib/usage-limits.ts`)
86
+
88
87
  - **Purpose**: Fetch Claude API rate limits from OAuth endpoint
89
88
  - **Auth**: Retrieves OAuth token from macOS Keychain (`Claude Code-credentials`)
90
89
  - **API**: `https://api.anthropic.com/api/oauth/usage`
@@ -92,12 +91,14 @@ Claude Code Hook → stdin JSON → index.ts
92
91
  - **Error handling**: Fails silently, returns null on errors
93
92
 
94
93
  ### Git Status (`lib/git.ts`)
94
+
95
95
  - **Purpose**: Show current branch and uncommitted changes
96
96
  - **Detection**: Checks both staged and unstaged changes
97
97
  - **Output**: Branch name + line additions/deletions
98
98
  - **Display**: `main* (+123 -45)` with color coding
99
99
 
100
100
  ### Formatters (`lib/formatters.ts`)
101
+
101
102
  - **Colors**: ANSI color codes for terminal output
102
103
  - **Token display**: `62.5K`, `1.2M` format
103
104
  - **Time formatting**: `3h21m`, `45m` for countdowns
@@ -106,16 +107,19 @@ Claude Code Hook → stdin JSON → index.ts
106
107
  ## Output Specification
107
108
 
108
109
  ### Line 1: Session Info
110
+
109
111
  ```
110
112
  main* (+123 -45) | ~/.claude | Sonnet 4.5
111
113
  ```
112
114
 
113
115
  ### Line 2: Metrics
116
+
114
117
  ```
115
118
  $0.17 (6m) | 62.5K tokens | 31% | 15% (3h27m)
116
119
  ```
117
120
 
118
121
  **Components:**
122
+
119
123
  - `$0.17` - Session cost (USD)
120
124
  - `(6m)` - Session duration
121
125
  - `62.5K tokens` - Context tokens used (from transcript)
@@ -147,6 +151,7 @@ echo '{ ... }' | bun run start
147
151
  ### Error Handling & Performance
148
152
 
149
153
  **Error Handling** - All components fail silently:
154
+
150
155
  - Missing transcript → 0 tokens, 0%
151
156
  - API failure → No usage limits shown
152
157
  - Git errors → "no-git" branch
@@ -155,6 +160,7 @@ echo '{ ... }' | bun run start
155
160
  This ensures statusline never crashes Claude Code.
156
161
 
157
162
  **Performance Benchmarks:**
163
+
158
164
  - Context calculation: ~10-50ms (depends on transcript size)
159
165
  - API call: ~100-300ms (cached by Claude API)
160
166
  - Git operations: ~20-50ms
@@ -177,7 +183,23 @@ This ensures statusline never crashes Claude Code.
177
183
 
178
184
  ## Known Limitations
179
185
 
186
+ - macOS only (uses Keychain)
180
187
  - Requires `git` CLI for git status
181
188
  - Requires Claude Code OAuth (not API key)
182
189
  - Transcript must be accessible (permissions)
183
- - Cross-platform support: macOS (Keychain), Windows & Linux (file-based credentials)
190
+
191
+ ## Critical Requirements
192
+
193
+ ### Configuration Updates
194
+
195
+ - **CRITICAL**: When updating `statusline.config.json` or `statusline.config.ts`, you **MUST** update the interactive demo in `src/commands/interactive-config.ts`
196
+ - **ALWAYS** run `bun run config` after config changes to verify the interactive demo works correctly
197
+ - **REQUIRED**: Keep config file structure in sync with interactive prompts
198
+
199
+ ### Runtime & Dependencies
200
+
201
+ - **ALWAYS** use Bun for all commands and runtime operations
202
+ - Use `bun run <script>` instead of `npm run` or `pnpm run`
203
+ - Use `bun install` for dependency management
204
+ - **AUTHORIZED LIBRARIES**: `@biomejs/biome` for linting/formatting, third-party libraries like `tiers` are permitted if needed
205
+ - **NEVER** add external npm packages without verification - prefer Bun APIs first
@@ -9,6 +9,8 @@ Clean, modular statusline for Claude Code with TypeScript + Bun.
9
9
  - 🧩 Context tokens used
10
10
  - 📊 Context percentage (0-100%)
11
11
  - ⏱️ Five-hour usage limit with reset time
12
+ - 📅 Weekly usage limit with configurable threshold
13
+ - 📈 Daily usage percentage tracking and statistics
12
14
 
13
15
  ## Structure
14
16
 
@@ -38,6 +40,12 @@ bun run spend:today
38
40
  # View this month's spending
39
41
  bun run spend:month
40
42
 
43
+ # View usage statistics
44
+ bun run stats
45
+
46
+ # Interactive config demo
47
+ bun run demo
48
+
41
49
  # Format code
42
50
  bun run format
43
51
 
@@ -45,7 +53,9 @@ bun run format
45
53
  bun run lint
46
54
  ```
47
55
 
48
- ## Spend Tracking
56
+ ## Tracking Features
57
+
58
+ ### Spend Tracking
49
59
 
50
60
  The statusline automatically saves session data to `data/spend.json`. You can view your spending with:
51
61
 
@@ -63,6 +73,84 @@ Each session tracks:
63
73
  - Lines added/removed
64
74
  - Working directory
65
75
 
76
+ ### Usage Statistics
77
+
78
+ Daily usage percentages are automatically tracked in `data/daily-usage.json`. Each 5-hour rate limit period is tracked separately using the `resets_at` timestamp as a unique key.
79
+
80
+ ```bash
81
+ bun run stats
82
+ ```
83
+
84
+ This shows:
85
+ - Average daily usage percentage across all tracked days
86
+ - Total days and total 5-hour periods tracked
87
+ - Recent 7-day usage history with visual bars
88
+ - Per-day statistics: average, max, min across all 5-hour periods
89
+ - Data is kept for 90 days
90
+
91
+ **How it works:**
92
+ - Each `resets_at` value represents a unique 5-hour rate limit period
93
+ - Multiple 5-hour periods can occur in a single day
94
+ - If the API is called multiple times during the same 5-hour period, only the latest value is kept
95
+ - Daily statistics show the average, maximum, and minimum usage across all periods in that day
96
+
97
+ ## Interactive Demo
98
+
99
+ Explore all configuration options with a live preview:
100
+
101
+ ```bash
102
+ bun run demo
103
+ ```
104
+
105
+ This opens an interactive menu where you can:
106
+ - Toggle any config option with arrow keys and spacebar
107
+ - See instant preview of how the statusline changes
108
+ - Navigate through all available settings
109
+ - Reset to defaults with `R`
110
+ - Explore session, limits, weekly usage, and git display options
111
+
112
+ **Controls:**
113
+ - `↑↓` or `j/k` - Navigate options
114
+ - `Space` - Toggle selected option
115
+ - `R` - Reset to defaults
116
+ - `Q` - Quit
117
+
118
+ ## Configuration
119
+
120
+ The statusline can be customized via `statusline.config.ts`. Key configuration options:
121
+
122
+ ### Weekly Usage Display
123
+
124
+ ```typescript
125
+ weeklyUsage: {
126
+ enabled: boolean | "90%", // true: always show, false: never, "90%": show when 5-hour usage >= 90%
127
+ showTimeLeft: boolean,
128
+ percentage: {
129
+ enabled: boolean,
130
+ progressBar: {
131
+ enabled: boolean,
132
+ length: 5 | 10 | 15,
133
+ style: "filled" | "rectangle" | "braille",
134
+ color: "progressive" | "green" | "yellow" | "red"
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ **Default:** `enabled: "90%"` - Weekly limits appear when your 5-hour usage reaches 90%
141
+
142
+ Display format: `W: ⣿⣿⣧⣀⣀⣀⣀⣀⣀⣀ 45% (6d12h)`
143
+
144
+ ### Other Configuration Options
145
+
146
+ - **Session display**: Cost, tokens, context percentage
147
+ - **Limits display**: Five-hour usage limits
148
+ - **Git display**: Branch, changes, staged/unstaged files
149
+ - **Path display**: Full, truncated, or basename modes
150
+ - **Progress bars**: Multiple styles and color schemes
151
+
152
+ See `statusline.config.ts` for all available options and defaults.
153
+
66
154
  ## Usage in Claude Code
67
155
 
68
156
  Update your `~/.claude/settings.json`: