gitrakz

module
v0.6.3 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, a visible .env.example, and an owner-only .env copied from that example on first install. Later installs and upgrades refresh .env.example but leave your .env alone, apart from its image pin. 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            # refresh compose + .env.example; creates .env only if missing
gitrakz status
gitrakz logs -f
gitrakz stop
gitrakz upgrade          # back up data, re-pin to the latest release, then pull it
gitrakz upgrade --rolling # test the moving :latest built from main, just this once
gitrakz restore ~/.config/gitrakz/backups/<timestamp>.tar.gz
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); its current reference is always beside it as .env.example. SQLite state stays in Docker's named gitrakz-data volume. Every gitrakz upgrade writes a snapshot named YYYYMMDDHHMMSS.tar.gz under the install directory's backups/ folder and keeps the newest three. gitrakz restore <backup.tar.gz> validates the archive, asks before replacing the volume, snapshots its current contents, and leaves the stack stopped for an explicit gitrakz start. 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):

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

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