Skip to content
reposummary
Edit
On this page

#reposummary

reposummary is a command-line tool that turns a git repository's commits over a chosen time window or revision range into a Markdown journal, with optional LLM-written narration. It is built for developers and AI agents who need a readable account of what a repository has been doing without walking the log commit by commit. Everything in the journal -- commit classification, per-directory churn, issue references, authors and tags -- is extracted deterministically from git, so LLM tokens are spent only on the optional prose layer.

#Quick start

Install the binary, then summarize a repository. --window and --synthesis are both required and have no default, so every run states which span it covers and whether an LLM narrates it.

go install github.com/smm-h/reposummary@latest

reposummary summarize . --window today --synthesis none

npm (npm i -g reposummary) and PyPI (pip install reposummary) ship wrappers that download the same prebuilt binary.

#Documentation

The usage guide covers every window form, the three synthesis backends and the on-disk cache. The CLI reference is generated from the command declarations themselves, so it lists each flag exactly as the binary parses it.

#Packages

The work is split so that the expensive part stays optional: window parsing, git extraction and digesting are deterministic and cost nothing, rendering turns a digest into Markdown, and synthesis is the only stage that talks to a model. The cache sits underneath, keyed on the inputs that fully determine a journal, and prunes entries unread for 90 days.

#internal/window

Package window parses the --window flag into a resolved time/rev range spec.

A window is either a "daterange" (git --since/--until dates over a tip) or a "range" (an explicit git revision range like a..b, or the full history). Parsing is strict: an unrecognized form is a hard error, never a silent fallthrough.

#Spec

Go go
type Spec struct

Spec is a resolved time window.

#ParseWindow

Go go
func ParseWindow(spec string) (Spec, error)

ParseWindow resolves a window spec string. Forms are matched in order; an unrecognized form returns an error listing the supported forms.

#internal/gitdata

Package gitdata extracts commit metadata, file churn, and tags from a git repository over a resolved window. Everything is read deterministically from git subprocesses; no LLM tokens are spent here.

#FileChange

Go go
type FileChange struct

FileChange is a single file touched by a commit. Added/Deleted are 0 for binary files (git reports "-" for those).

#Commit

Go go
type Commit struct

Commit is a single commit's extracted metadata.

#Tag

Go go
type Tag struct

Tag is a git tag whose target resolves to a collected commit.

#GitBase

Go go
func GitBase(repo string) ([]string, error)

GitBase returns the git command prefix for a repo, or an error if the path is not a git repository. It supports normal repos (.git dir or a working git rev-parse) and the ".bare" convention (a bare repo stored under /.bare).

#Collect

Go go
func Collect(base []string, win window.Spec, tip string) ([]Commit, []Tag, error)

Collect gathers commits, their file churn, and matching tags for a window. Empty output yields empty slices and a nil error.

#internal/digest

Package digest turns raw git data into a structured, deterministic summary: commit classification, per-directory churn, issue references, author counts, and per-day activity. This is the free (no-LLM) narrative layer.

#Digest

Go go
type Digest struct

Digest is the structured summary of a window's activity.

#Classify

Go go
func Classify(c gitdata.Commit) string

Classify returns the change category for a commit: one of "breaking", "feature", "fix", or "other".

#CleanSubject

Go go
func CleanSubject(c gitdata.Commit) string

CleanSubject strips a Conventional Commits prefix if present, otherwise returns the subject unchanged. The result is trimmed.

#Build

Go go
func Build(commits []gitdata.Commit, tags []gitdata.Tag, win window.Spec, repoName string) Digest

Build assembles a Digest from collected commits and tags. Commits are assumed to be newest-first (git log order).

#internal/render

Package render turns a Digest (plus an optional LLM narrative) into the final Markdown journal, and produces the compact plain-text digest fed to the LLM prompt. It never imports synth, avoiding an import cycle.

#Markdown

Go go
func Markdown(d digest.Digest, narrative, version string) string

Markdown renders the full journal for a digest and narrative.

#DigestForLLM

Go go
func DigestForLLM(d digest.Digest) string

DigestForLLM produces a compact plain-text digest for the LLM prompt.

#internal/synth

Package synth turns a digest into prose via an explicitly-chosen LLM backend.

There is NO silent fallback: if the chosen backend fails at runtime, that is a hard error. The caller picks "none", "claude-cli", or "anthropic-api"; the choice is honored or it errors.

#PROMPT_TEMPLATE

Go go
const PROMPT_TEMPLATE = `You are writing a short journal entry narrating what happened in a software repository over a time window.

PROMPT_TEMPLATE instructs the model to write flowing prose from the digest.

#Synthesize

Go go
func Synthesize(d digest.Digest, mode, model string) (string, error)

Synthesize produces narrative prose for a digest using the chosen backend.

#internal/cache

Package cache is a deterministic on-disk journal cache. The journal for a fixed (firstSHA, lastSHA, synthesis, model, version, windowLabel) tuple is deterministic, so identical windows reuse cached output: cost is O(new commits), not O(window size). Storage is plain files; no database. Entries age out: a successful write prunes entries not read in the last 90 days, while a cache hit refreshes an entry's mtime so hot entries stay warm.

#Cache

Go go
type Cache struct

Cache is a filesystem-backed journal cache rooted at a directory.

#New

Go go
func New(dir string) (*Cache, error)

New opens (creating if needed) a cache at dir. An empty dir uses DefaultDir().

#DefaultDir

Go go
func DefaultDir() string

DefaultDir returns the default cache directory: $XDG_CACHE_HOME/reposummary, or ~/.cache/reposummary.

#MakeKey

Go go
func MakeKey(firstSHA, lastSHA, synthesis, model, version, windowLabel string) string

MakeKey returns the sha256 hex of the join of the cache inputs. The journal is fully determined by this tuple.

windowLabel is part of the key because a zero-commit window has empty firstSHA/lastSHA: without the label, every distinct empty window would collapse to the same key and a cached "no activity" journal could be served under the wrong window heading.

#Cache.Get

Go go
func (c *Cache) Get(key string) (string, bool)

Get reads a cached journal by key. The second return is false on a miss. On a hit the entry's mtime is refreshed so that frequently-read entries survive age-based pruning. A failed touch is non-fatal (the read still succeeds).

#Cache.Set

Go go
func (c *Cache) Set(key, md string) error

Set writes a journal to the cache under key, then opportunistically prunes entries older than maxAge. Pruning is best-effort housekeeping: its failures are swallowed and never turn a successful write into an error.

Search