gitrakz

module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 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

Docker
docker run --rm -p 8080:8080 \
  -e GITRAKZ_GH_USER=your-github-username \
  -e GITRAKZ_DB_PATH=/data/gitrakz.db \
  -v gitrakz-data:/data \
  -v "$HOME/.config/gh:/root/.config/gh:ro" \
  psyb0t/gitrakz:latest

Then open http://localhost:8080. gitrakz uses the mounted gh credentials to sync; trigger one from the UI ("Sync now") or wait for the background ticker.

From source
git clone https://github.com/psyb0t/gitrakz
cd gitrakz
make build                          # static binary in ./build (via Docker)
GITRAKZ_GH_USER=your-username ./build/gitrakz run

Local dev without Docker:

cd web && pnpm install && pnpm build && cd ..   # build the embedded SPA
GITRAKZ_GH_USER=your-username go run ./cmd run

gh must be installed and authenticated (gh auth login) wherever the binary runs.

Configuration

All configuration is environment variables, prefixed GITRAKZ_. GITRAKZ_GH_USER is required; everything else has a sane default.

Variable Default What it does
GITRAKZ_GH_USER (required) The GitHub user whose activity is tracked.
GITRAKZ_HTTP_ADDR :8080 Listen address for the HTTP server + embedded SPA.
GITRAKZ_AUTH_TOKEN (empty) When set, /api requires Authorization: Bearer <token>. Empty = open (single-user / trusted network).
GITRAKZ_DB_PATH /data/gitrakz.db SQLite file. Migrations run on boot.
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.

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