gitrakz

module
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: MIT

README

gitrakz

CI coverage version license Docker Pulls

Self-hosted GitHub activity tracker: it syncs your gh activity into a local SQLite database, renders a filterable timeline and derived work sessions, and runs programmatic templates — deterministic transform pipelines composed onto typed display blocks — that export to CSV, PDF, or JSON. A single Go binary with the Svelte UI embedded in it; no external services required.

Timeline

A template is not a saved prompt — it is a saved composition: a form, a transform pipeline over the timeline, and a display layout. The pipeline is deterministic; the LLM (optional) only composes a template from the building blocks or narrates a single prose block. Everything you see is one of a fixed set of renderers.

A template run

Contents

Features

  • Timeline — every commit / PR / review / issue / release for a GitHub user, grouped by day, with owner/repo, title, +additions/-deletions, time and link. Filter by owner, repo, type and date range; paginated with hasMore (never a total count).
  • Sessions — derived, not stored: events are grouped per owner and split into work sessions whenever the idle gap exceeds GITRAKZ_SESSION_GAP, with a configurable lead-in so pre-first-commit work is counted. The grounding for timesheet-style reports.
  • Templates — built-in and custom. A template = { form, transform, layout, exports }. Run one over a filtered range and get a typed block document you can export. Built-ins ship ready to run; custom ones are created, edited (built-ins clone-on-edit) or LLM-composed from a plain-language description.
  • Deterministic transforms — a fixed library of primitives (sessionize, exclude-off-time, split-by-active-days, group-by, aggregate, rate, passthrough) plus one LLM-backed step (describe-work) whose output is cached in the DB by prompt/config version so a re-run costs nothing.
  • Typed display blocksheading, text, table, metric, keyvalue, list, code, chart. One renderer per block, in the SPA and in every exporter.
  • Exports — CSV, JSON and PDF of any document or template run.
  • Embedded Svelte SPA — the UI is compiled into the binary. Shareable deep-link runs: /?tpl=<id>&run=1.
  • Single static binary or a small Docker image — pure-Go SQLite (no cgo), migrations embedded, sync on a background ticker.
  • LLM-optionaldescribe-work, text prose blocks and "generate a template with AI" go through elelem, selecting an openai (or OpenAI-compatible) or anthropic provider by env. Leave the provider config empty and everything deterministic still works.

How it works

gh CLI ──sync──▶ SQLite (events) ──▶ timeline / sessions
                                 └──▶ template = form + transform pipeline + layout
                                              └──▶ typed block document ──▶ SPA / CSV / PDF / JSON

gitrakz shells out to the GitHub gh CLI to discover a user's repos and pull their activity incrementally — per-repo and fail-soft, so one rate-limited repo never aborts the rest — persisting events into SQLite. A run resolves a template, queries the filtered timeline, runs the transform pipeline deterministically, maps the result onto the display layout, and returns the block document.

Quickstart

Per-user (no root) — installs into your home, just for you:

curl -fsSL https://raw.githubusercontent.com/psyb0t/gitrakz/main/install.sh | bash

That drops the gitrakz command into ~/.local/bin and the config into ~/.config/gitrakz. If ~/.local/bin is not on your PATH, the installer prints the exact one-liner to add it (for both bash and zsh).

System-wide — one shared stack any user in the docker group can drive:

curl -fsSL https://raw.githubusercontent.com/psyb0t/gitrakz/main/install.sh | sudo bash

That puts the command in /usr/local/bin and the config in /etc/gitrakz (root-owned, readable by the docker group). Running as root also installs the GitHub CLI for you if it is missing; the per-user install expects gh to be present already.

Either way it pins the local stack to the latest release tag (never :latest on your machine) and drops docker-compose.yml plus an owner-only .env. The mode is chosen from who runs it — root gives system-wide, otherwise per-user — and you can force it with --user or --system. No source checkout required.

Authenticate GitHub once — the wrapper reads a token from it — then start it. It tracks your own activity by default:

gh auth login
gitrakz start

Open http://127.0.0.1:8080. The rest is deliberately boring:

gitrakz setup            # recreate the config (compose + .env) without clobbering it
gitrakz status
gitrakz logs -f
gitrakz stop
gitrakz upgrade          # re-pin to the latest release, pull it, drop the old image
gitrakz upgrade --rolling # test the moving :latest built from main, just this once
gitrakz uninstall        # remove the command; asks before deleting your data

Configuration lives in ~/.config/gitrakz/.env (or /etc/gitrakz/.env for a system-wide install), and the SQLite database lives beside it in data/ — not hidden in a Docker-managed volume. Edit .env right after install to change the published port (GITRAKZ_PUBLISH_PORT), expose it beyond localhost (GITRAKZ_PUBLISH_ADDR), set an API bearer token (GITRAKZ_AUTH_TOKEN), track a different user (GITRAKZ_GH_USER), or enable the optional LLM features (GITRAKZ_ELELEM_*). gitrakz start picks the changes up. The GitHub token is never written to .env; the wrapper injects it at runtime from gh auth token.

Run it with Docker directly

The wrapper is only a guardrail around Docker. To drive it yourself, pass the token straight through — that is the only auth gitrakz needs (it runs gh inside the container with it):

mkdir -p gitrakz-data
docker run --rm -p 8080:8080 \
  --user "$(id -u):$(id -g)" \
  -e GH_TOKEN="$(gh auth token)" \
  -v "$PWD/gitrakz-data:/data" \
  psyb0t/gitrakz:v0.6.1 run          # pin a release tag, not :latest

That gitrakz-data/ folder is the database on your machine: copy or back it up like any other application data. Add any -e GITRAKZ_* from the Configuration table. Or run the same pinned stack straight from the compose file the installer wrote:

export GH_TOKEN="$(gh auth token)"
docker compose --project-directory ~/.config/gitrakz \
  --env-file ~/.config/gitrakz/.env -f ~/.config/gitrakz/docker-compose.yml up -d
From source
git clone https://github.com/psyb0t/gitrakz
cd gitrakz
make build                          # static binary in ./build (via Docker)
GH_TOKEN="$(gh auth token)" ./build/gitrakz run

Local dev without Docker:

cd web && pnpm install && pnpm build && cd ..   # build the embedded SPA
GH_TOKEN="$(gh auth token)" go run ./cmd run

gh must be installed and authenticated (gh auth login) wherever the binary runs — gitrakz shells out to it, using GH_TOKEN for auth.

Configuration

All configuration is environment variables, prefixed GITRAKZ_. Everything has a sane default — with authenticated gh, gitrakz runs with no config at all.

Variable Default What it does
GITRAKZ_GH_USER (gh login) The GitHub user whose activity is tracked. Defaults to the gh CLI's authenticated login, so leave it unset to track yourself; set it to track another user.
GITRAKZ_AUTH_TOKEN (empty) When set, /api requires Authorization: Bearer <token>. Empty = open (single-user / trusted network).
GITRAKZ_SYNC_SINCE 2025-01-01 Earliest activity to pull on a first sync.
GITRAKZ_SYNC_INTERVAL 30m Background incremental-sync cadence.
GITRAKZ_SESSION_GAP 30m Idle gap that starts a new work session.
GITRAKZ_SESSION_LEADIN 25m Padding added before a session's first event (pre-commit work).
GITRAKZ_ELELEM_TYPE openai LLM provider driver — openai (also any OpenAI-compatible endpoint) or anthropic.
GITRAKZ_ELELEM_BASE_URL (empty) API host for the provider, used by describe-work, prose blocks and template generation.
GITRAKZ_ELELEM_MODEL (empty) Model name for the LLM endpoint.
GITRAKZ_ELELEM_API_KEY (empty) API key for the LLM endpoint.

Logging follows the standard LOG_LEVEL / LOG_FORMAT / LOG_ADD_SOURCE env vars. Every request is logged with a requestId that the SPA also echoes to the browser console, so a full request reconstructs across both sides.

Building blocks and templates

A template is programmatic and has three parts, composed from two fixed libraries:

template = { id, name, description,
             form,       // input fields a run collects (rate, lead-in, off-hours…)
             transform,  // ordered pipeline of transform primitives (deterministic)
             layout,     // display-block composition rendering the transform output
             exports }   // [csv, pdf, json]

Transform primitives (compute over the timeline): sessionize, exclude-off-time, split-by-active-days, group-by, aggregate, rate, passthrough, and the LLM-backed describe-work (cached in the DB by prompt/config version).

Display blocks (render the result): heading, text, list, table, keyvalue, metric, code, chart.

Ships with built-in templates — Activity summary, Commits per repo, and a Work sessions timesheet — and "Generate with AI" turns a description into a draft template you review and save. No raw HTML or code is ever authored by hand.

HTTP API

Everything the SPA does is a REST call under /api/v1 (JSON, camelCase), behind GITRAKZ_AUTH_TOKEN when set. The /api/v1 prefix lives in the spec's servers: block; the paths are version-less there.

GET  /                           # the embedded Svelte SPA
GET  /api/v1/owners              # distinct owners
GET  /api/v1/repos?owner=        # repos under an owner
GET  /api/v1/timeline?owner=&repo=&type=&from=&to=&page=&perPage=
GET  /api/v1/sessions?…          # sessionized view + heuristic hours
POST /api/v1/sync                # trigger an incremental sync
GET  /api/v1/sync/status         # last sync status
GET  /api/v1/templates           # list (built-in + custom)
POST /api/v1/templates           # create a custom template
PUT  /api/v1/templates/{id}      # edit (built-ins clone-on-edit)
DELETE /api/v1/templates/{id}    # delete a custom template
POST /api/v1/templates/generate  # LLM-compose a template draft from a description
POST /api/v1/run                 # run a template over a filter -> block document (JSON)
POST /api/v1/export              # export a document / run to csv|pdf|json

The OpenAPI spec at api/api.yml is the source of truth; the server interface and types are generated from it.

Agent integrations

This repo ships a documentation skill for agents that drive a gitrakz instance: setup (installer or Docker), the /api/v1 REST surface, and the GH_TOKEN auth model. It does not pretend gitrakz is an MCP server — gitrakz exposes a REST API, not an MCP endpoint.

Claude Code
claude plugin marketplace add psyb0t/agents
claude plugin install gitrakz@psyb0t

Claude Code asks for the gitrakz URL (and an optional bearer token) when the plugin is enabled; the token is stored as sensitive user configuration.

Codex
codex plugin marketplace add psyb0t/agents
codex plugin add gitrakz@psyb0t

Inside this repository, use $gitrakz. After marketplace installation, use $gitrakz:gitrakz.

OpenClaw

The same skill is published to ClawHub on tagged releases:

openclaw skills install @psyb0t/gitrakz

The detailed setup reference is .agents/skills/gitrakz/references/setup.md.

Development

make lint            # golangci-lint (80+ linters) + go fix diff
make test            # go test -race ./...
make test-coverage   # 90% gate (excludes generated code, /cmd and services)
make generate        # regenerate the OpenAPI server + gorm repos (go generate ./...)
make build           # static binary via Docker
make run-dev         # run in the dev container

Stack: Go on servicepack · HTTP via aichteeteapee serbewr + oapi-codegen strict server · SQLite via gorm + gorm-gen (schema owned by embedded SQL migrations, never AutoMigrate) · LLM via elelem · gh shell-out via commander · Svelte 5 + Vite SPA embedded with go:embed.

Project layout follows golang-standards/project-layout: cmd/ entrypoints, internal/pkg/http/{api,server} (generated API + handlers), internal/pkg/services/http-server (the servicepack service), internal/pkg/db/{models,repositories,migrations}, internal/pkg/transform/* and internal/pkg/common/*, web/ for the SPA.

See CHANGELOG.md for release notes.

Security notes

  • gitrakz runs the gh CLI with whatever credentials it's given — mount a read-scoped token; it only reads activity.
  • Set GITRAKZ_AUTH_TOKEN for anything beyond a trusted single-user network; without it the API is open.
  • Point GITRAKZ_ELELEM_BASE_URL only at endpoints you control or trust — describe-work and template generation send commit titles / diffs there.
  • The SPA renders markdown without raw-HTML injection; template output is typed blocks, never author-supplied HTML.

License

MIT — see LICENSE.


Built with spite using servicepack.

Directories

Path Synopsis
cmd
repogen command
Command repogen runs gorm-gen over the db models to produce the typed repositories.
Command repogen runs gorm-gen over the db models to produce the typed repositories.
internal
app
pkg/common/blocks
Package blocks defines the fixed library of display building blocks used to compose a template's rendered layout.
Package blocks defines the fixed library of display building blocks used to compose a template's rendered layout.
pkg/common/template
Package template defines the programmatic template contract for gitrakz: a saved composition of a form, a transform pipeline, and a display layout — never a saved prompt.
Package template defines the programmatic template contract for gitrakz: a saved composition of a form, a transform pipeline, and a display layout — never a saved prompt.
pkg/common/templates
Package templates provides gitrakz's built-in template library: a small set of deterministic, Builtin=true templates seeded into the templates table on every service boot, so the template runner and templates manager have something to run before any user or LLM-authored template exists.
Package templates provides gitrakz's built-in template library: a small set of deterministic, Builtin=true templates seeded into the templates table on every service boot, so the template runner and templates manager have something to run before any user or LLM-authored template exists.
pkg/common/transform
Package transform defines the pipeline contract for gitrakz templates: an ordered chain of primitives that reshape a selected timeline into a typed display document.
Package transform defines the pipeline contract for gitrakz templates: an ordered chain of primitives that reshape a selected timeline into a typed display document.
pkg/common/types
Package types defines the shared domain vocabulary for gitrakz — events, sessions, form values — as plain data structs.
Package types defines the shared domain vocabulary for gitrakz — events, sessions, form values — as plain data structs.
pkg/config
Package config loads gitrakz's runtime configuration from environment variables prefixed with GITRAKZ_.
Package config loads gitrakz's runtime configuration from environment variables prefixed with GITRAKZ_.
pkg/db
Package db wires gitrakz's SQLite storage: opening the database, running the embedded migrations, and exposing a typed Store facade over the generated gorm-gen repositories.
Package db wires gitrakz's SQLite storage: opening the database, running the embedded migrations, and exposing a typed Store facade over the generated gorm-gen repositories.
pkg/db/migrations
Package migrations embeds gitrakz's SQLite schema migrations into the binary so it stays self-contained — no migrations directory to mount, no version skew between the binary and the SQL files.
Package migrations embeds gitrakz's SQLite schema migrations into the binary so it stays self-contained — no migrations directory to mount, no version skew between the binary and the SQL files.
pkg/engine
Package engine runs a template's transform pipeline over a selected timeline and renders the result into a display Document via the template's layout — the glue between the transform and template packages.
Package engine runs a template's transform pipeline over a selected timeline and renders the result into a display Document via the template's layout — the glue between the transform and template packages.
pkg/export
Package export serializes a blocks.Document into the output formats gitrakz exposes to callers: raw JSON, CSV, and PDF.
Package export serializes a blocks.Document into the output formats gitrakz exposes to callers: raw JSON, CSV, and PDF.
pkg/ghsync
Package ghsync pulls a GitHub user's activity into gitrakz's local store, incrementally.
Package ghsync pulls a GitHub user's activity into gitrakz's local store, incrementally.
pkg/http/api
Package api provides primitives to interact with the openapi HTTP API.
Package api provides primitives to interact with the openapi HTTP API.
pkg/http/server
Package server implements gitrakz's HTTP handlers as the generated api.StrictServerInterface.
Package server implements gitrakz's HTTP handlers as the generated api.StrictServerInterface.
pkg/services/http-server
Package httpserver is gitrakz's only servicepack service: it boots the whole app — SQLite storage + migrations, the gh sync engine, the transform/template engine, the LLM adapters, and an aichteeteapee/serbewr HTTP server mounting the generated API under /api/v1/ plus the embedded Svelte SPA — and runs a background sync ticker for the lifetime of the process.
Package httpserver is gitrakz's only servicepack service: it boots the whole app — SQLite storage + migrations, the gh sync engine, the transform/template engine, the LLM adapters, and an aichteeteapee/serbewr HTTP server mounting the generated API under /api/v1/ plus the embedded Svelte SPA — and runs a background sync ticker for the lifetime of the process.
pkg/transform/aggregate
Package aggregate implements the "aggregate" transform primitive: it folds every existing State.Rows entry's Values[field] down to one summary value (sum, avg, min, or max) and appends the result as a new Row.
Package aggregate implements the "aggregate" transform primitive: it folds every existing State.Rows entry's Values[field] down to one summary value (sum, avg, min, or max) and appends the result as a new Row.
pkg/transform/describework
Package describework implements the "describe-work" 🤖 transform primitive: it turns a group of raw commit titles into a one-line, natural-language description of the work — because commit subjects like "fix", "wip", or "asdf" don't belong in a timesheet or client report.
Package describework implements the "describe-work" 🤖 transform primitive: it turns a group of raw commit titles into a one-line, natural-language description of the work — because commit subjects like "fix", "wip", or "asdf" don't belong in a timesheet or client report.
pkg/transform/excludeofftime
Package excludeofftime implements the "exclude-off-time" transform primitive: it subtracts recurring daily off-hours windows (lunch breaks, end-of-day cutoffs, etc.) from every session's duration.
Package excludeofftime implements the "exclude-off-time" transform primitive: it subtracts recurring daily off-hours windows (lunch breaks, end-of-day cutoffs, etc.) from every session's duration.
pkg/transform/groupby
Package groupby implements the "group-by" transform primitive: it buckets the timeline by a chosen Event field and writes one Row per bucket to State.Rows, each carrying the bucket's event count and additions/ deletions totals.
Package groupby implements the "group-by" transform primitive: it buckets the timeline by a chosen Event field and writes one Row per bucket to State.Rows, each carrying the bucket's event count and additions/ deletions totals.
pkg/transform/passthrough
Package passthrough implements the "passthrough" transform primitive: it projects the raw event timeline directly into a display table with no aggregation, grouping, or filtering.
Package passthrough implements the "passthrough" transform primitive: it projects the raw event timeline directly into a display table with no aggregation, grouping, or filtering.
pkg/transform/rate
Package rate implements the "rate" transform primitive: it derives a dollars value for every row by multiplying its hours value by an hourly rate resolved from the run's form values, falling back to a params default.
Package rate implements the "rate" transform primitive: it derives a dollars value for every row by multiplying its hours value by an hourly rate resolved from the run's form values, falling back to a params default.
pkg/transform/registry
Package registry wires every built-in transform primitive into a transform.Registry so the template engine can build a pipeline from a template's named steps.
Package registry wires every built-in transform primitive into a transform.Registry so the template engine can build a pipeline from a template's named steps.
pkg/transform/sessionize
Package sessionize implements the "sessionize" transform primitive: it clusters a timeline's events into per-owner work sessions using a gap-based heuristic, so downstream primitives can reason about continuous blocks of work instead of raw event timestamps.
Package sessionize implements the "sessionize" transform primitive: it clusters a timeline's events into per-owner work sessions using a gap-based heuristic, so downstream primitives can reason about continuous blocks of work instead of raw event timestamps.
pkg/transform/splitbyactivedays
Package splitbyactivedays implements a transform.Primitive that groups State.Sessions by the UTC calendar day of each session's start time and emits one aggregated Row per active day.
Package splitbyactivedays implements a transform.Primitive that groups State.Sessions by the UTC calendar day of each session's start time and emits one aggregated Row per active day.
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL