aiblueprint-cli 1.4.81 → 1.4.82

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 (80) hide show
  1. package/README.md +21 -17
  2. package/agents-config/claude-config/scripts/statusline/data/.gitignore +8 -0
  3. package/agents-config/claude-config/scripts/statusline/data/.gitkeep +0 -0
  4. package/agents-config/claude-config/scripts/statusline/defaults.json +10 -10
  5. package/agents-config/claude-config/scripts/statusline/src/commands/interactive-config.ts +118 -6
  6. package/agents-config/claude-config/scripts/statusline/src/index.ts +100 -9
  7. package/agents-config/claude-config/scripts/statusline/src/lib/config.ts +28 -2
  8. package/agents-config/claude-config/scripts/statusline/src/lib/features/limits/commands/weekly-analysis.ts +108 -0
  9. package/agents-config/claude-config/scripts/statusline/src/lib/features/limits/index.ts +164 -0
  10. package/agents-config/claude-config/scripts/statusline/src/lib/features/limits/types.ts +15 -0
  11. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/commands/migrate-to-sqlite.ts +136 -0
  12. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/commands/spend-day.ts +79 -0
  13. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/commands/spend-month.ts +66 -0
  14. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/commands/spend-project.ts +85 -0
  15. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/database.ts +395 -0
  16. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/index.ts +178 -0
  17. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/payload-logger.ts +163 -0
  18. package/agents-config/claude-config/scripts/statusline/src/lib/features/spend/types.ts +37 -0
  19. package/agents-config/claude-config/scripts/statusline/src/lib/presets.ts +13 -13
  20. package/agents-config/claude-config/scripts/statusline/statusline.config.free.json +4 -1
  21. package/agents-config/claude-config/scripts/statusline/statusline.config.json +44 -23
  22. package/agents-config/scripts/statusline/data/.gitignore +8 -0
  23. package/agents-config/scripts/statusline/data/.gitkeep +0 -0
  24. package/agents-config/scripts/statusline/defaults.json +10 -10
  25. package/agents-config/scripts/statusline/src/commands/interactive-config.ts +118 -6
  26. package/agents-config/scripts/statusline/src/index.ts +100 -9
  27. package/agents-config/scripts/statusline/src/lib/config.ts +28 -2
  28. package/agents-config/scripts/statusline/src/lib/features/limits/commands/weekly-analysis.ts +108 -0
  29. package/agents-config/scripts/statusline/src/lib/features/limits/index.ts +164 -0
  30. package/agents-config/scripts/statusline/src/lib/features/limits/types.ts +15 -0
  31. package/agents-config/scripts/statusline/src/lib/features/spend/commands/migrate-to-sqlite.ts +136 -0
  32. package/agents-config/scripts/statusline/src/lib/features/spend/commands/spend-day.ts +79 -0
  33. package/agents-config/scripts/statusline/src/lib/features/spend/commands/spend-month.ts +66 -0
  34. package/agents-config/scripts/statusline/src/lib/features/spend/commands/spend-project.ts +85 -0
  35. package/agents-config/scripts/statusline/src/lib/features/spend/database.ts +395 -0
  36. package/agents-config/scripts/statusline/src/lib/features/spend/index.ts +178 -0
  37. package/agents-config/scripts/statusline/src/lib/features/spend/payload-logger.ts +163 -0
  38. package/agents-config/scripts/statusline/src/lib/features/spend/types.ts +37 -0
  39. package/agents-config/scripts/statusline/src/lib/presets.ts +13 -13
  40. package/agents-config/scripts/statusline/statusline.config.free.json +4 -1
  41. package/agents-config/scripts/statusline/statusline.config.json +44 -23
  42. package/agents-config/skills/agents-managers/SKILL.md +1 -1
  43. package/agents-config/skills/agents-managers/references/writing-agent-prompts.md +1 -1
  44. package/agents-config/{commands/prompts/create-vitejs-app.md → skills/create-vitejs-app/SKILL.md} +2 -1
  45. package/agents-config/skills/environments-manager/SKILL.md +6 -6
  46. package/agents-config/skills/environments-manager/examples/{claude/commands/dev.md → skills/dev/SKILL.md} +2 -1
  47. package/agents-config/skills/environments-manager/examples/{claude/commands/lint.md → skills/lint/SKILL.md} +2 -1
  48. package/agents-config/skills/environments-manager/examples/{claude/commands/test.md → skills/test/SKILL.md} +2 -1
  49. package/agents-config/skills/environments-manager/examples/{claude/commands/typecheck.md → skills/typecheck/SKILL.md} +2 -1
  50. package/agents-config/skills/environments-manager/references/claude.md +13 -9
  51. package/agents-config/skills/environments-manager/references/cursor.md +2 -2
  52. package/agents-config/{commands/prompts/nextjs-add-prisma-db.md → skills/nextjs-add-prisma-db/SKILL.md} +1 -0
  53. package/agents-config/{commands/prompts/nextjs-setup-better-auth.md → skills/nextjs-setup-better-auth/SKILL.md} +1 -0
  54. package/agents-config/{commands/prompts/nextjs-setup-project.md → skills/nextjs-setup-project/SKILL.md} +1 -0
  55. package/agents-config/{commands/prompts/prompt.md → skills/prompt/SKILL.md} +5 -0
  56. package/agents-config/{commands/prompts/saas-challenge-idea.md → skills/saas-challenge-idea/SKILL.md} +1 -0
  57. package/agents-config/{commands/prompts/saas-create-architecture.md → skills/saas-create-architecture/SKILL.md} +1 -0
  58. package/agents-config/{commands/prompts/saas-create-headline.md → skills/saas-create-headline/SKILL.md} +1 -0
  59. package/agents-config/{commands/prompts/saas-create-landing-copywritting.md → skills/saas-create-landing-copywritting/SKILL.md} +1 -0
  60. package/agents-config/{commands/prompts/saas-create-legals-docs.md → skills/saas-create-legals-docs/SKILL.md} +1 -0
  61. package/agents-config/{commands/prompts/saas-create-logos.md → skills/saas-create-logos/SKILL.md} +1 -0
  62. package/agents-config/{commands/prompts/saas-create-prd.md → skills/saas-create-prd/SKILL.md} +4 -3
  63. package/agents-config/{commands/prompts/saas-create-tasks.md → skills/saas-create-tasks/SKILL.md} +1 -0
  64. package/agents-config/{commands/prompts/saas-define-pricing.md → skills/saas-define-pricing/SKILL.md} +1 -0
  65. package/agents-config/{commands/prompts/saas-find-domain-name.md → skills/saas-find-domain-name/SKILL.md} +1 -0
  66. package/agents-config/{commands/prompts/saas-implement-landing-page.md → skills/saas-implement-landing-page/SKILL.md} +1 -0
  67. package/agents-config/{commands/prompts/setup-tmux.md → skills/setup-tmux/SKILL.md} +5 -0
  68. package/agents-config/{commands/prompts/tools.md → skills/tools/SKILL.md} +1 -0
  69. package/agents-config/skills/ultrathink/SKILL.md +1 -1
  70. package/agents-config/skills/use-style/SKILL.md +7 -4
  71. package/agents-config/skills/use-style/examples/luma.html +221 -0
  72. package/agents-config/skills/use-style/examples/testspirite.html +340 -0
  73. package/agents-config/skills/use-style/styles/ios-app.md +350 -0
  74. package/agents-config/skills/use-style/styles/luma.md +422 -0
  75. package/agents-config/skills/use-style/styles/new-york-times.md +1 -1
  76. package/agents-config/skills/use-style/styles/split-auth.md +2 -2
  77. package/agents-config/skills/use-style/styles/testspirite.md +397 -0
  78. package/dist/cli.js +17 -80
  79. package/package.json +1 -1
  80. package/agents-config/claude-config/scripts/.claude/commands/fix-on-my-computer.md +0 -87
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AIBlueprint CLI
2
2
 
3
- Supercharge Claude Code with security hooks, custom commands, a lightweight statusline, and workflow automation.
3
+ Supercharge Claude Code and Codex with security hooks, reusable skills, a lightweight statusline, and workflow automation.
4
4
 
5
5
  📚 **[Full Documentation](https://codelynx.dev/docs)** | 🎯 [Premium Features](https://mlv.sh/claude-cli)
6
6
 
@@ -15,7 +15,7 @@ npx aiblueprint-cli@latest agents setup
15
15
 
16
16
  - **🛡️ Security** - Command validation hooks blocking dangerous operations
17
17
  - **📊 Statusline** - Git status and session context display
18
- - **🤖 Commands** - 16 pre-configured workflow automation commands
18
+ - **🤖 Skills** - 30+ reusable workflow skills
19
19
  - **🎭 Agents** - 3 specialized AI agents for codebase exploration
20
20
  - **⚡ Scripts** - Built-in statusline utilities
21
21
 
@@ -67,10 +67,6 @@ npx aiblueprint-cli@latest agents setup
67
67
  # Add specific hooks
68
68
  npx aiblueprint-cli@latest agents add hook post-edit-typescript
69
69
 
70
- # Manage commands
71
- npx aiblueprint-cli@latest agents add commands
72
- npx aiblueprint-cli@latest agents add commands commit
73
-
74
70
  # Create symlinks between tools
75
71
  npx aiblueprint-cli@latest agents symlink
76
72
 
@@ -104,23 +100,26 @@ npx aiblueprint-cli@latest agents config unify sessions
104
100
  - Session context and duration
105
101
  - Colored visual indicators
106
102
 
107
- ### Commands (16 Available)
103
+ ### Workflow Skills
108
104
 
109
105
  **Development:**
110
106
  - `commit` - Fast conventional commits
111
- - `create-pull-request` - Auto-generated PRs
107
+ - `create-pr` - Auto-generated PRs
112
108
  - `fix-pr-comments` - Resolve PR comments
113
- - `run-tasks` - Execute GitHub issues
109
+ - `merge` - Context-aware branch merging
114
110
 
115
- **Analysis:**
116
- - `deep-code-analysis` - Comprehensive codebase investigation
117
- - `explain-architecture` - Pattern analysis with diagrams
118
- - `cleanup-context` - Memory optimization
111
+ **Project setup:**
112
+ - `nextjs-setup-project` - Scaffold the preferred Next.js stack
113
+ - `nextjs-add-prisma-db` - Add Prisma and PostgreSQL
114
+ - `nextjs-setup-better-auth` - Wire Better Auth with email OTP
115
+ - `create-vitejs-app` - Create a Vite app with Tailwind and shadcn/ui
119
116
 
120
- **Utilities:**
121
- - `rules-manager` - AGENTS.md and `.agents/rules/` management
122
- - `watch-ci` - Automated CI monitoring
123
- - `epct` - Explore-Plan-Code-Test methodology
117
+ **SaaS planning:**
118
+ - `saas-create-prd` - Build a lean Product Requirements Document
119
+ - `saas-create-architecture` - Design technical architecture
120
+ - `saas-create-tasks` - Generate implementation tasks
121
+ - `saas-define-pricing` - Create pricing strategy
122
+ - `saas-find-domain-name` - Generate and validate domains
124
123
 
125
124
  ### Agents (3 Specialized)
126
125
 
@@ -158,6 +157,11 @@ npx skills add Melvynx/aiblueprint --skill skill-manager
158
157
  | `merge` | Context-aware branch merging |
159
158
  | `prompt-creator` | Expert prompt engineering |
160
159
  | `skill-manager` | Manage skills and rules across Claude Code, Codex, and Cursor |
160
+ | `saas-create-prd` | Build a lean SaaS PRD |
161
+ | `saas-create-architecture` | Design SaaS technical architecture |
162
+ | `saas-create-tasks` | Generate implementation task files |
163
+ | `nextjs-setup-project` | Set up a production-ready Next.js project |
164
+ | `create-vitejs-app` | Create a Vite app with Tailwind and shadcn/ui |
161
165
  | `use-style` | Apply named UI style guides before implementation |
162
166
  | `rules-manager` | Create and maintain AGENTS.md and agent rule files |
163
167
  | `agents-managers` | Manage Claude Code agents and Task-tool orchestration |
@@ -0,0 +1,8 @@
1
+ # Ignore all data files
2
+ *.db
3
+ *.json
4
+
5
+ # But keep this .gitignore
6
+ !.gitignore
7
+
8
+ last_payload.txt
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "features": {
3
- "usageLimits": false,
4
- "spendTracking": false
3
+ "usageLimits": true,
4
+ "spendTracking": true
5
5
  },
6
6
  "oneLine": true,
7
7
  "showSonnetModel": false,
@@ -17,7 +17,7 @@
17
17
  "separator": "•",
18
18
  "session": {
19
19
  "infoSeparator": null,
20
- "cost": { "enabled": false, "format": "decimal1" },
20
+ "cost": { "enabled": true, "format": "decimal1" },
21
21
  "duration": { "enabled": true },
22
22
  "tokens": { "enabled": true, "showMax": false, "showDecimals": false },
23
23
  "percentage": {
@@ -40,9 +40,9 @@
40
40
  "overheadTokens": 0
41
41
  },
42
42
  "limits": {
43
- "enabled": false,
44
- "showTimeLeft": false,
45
- "showPacingDelta": false,
43
+ "enabled": true,
44
+ "showTimeLeft": true,
45
+ "showPacingDelta": true,
46
46
  "cost": { "enabled": false, "format": "decimal1" },
47
47
  "percentage": {
48
48
  "enabled": true,
@@ -57,9 +57,9 @@
57
57
  }
58
58
  },
59
59
  "weeklyUsage": {
60
- "enabled": false,
61
- "showTimeLeft": false,
62
- "showPacingDelta": false,
60
+ "enabled": "90%",
61
+ "showTimeLeft": true,
62
+ "showPacingDelta": true,
63
63
  "cost": { "enabled": false, "format": "decimal1" },
64
64
  "percentage": {
65
65
  "enabled": true,
@@ -74,7 +74,7 @@
74
74
  }
75
75
  },
76
76
  "dailySpend": {
77
- "cost": { "enabled": false, "format": "decimal1" }
77
+ "cost": { "enabled": true, "format": "decimal1" }
78
78
  },
79
79
  "thinking": {
80
80
  "showDisabledWarning": true
@@ -2,7 +2,11 @@
2
2
 
3
3
  import { readFileSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
- import { defaultConfig, type StatuslineConfig } from "../lib/config";
5
+ import {
6
+ defaultConfig,
7
+ loadConfig as loadStatuslineConfig,
8
+ type StatuslineConfig,
9
+ } from "../lib/config";
6
10
  import { colors } from "../lib/formatters";
7
11
  import {
8
12
  cycle,
@@ -161,6 +165,7 @@ const tabs: Tab[] = [
161
165
  id: "session",
162
166
  label: "SESSION",
163
167
  options: [
168
+ toggle("session.cost.enabled", "Cost display"),
164
169
  toggle("session.duration.enabled", "Duration"),
165
170
  toggle("session.tokens.enabled", "Token count"),
166
171
  toggle("session.tokens.showMax", " Show max", {
@@ -200,6 +205,116 @@ const tabs: Tab[] = [
200
205
  toggle("context.useUsableContextOnly", "45k buffer"),
201
206
  ],
202
207
  },
208
+ {
209
+ id: "limits",
210
+ label: "5-HOUR",
211
+ options: [
212
+ {
213
+ path: "limits.enabled",
214
+ label: "5-hour limit",
215
+ type: "cycle" as const,
216
+ choices: ["Enabled", "Disabled"],
217
+ getValue: (c) => (c.limits.enabled ? "Enabled" : "Disabled"),
218
+ cycle: (c) => {
219
+ c.limits.enabled = !c.limits.enabled;
220
+ },
221
+ },
222
+ toggle("limits.showTimeLeft", " Time remaining", {
223
+ hidden: (c) => !c.limits.enabled,
224
+ }),
225
+ toggle("limits.showPacingDelta", " Pacing delta", {
226
+ hidden: (c) => !c.limits.enabled,
227
+ }),
228
+ toggle("limits.cost.enabled", " Period cost", {
229
+ hidden: (c) => !c.limits.enabled,
230
+ }),
231
+ toggle("limits.percentage.showValue", " Show % value", {
232
+ hidden: (c) => !c.limits.enabled,
233
+ }),
234
+ progressStyleCycle("limits.percentage", (c) => !c.limits.enabled),
235
+ cycle(
236
+ "limits.percentage.progressBar.length",
237
+ " Length",
238
+ PROGRESS_BAR_LENGTHS,
239
+ {
240
+ hidden: (c) =>
241
+ !c.limits.enabled || !c.limits.percentage.progressBar.enabled,
242
+ },
243
+ ),
244
+ cycle(
245
+ "limits.percentage.progressBar.color",
246
+ " Color",
247
+ PROGRESS_BAR_COLORS,
248
+ {
249
+ hidden: (c) =>
250
+ !c.limits.enabled || !c.limits.percentage.progressBar.enabled,
251
+ },
252
+ ),
253
+ ],
254
+ },
255
+ {
256
+ id: "weekly",
257
+ label: "WEEKLY",
258
+ options: [
259
+ {
260
+ path: "weeklyUsage.enabled",
261
+ label: "Weekly limit",
262
+ type: "cycle" as const,
263
+ choices: ["Always", "At 90%", "Never"],
264
+ getValue: (c) => {
265
+ if (c.weeklyUsage.enabled === true) return "Always";
266
+ if (c.weeklyUsage.enabled === "90%") return "At 90%";
267
+ return "Never";
268
+ },
269
+ cycle: (c, dir) => {
270
+ const modes = [true, "90%", false] as const;
271
+ const idx =
272
+ c.weeklyUsage.enabled === true
273
+ ? 0
274
+ : c.weeklyUsage.enabled === "90%"
275
+ ? 1
276
+ : 2;
277
+ c.weeklyUsage.enabled = modes[(idx + dir + 3) % 3];
278
+ },
279
+ },
280
+ toggle("weeklyUsage.showTimeLeft", " Time remaining", {
281
+ hidden: (c) => c.weeklyUsage.enabled === false,
282
+ }),
283
+ toggle("weeklyUsage.showPacingDelta", " Pacing delta", {
284
+ hidden: (c) => c.weeklyUsage.enabled === false,
285
+ }),
286
+ toggle("weeklyUsage.cost.enabled", " Cost", {
287
+ hidden: (c) => c.weeklyUsage.enabled === false,
288
+ }),
289
+ toggle("weeklyUsage.percentage.showValue", " Show % value", {
290
+ hidden: (c) => c.weeklyUsage.enabled === false,
291
+ }),
292
+ progressStyleCycle(
293
+ "weeklyUsage.percentage",
294
+ (c) => c.weeklyUsage.enabled === false,
295
+ ),
296
+ cycle(
297
+ "weeklyUsage.percentage.progressBar.length",
298
+ " Length",
299
+ PROGRESS_BAR_LENGTHS,
300
+ {
301
+ hidden: (c) =>
302
+ c.weeklyUsage.enabled === false ||
303
+ !c.weeklyUsage.percentage.progressBar.enabled,
304
+ },
305
+ ),
306
+ cycle(
307
+ "weeklyUsage.percentage.progressBar.color",
308
+ " Color",
309
+ PROGRESS_BAR_COLORS,
310
+ {
311
+ hidden: (c) =>
312
+ c.weeklyUsage.enabled === false ||
313
+ !c.weeklyUsage.percentage.progressBar.enabled,
314
+ },
315
+ ),
316
+ ],
317
+ },
203
318
  {
204
319
  id: "git",
205
320
  label: "GIT",
@@ -239,6 +354,7 @@ const tabs: Tab[] = [
239
354
  cycle("pathDisplayMode", "Path display", PATH_DISPLAY_MODES),
240
355
  toggle("showSonnetModel", "Show Sonnet model"),
241
356
  toggle("oneLine", "Single line mode"),
357
+ toggle("dailySpend.cost.enabled", "Daily spend"),
242
358
  ],
243
359
  },
244
360
  ];
@@ -248,11 +364,7 @@ const tabs: Tab[] = [
248
364
  // ─────────────────────────────────────────────────────────────
249
365
 
250
366
  function loadConfig(): StatuslineConfig {
251
- try {
252
- return JSON.parse(readFileSync(CONFIG_FILE_PATH, "utf-8"));
253
- } catch {
254
- return JSON.parse(JSON.stringify(defaultConfig));
255
- }
367
+ return loadStatuslineConfig();
256
368
  }
257
369
 
258
370
  function saveConfig(config: StatuslineConfig): void {
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { readFile } from "node:fs/promises";
3
+ import { readFile, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
- import { defaultConfig, type StatuslineConfig } from "./lib/config";
5
+ import {
6
+ loadConfig as loadStatuslineConfig,
7
+ type StatuslineConfig,
8
+ } from "./lib/config";
6
9
  import { getContextData } from "./lib/context";
7
10
  import {
8
11
  colors,
@@ -19,6 +22,45 @@ import {
19
22
  } from "./lib/render-pure";
20
23
  import type { HookInput } from "./lib/types";
21
24
 
25
+ // Optional feature imports - just delete the folder to disable!
26
+ let getUsageLimits: any = null;
27
+ let normalizeResetsAt: any = null;
28
+ let getPeriodCost: any = null;
29
+ let getTodayCostV2: any = null;
30
+ let saveSessionV2: any = null;
31
+
32
+ try {
33
+ const limitsModule = await import("./lib/features/limits");
34
+ getUsageLimits = limitsModule.getUsageLimits;
35
+ } catch {
36
+ // Limits feature not available - that's OK!
37
+ }
38
+
39
+ try {
40
+ const utilsModule = await import("./lib/utils");
41
+ normalizeResetsAt = utilsModule.normalizeResetsAt;
42
+ } catch {
43
+ // Fallback normalizeResetsAt
44
+ normalizeResetsAt = (resetsAt: string) => resetsAt;
45
+ }
46
+
47
+ let getTodayRealCost: (() => number) | null = null;
48
+ try {
49
+ const spendModule = await import("./lib/features/spend");
50
+ getPeriodCost = spendModule.getPeriodCost;
51
+ getTodayCostV2 = spendModule.getTodayCostV2;
52
+ saveSessionV2 = spendModule.saveSessionV2;
53
+ } catch {
54
+ // Spend tracking feature not available - that's OK!
55
+ }
56
+
57
+ try {
58
+ const loggerModule = await import("./lib/features/spend/payload-logger");
59
+ getTodayRealCost = loggerModule.getTodayRealCost;
60
+ } catch {
61
+ // Payload logger not available
62
+ }
63
+
22
64
  // Re-export from render-pure for backwards compatibility
23
65
  export {
24
66
  renderStatusline,
@@ -26,7 +68,12 @@ export {
26
68
  type UsageLimit,
27
69
  } from "./lib/render-pure";
28
70
 
29
- const CONFIG_FILE_PATH = join(import.meta.dir, "..", "statusline.config.json");
71
+ const LAST_PAYLOAD_PATH = join(
72
+ import.meta.dir,
73
+ "..",
74
+ "data",
75
+ "last_payload.txt",
76
+ );
30
77
  const CLAUDE_SETTINGS_PATH = join(
31
78
  process.env.HOME || "",
32
79
  ".claude",
@@ -48,21 +95,30 @@ async function loadClaudeSettings(): Promise<ClaudeSettings> {
48
95
  }
49
96
 
50
97
  async function loadConfig(): Promise<StatuslineConfig> {
51
- try {
52
- const content = await readFile(CONFIG_FILE_PATH, "utf-8");
53
- return JSON.parse(content);
54
- } catch {
55
- return defaultConfig;
56
- }
98
+ return loadStatuslineConfig();
57
99
  }
58
100
 
59
101
  async function main() {
60
102
  try {
61
103
  const input: HookInput = await Bun.stdin.json();
62
104
 
105
+ // Save last payload for debugging
106
+ await writeFile(LAST_PAYLOAD_PATH, JSON.stringify(input, null, 2));
107
+
63
108
  const config = await loadConfig();
64
109
  const claudeSettings = await loadClaudeSettings();
65
110
 
111
+ // Get usage limits (if feature exists)
112
+ const usageLimits = getUsageLimits
113
+ ? await getUsageLimits()
114
+ : { five_hour: null, seven_day: null };
115
+ const currentResetsAt = usageLimits.five_hour?.resets_at ?? undefined;
116
+
117
+ // Save session with current period context (if feature exists)
118
+ if (saveSessionV2) {
119
+ await saveSessionV2(input, currentResetsAt);
120
+ }
121
+
66
122
  const git = await getGitStatus();
67
123
 
68
124
  let contextTokens: number | null;
@@ -102,6 +158,24 @@ async function main() {
102
158
  contextPercentage = contextData.percentage;
103
159
  }
104
160
 
161
+ // Get period cost from SQLite (if feature exists)
162
+ let periodCost: number | undefined;
163
+ let todayCost: number | undefined;
164
+
165
+ if (getPeriodCost && normalizeResetsAt) {
166
+ const normalizedPeriodId = currentResetsAt
167
+ ? normalizeResetsAt(currentResetsAt)
168
+ : null;
169
+ periodCost = normalizedPeriodId ? getPeriodCost(normalizedPeriodId) : 0;
170
+ }
171
+
172
+ // Get today's cost from payloads (more accurate than DB)
173
+ if (getTodayRealCost) {
174
+ todayCost = getTodayRealCost();
175
+ } else if (getTodayCostV2) {
176
+ todayCost = getTodayCostV2();
177
+ }
178
+
105
179
  const data: StatuslineData = {
106
180
  branch: formatBranch(git, config.git),
107
181
  dirPath: formatPath(input.workspace.current_dir, config.pathDisplayMode),
@@ -121,6 +195,23 @@ async function main() {
121
195
  sessionDuration: formatDuration(input.cost.total_duration_ms),
122
196
  contextTokens,
123
197
  contextPercentage,
198
+ ...(getUsageLimits && {
199
+ usageLimits: {
200
+ five_hour: usageLimits.five_hour
201
+ ? {
202
+ utilization: usageLimits.five_hour.utilization,
203
+ resets_at: usageLimits.five_hour.resets_at,
204
+ }
205
+ : null,
206
+ seven_day: usageLimits.seven_day
207
+ ? {
208
+ utilization: usageLimits.seven_day.utilization,
209
+ resets_at: usageLimits.seven_day.resets_at,
210
+ }
211
+ : null,
212
+ },
213
+ }),
214
+ ...((getPeriodCost || getTodayCostV2) && { periodCost, todayCost }),
124
215
  thinkingEnabled: claudeSettings.alwaysThinkingEnabled ?? true,
125
216
  };
126
217
 
@@ -10,11 +10,37 @@ export const defaultConfig: StatuslineConfig = JSON.parse(
10
10
  readFileSync(DEFAULTS_PATH, "utf-8"),
11
11
  );
12
12
 
13
+ function cloneDefaultConfig(): StatuslineConfig {
14
+ return JSON.parse(JSON.stringify(defaultConfig));
15
+ }
16
+
17
+ function isRecord(value: unknown): value is Record<string, unknown> {
18
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
19
+ }
20
+
21
+ function mergeConfigDefaults<T>(defaults: T, overrides: unknown): T {
22
+ if (!isRecord(defaults) || !isRecord(overrides)) {
23
+ return overrides === undefined ? defaults : (overrides as T);
24
+ }
25
+
26
+ const merged: Record<string, unknown> = { ...defaults };
27
+ for (const [key, value] of Object.entries(overrides)) {
28
+ merged[key] = mergeConfigDefaults(
29
+ (defaults as Record<string, unknown>)[key],
30
+ value,
31
+ );
32
+ }
33
+ return merged as T;
34
+ }
35
+
13
36
  export function loadConfig(): StatuslineConfig {
14
37
  try {
15
- return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
38
+ return mergeConfigDefaults(
39
+ defaultConfig,
40
+ JSON.parse(readFileSync(CONFIG_PATH, "utf-8")),
41
+ );
16
42
  } catch {
17
- return JSON.parse(JSON.stringify(defaultConfig));
43
+ return cloneDefaultConfig();
18
44
  }
19
45
  }
20
46
 
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { colors, formatProgressBar } from "../../../formatters";
4
+ import { getUsageLimits } from "../index";
5
+
6
+ const WEEKLY_HOURS = 168; // 7 days * 24 hours
7
+
8
+ function calculateTimeProgress(resetsAt: string | null): {
9
+ hoursRemaining: number;
10
+ hoursElapsed: number;
11
+ timeElapsedPercent: number;
12
+ } {
13
+ if (!resetsAt) {
14
+ return {
15
+ hoursRemaining: 0,
16
+ hoursElapsed: WEEKLY_HOURS,
17
+ timeElapsedPercent: 100,
18
+ };
19
+ }
20
+
21
+ const resetDate = new Date(resetsAt);
22
+ const now = new Date();
23
+ const diffMs = resetDate.getTime() - now.getTime();
24
+ const hoursRemaining = Math.max(0, diffMs / 3600000);
25
+ const hoursElapsed = WEEKLY_HOURS - hoursRemaining;
26
+ const timeElapsedPercent = (hoursElapsed / WEEKLY_HOURS) * 100;
27
+
28
+ return { hoursRemaining, hoursElapsed, timeElapsedPercent };
29
+ }
30
+
31
+ function formatDelta(delta: number): string {
32
+ const sign = delta >= 0 ? "+" : "";
33
+ const value = `${sign}${delta.toFixed(1)}%`;
34
+
35
+ if (delta > 5) return colors.green(value);
36
+ if (delta > 0) return colors.lightGray(value);
37
+ if (delta > -5) return colors.yellow(value);
38
+ if (delta > -15) return colors.orange(value);
39
+ return colors.red(value);
40
+ }
41
+
42
+ async function main() {
43
+ const limits = await getUsageLimits();
44
+
45
+ console.log("");
46
+ console.log(colors.bold("📊 Weekly Usage Analysis"));
47
+ console.log(colors.gray("─".repeat(40)));
48
+
49
+ if (!limits.seven_day) {
50
+ console.log(colors.red("No weekly limit data available"));
51
+ return;
52
+ }
53
+
54
+ const { utilization, resets_at } = limits.seven_day;
55
+ const { timeElapsedPercent } = calculateTimeProgress(resets_at);
56
+
57
+ const expectedUsage = timeElapsedPercent;
58
+ const delta = utilization - expectedUsage;
59
+
60
+ console.log("");
61
+ console.log(colors.gray("Weekly Limits Overview"));
62
+ console.log(colors.gray("─".repeat(50)));
63
+ console.log("");
64
+
65
+ const usageBar = formatProgressBar({
66
+ percentage: utilization,
67
+ length: 20,
68
+ style: "filled",
69
+ colorMode: "blue",
70
+ background: "none",
71
+ });
72
+ console.log(
73
+ `${colors.blue("Usage")} ${usageBar} ${colors.lightGray(utilization.toFixed(1))}${colors.gray("%")}`,
74
+ );
75
+
76
+ const timeBar = formatProgressBar({
77
+ percentage: timeElapsedPercent,
78
+ length: 20,
79
+ style: "filled",
80
+ colorMode: "purple",
81
+ background: "none",
82
+ });
83
+ console.log(
84
+ `${colors.purple("Time ")} ${timeBar} ${colors.lightGray(timeElapsedPercent.toFixed(1))}${colors.gray("%")} ${colors.gray("·")} ${formatDelta(delta)}`,
85
+ );
86
+
87
+ console.log("");
88
+ console.log(colors.gray("─".repeat(50)));
89
+ console.log("");
90
+
91
+ if (limits.five_hour) {
92
+ console.log(colors.gray("─".repeat(40)));
93
+ console.log("");
94
+ console.log(colors.gray("Session Limit (5h):"));
95
+ console.log(
96
+ ` ${formatProgressBar({
97
+ percentage: limits.five_hour.utilization,
98
+ length: 15,
99
+ style: "filled",
100
+ colorMode: "progressive",
101
+ background: "none",
102
+ })} ${colors.lightGray(limits.five_hour.utilization.toString())}${colors.gray("%")}`,
103
+ );
104
+ console.log("");
105
+ }
106
+ }
107
+
108
+ main();