feat: add Claude usage tracking via CLI

Adds a Claude usage tracking feature that displays session, weekly, and Sonnet usage stats. Uses the Claude CLI's /usage command to fetch data (no API key required).

Features:
- Usage popover in board header showing session, weekly, and Sonnet limits
- Progress bars with color-coded status (green/orange/red)
- Auto-refresh with configurable interval
- Caching of usage data with stale indicator
- Settings section for refresh interval configuration

Server:
- ClaudeUsageService: Executes Claude CLI via PTY (expect) to fetch usage
- New /api/claude/usage endpoint

UI:
- ClaudeUsagePopover component with usage cards
- ClaudeUsageSection in settings for configuration
- Integration with app store for persistence
This commit is contained in:
Mohamad Yahia
2025-12-21 08:03:43 +04:00
parent 5aedb4fadf
commit 5bd2b705dc
11 changed files with 952 additions and 5 deletions

View File

@@ -0,0 +1,44 @@
import { Router, Request, Response } from "express";
import { ClaudeUsageService } from "../../services/claude-usage-service.js";
export function createClaudeRoutes(): Router {
const router = Router();
const service = new ClaudeUsageService();
// Get current usage (fetches from Claude CLI)
router.get("/usage", async (req: Request, res: Response) => {
try {
// Check if Claude CLI is available first
const isAvailable = await service.isAvailable();
if (!isAvailable) {
res.status(503).json({
error: "Claude CLI not found",
message: "Please install Claude Code CLI and run 'claude login' to authenticate"
});
return;
}
const usage = await service.fetchUsageData();
res.json(usage);
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
if (message.includes("Authentication required") || message.includes("token_expired")) {
res.status(401).json({
error: "Authentication required",
message: "Please run 'claude login' to authenticate"
});
} else if (message.includes("timed out")) {
res.status(504).json({
error: "Command timed out",
message: "The Claude CLI took too long to respond"
});
} else {
console.error("Error fetching usage:", error);
res.status(500).json({ error: message });
}
}
});
return router;
}

View File

@@ -0,0 +1,35 @@
/**
* Claude Usage types for CLI-based usage tracking
*/
export type ClaudeUsage = {
sessionTokensUsed: number;
sessionLimit: number;
sessionPercentage: number;
sessionResetTime: string; // ISO date string
sessionResetText: string; // Raw text like "Resets 10:59am (Asia/Dubai)"
weeklyTokensUsed: number;
weeklyLimit: number;
weeklyPercentage: number;
weeklyResetTime: string; // ISO date string
weeklyResetText: string; // Raw text like "Resets Dec 22 at 7:59pm (Asia/Dubai)"
opusWeeklyTokensUsed: number;
opusWeeklyPercentage: number;
opusResetText: string; // Raw text like "Resets Dec 27 at 9:59am (Asia/Dubai)"
costUsed: number | null;
costLimit: number | null;
costCurrency: string | null;
lastUpdated: string; // ISO date string
userTimezone: string;
};
export type ClaudeStatus = {
indicator: {
color: "green" | "yellow" | "orange" | "red" | "gray";
};
description: string;
};