A self-hosted analytics dashboard for tracking your Claude Code usage statistics across multiple machines. Deploy for free on GitHub Pages with zero configuration.
- Multi-Device Support: Aggregate usage from all your machines into one dashboard
- Comprehensive Stats: Lifetime, daily, weekly, and monthly usage metrics
- Streak Tracking: Monitor your current and longest usage streaks
- Activity Heatmap: GitHub-style contribution graph of your usage
- Activity Timeline: Paginated daily usage breakdown
- Dynamic ASCII Art: Your name rendered as ASCII art in the header
- Dark/Light Mode: Automatic theme switching with preference persistence
- Zero Config: No databases, no API keys, no external services
- Git-Based Storage: All stats stored as JSON files in your repo
- Deploy Anywhere: GitHub Pages, Cloudflare Pages, Netlify, or any static host
# Fork this repo on GitHub, then:
git clone https://github.com/YOUR_USERNAME/ccstats.git
cd ccstats
npm installThe fork includes sample data from the original repo. Remove it so your dashboard starts fresh:
rm -f data/machines/*.json data/days.json data/stats.jsonEdit config.js with your info:
window.CONFIG = {
userName: 'Your Name', // Displayed as ASCII art in header
userBio: 'Your bio here',
socials: {
github: 'yourusername',
twitter: 'yourhandle',
// ...
}
};npm run setupThis generates a unique machine ID and saves it to .env (gitignored). Each machine that contributes data needs to run this once.
npm run stats # Collect usage data from this machine
npm run push # Commit and push to GitHubThat's it! Your dashboard will be live at https://YOUR_USERNAME.github.io/ccstats in ~1 minute.
- Go to your repo on GitHub
- Click Settings β Pages
- Under Build and deployment:
- Source: GitHub Actions
- Done! GitHub will auto-deploy on every push
Want stats.yourdomain.com instead of username.github.io/repo?
-
Add a
CNAMEfile to the root of your repo:echo "stats.yourdomain.com" > CNAME
-
Add DNS records at your domain provider:
A @ 185.199.108.153 A @ 185.199.109.153 A @ 185.199.110.153 A @ 185.199.111.153Or for subdomain:
CNAME stats YOUR_USERNAME.github.io -
Push and wait for DNS propagation (~10 min to 24 hrs)
GitHub provides free SSL certificates automatically!
npm run stats # Pull latest, collect this machine's data, aggregate all machines
npm run push # Commit and push to GitHubSet up a cron job on each machine:
# Add to your crontab (crontab -e)
0 23 * * * cd /path/to/ccstats && npm run stats && npm run pushThis runs every day at 11 PM, updates your stats, and pushes to GitHub. Each machine can run on its own schedule β there's no coordination needed.
.
βββ index.html # Main dashboard page
βββ config.js # YOUR CONFIGURATION (edit this!)
βββ setup.js # Machine registration (generates .env)
βββ stats.js # Data collection, aggregation, and stats
βββ .env.example # Template for machine identity
βββ .env # YOUR machine identity (git-ignored, created by setup)
βββ css/
β βββ style.css # Dashboard styles
βββ js/
β βββ app.js # Dashboard logic (ASCII art, pagination, heatmap)
βββ data/
β βββ machines/ # Per-machine snapshots (one file per device)
β β βββ {uuid}.json # This machine's complete daily history
β βββ stats.json # Aggregated statistics (computed)
β βββ days.json # Aggregated daily data (computed)
βββ images/
β βββ example-site.png # Screenshot for README
βββ .github/
β βββ workflows/
β βββ pages.yml # GitHub Pages deployment
βββ package.json # npm scripts
- Setup:
npm run setupregisters this machine with a unique ID stored in.env - Pull:
npm run statsstarts by pulling the latest data from git - Collect: Runs
ccusageto get this machine's complete Claude Code usage history - Snapshot: Writes the full history to
data/machines/{machine-id}.json(idempotent overwrite) - Aggregate: Reads all machine files and sums usage per day across devices
- Compute: Calculates statistics from the aggregated daily data
- Push:
npm run pushcommits and pushes to GitHub β GitHub Actions deploys to Pages - Display: The dashboard loads the aggregated data (no backend required!)
Edit config.js to customize your dashboard:
window.CONFIG = {
// Profile
userName: 'Your Name', // Rendered as ASCII art in header
userEmail: 'your@email.com',
userBio: 'Developer & AI enthusiast',
userLocation: 'San Francisco, CA',
userTimezone: 'America/Los_Angeles',
// Social links (leave blank to hide)
socials: {
github: 'yourusername',
twitter: 'yourhandle',
linkedin: 'https://linkedin.com/in/yourprofile',
website: 'https://yoursite.com'
},
// Site metadata
siteTitle: 'Claude Code Usage Stats',
siteDescription: 'My personal Claude Code analytics'
};Changes take effect on next deployment (just git push).
Edit css/style.css:
:root[data-theme="dark"] {
--bg-primary: #0a0a0a;
--accent: #00ff88; /* Change this! */
/* ... more variables */
}Edit js/app.js to change calculations or add new metrics.
This dashboard works on ANY static host. Here are quick guides:
- Connect your GitHub repo
- Build command: (leave empty)
- Build output directory:
.(root) - Deploy!
- Connect your GitHub repo
- Build command: (leave empty)
- Publish directory:
.(root) - Deploy!
npm install -g vercel
vercel
# Follow prompts, set output directory to "."All platforms support custom domains with free SSL.
This dashboard supports aggregating usage from multiple machines into a single view. Each machine contributes its own data without conflicts.
On each additional machine that should contribute data:
git clone https://github.com/YOUR_USERNAME/ccstats.git
cd ccstats
npm install
npm run setup # Generates a unique machine ID
npm run stats # Collect and aggregate
npm run push # Push to GitHubYou don't need to clear demo data or edit config.js again β those changes are already in the repo from initial setup.
Each machine gets a UUID stored in .env (never committed). When you run npm run stats:
- The script pulls the latest
data/machines/files from git - Runs
ccusageto get this machine's complete usage history - Overwrites
data/machines/{your-uuid}.jsonwith the full snapshot - Reads all machine files and sums usage per day
- Writes the aggregated
data/days.jsonanddata/stats.json
Because each machine overwrites its own snapshot file, you can run npm run stats as many times per day as you want β no data is lost or double-counted.
Each machine only writes to its own file under data/machines/. Two machines never modify the same file, so git merges are always clean.
If a push fails because another machine pushed first, just re-run:
npm run stats && npm run pushThe stats command pulls the other machine's data before aggregating.
Running npm run setup on an already-registered machine generates a new UUID, which would create a second machine file and cause double-counting. The setup script guards against this β it will refuse to overwrite an existing .env.
If you genuinely need to re-register a machine (e.g., after deleting .env), also remove the old machine file from data/machines/.
Your usage stats are stored in your public GitHub repo by default. If you want to keep your data private:
- Make your repo private (Settings β Danger Zone β Change visibility)
- GitHub Pages still works with private repos (for Pro accounts)
- Or use Cloudflare Pages/Netlify (both support private repos for free)
# Serve locally (from repo root)
npx http-server . -p 3000
# Or use any static server
python3 -m http.server 3000Visit http://localhost:3000
npm run setup # Register this machine (generates .env with UUID, run once)
npm run stats # Pull latest, collect usage, aggregate all machines, save to data/
npm run push # Commit and push changes to GitHubCombine the daily workflow:
npm run stats && npm run push- Check that
data/stats.jsonanddata/days.jsonexist - Run
npm run statsto generate them - Commit and push the files
- Wait ~1 minute for GitHub Pages to deploy
- Make sure you committed the updated JSON files
- Check GitHub Actions tab for deployment status
- Hard refresh your browser:
Cmd+Shift+R(Mac) orCtrl+Shift+R(Windows)
- Verify
CNAMEfile is in the root directory and committed - Check DNS records are correct
- Wait for DNS propagation (up to 24 hours, usually < 1 hour)
- GitHub Pages β Settings β check for errors
Run npm run setup to register this machine. Every machine needs its own .env.
- Make sure
ccusageis installed:npm install - Check that you have Claude Code usage data:
npx ccusage --json - Ensure Node.js >= 18
This is normal. Re-run both commands:
npm run stats && npm run pushThe stats command pulls the other machine's data first, then re-aggregates.
This happens if npm run setup was run twice on the same machine, creating two machine files. Fix it by:
- Check
data/machines/for duplicate files from the same device - Delete the old/orphaned one
- Re-run
npm run stats && npm run push
Contributions welcome! See CONTRIBUTING.md for guidelines.
- Open issues for bugs or feature requests
- Submit PRs for improvements
- Share your customized dashboards!
Please note this project follows the Contributor Covenant Code of Conduct.
To report a vulnerability, see SECURITY.md.
MIT License - see LICENSE file for details
- Built for Claude Code users
- Uses ccusage for data collection
- Deployed on GitHub Pages (free forever!)
If you found this useful, give it a star on GitHub!
Made with Claude Code
Need help? Open an issue
