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;
}