Actiwitee
Self-hosted · Open source

Everywhere you code,
one contribution graph

Actiwitee merges GitHub, competitive programming, and local AI-coding sessions into a single heatmap and read-only API, built for portfolios that reflect how you actually work.

config.yaml

sources:

github: [personal, work]

leetcode: [saish_korgaonkar]

codeforces: [saish_k]

agent:

signals: [cursor, hermes-cli]

$ actiwitee collect

github:personal 459 contrib

leetcode 15 contrib

codeforces 6 contrib

codechef 141 contrib

→ store.json updated

$ curl /contributions

{

"total": 622,

"contributions": [

{ "date": "2026-07-25",

"level": 2, "localMinutes": 76 }

]

}

$ actiwitee agent --once

[agent] active: ai-cli, terminal

→ 76 min local today

→ heartbeat stored

Hermes cron · every 5m

Built for developers who code everywhere

Actiwitee is the self-hostable backend behind unified coding activity, from open source commits to contest submissions to late-night AI pair sessions.

Multi-source aggregation

Pull from GitHub (multi-account), LeetCode, Codeforces, CodeChef, and more. Weighted merge with percentile-based heatmap levels.

Local session tracking

A lightweight agent samples AI CLI, IDE, and terminal activity. Local minutes show up on the graph without inflating commit counts.

Portfolio-ready API

Serve a read-only JSON API from Cloudflare Workers + R2. Drop the heatmap into any site with one env var.

Config-driven

One YAML file for sources, weights, CORS, and agent signals. No database, just JSON on disk.

Full history

GitHub history back to account creation. Scrollable graph with rich per-day tooltips: contributions, local time, and source breakdown.

Automate with cron

Collect on a schedule, publish to R2, and run the agent every few minutes. Set it once and forget it.

Plug in your sources

Add handles and tokens in config.yaml. Run collect. Actiwitee normalizes dates, deduplicates, and merges into one timeline.

GitHub

Multi-account, private repos via PAT

LeetCode

Submission calendar sync

Codeforces

Contest & practice activity

CodeChef

Practice streaks

Local agent

Cursor, VS Code, Claude Code, Hermes, terminal

Demo

Sample data for development

Read-only JSON API

Three routes, no auth required. Host on Cloudflare Workers + R2 and call from any frontend with one env var.

Live example → codepulse.saish.xyz
GET/health

Liveness check and per-source freshness timestamps.

{ "ok": true, "sources": { "github:personal": "2026-07-25T..." } }
GET/contributions?year=2026

Lean heatmap rows, GitHub-compatible {date, count, level} shape.

{ "total": 622, "contributions": [{ "date": "2026-07-25", "count": 3, "level": 2 }] }
GET/activity

Full payload with scores, local minutes, and per-source breakdown per day.

{ "contributions": [{ "date": "...", "count": 3, "level": 2, "localMinutes": 76, "breakdown": { "github": 2, "local": 76 } }] }

Self-host in six steps

From zero to a live portfolio heatmap. Copy the commands, edit config.yaml for your handles, deploy the Worker, and point your site at the API.

Step 01

Install & try the demo

Clone the monorepo, install dependencies, and run with synthetic data. No tokens or network required.

terminal

git clone https://github.com/SaishKorgaonkar/actiwitee.git
cd actiwitee
npm install
npm run build
cd cli
node dist/cli.js collect --demo
node dist/cli.js serve   # http://localhost:4787

verify

curl localhost:4787/health
curl localhost:4787/activity | head
Step 02

Configure your sources

Scaffold config files, then add handles and tokens. Secrets stay in .env, never in config.yaml.

GitHub PATs need read:user scope for private contribution counts. Without a token, only public activity is fetched.

terminal

node dist/cli.js init
# edits config.yaml + .env

config.yaml (excerpt)

sources:
  github:
    - id: personal
      username: your-handle
      tokenEnv: ACTIWITEE_GITHUB_PERSONAL_TOKEN
      includePrivate: true
  leetcode: [{ username: your-lc-handle }]
  codeforces: [{ username: your-cf-handle }]

weights: { github: 1, leetcode: 3, codeforces: 3, local: 1 }
levels: { mode: percentile }

.env

ACTIWITEE_GITHUB_PERSONAL_TOKEN=ghp_...
ACTIWITEE_GITHUB_WORK_TOKEN=ghp_...
Step 03

Track local coding sessions

The agent samples process, app-focus, and session-log signals on an interval. Local minutes appear on the heatmap separately from commit counts.

macOS only for app-focus signals. Add a signal for every tool you use: Cursor, VS Code, Claude Code, Hermes, terminals, local models, etc.

config.yaml (agent)

agent:
  interval: 180
  signals:
    - { id: cursor, type: app-focus, match: "Cursor", category: editor }
    - { id: claude-code, type: session-log, path: "~/.claude/projects", category: ai-cli }
    - { id: ollama, type: process, match: "ollama|llama-server", category: local-model }

terminal

node dist/cli.js agent          # continuous
node dist/cli.js agent --once   # single sample
Step 04

Deploy the read-only API

Publish merged activity.json to Cloudflare R2 and serve it from a Worker. Your portfolio reads the JSON, no backend on your site.

Routes: GET /health, GET /contributions, GET /activity. CORS is configured in config.yaml for your portfolio origin.

publish locally

node dist/cli.js collect
node dist/cli.js show > data/activity.json
# upload to R2 (see cli/deploy/worker/README.md)

deploy worker

cd cli/deploy/worker
npm install
cp wrangler.toml.example wrangler.toml
npx wrangler login
npm run deploy
# attach custom domain, e.g. activity.yourdomain.com
Step 05

Wire your portfolio

Point any frontend at the API. The contributions shape matches GitHub-style heatmap components.

Use /contributions for lean {date,count,level} rows. Use /activity for per-source breakdowns and local minutes in tooltips.

.env (portfolio)

NEXT_PUBLIC_ACTIWITEE_API_URL=https://activity.yourdomain.com

fetch

const res = await fetch(`${API_URL}/activity`)
const { contributions } = await res.json()
// [{ date, count, level, localMinutes?, breakdown? }]
Step 06

Automate collection & publish

Install cron jobs with one script. Agent runs every 5 minutes; collect + R2 upload runs hourly. Your Mac must be awake for jobs to fire.

Requires npm run build first. Uses wrangler for R2 upload after Worker deploy (step 4). launchd or Hermes work too if you prefer those over cron.

install cron (macOS / Linux)

cd cli
# optional: set R2 target before install
export ACTIWITEE_R2_BUCKET=your-bucket
export ACTIWITEE_R2_KEY=actiwitee/activity.json

bash scripts/install-cron.sh
crontab -l | grep actiwitee

what gets installed

# every 5 min — local heartbeats
scripts/actiwitee-agent.sh

# every hour — collect, merge, upload to R2
scripts/actiwitee-publish.sh

# logs
/tmp/actiwitee-agent.log
/tmp/actiwitee-publish.log

# remove
bash scripts/install-cron.sh --uninstall

Ready to unify your coding activity?

Open source and self-hostable. Star the repo, follow the guide above, and wire the heatmap into your portfolio in an afternoon.