stillhere

module
v0.0.0-...-d4eba55 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: Apache-2.0

README

stillhere

what dat code do tho

Change analytics for git and GitHub (what landed, what lasted, who knows it) for tech leads and their agents. Every number traces back to the commits behind it.

stillhere measures specific properties of code changes: how long they lasted, how much they simplified, how much review they got. It does not measure a person's value, and it must not be the basis for performance or pay decisions.

Status: pre-release. Built: the code of milestones M0 to M2, except M2's GitHub App (sync github uses your own GitHub token), and the MCP server of M4 (product brief). Pending: the first release, so for now you install from source (see Install), and the labelled-sample check: no ranked metric has been validated against a lead's labels yet, so every rank is marked beta, and stillhere label and stillhere validate run the check on your own code (below).

Install

stillhere is one static binary for macOS, Linux and Windows, on amd64 and arm64. CI runs the tests on Linux only: it builds the macOS and Windows binaries on every change, but does not test them. stillhere runs the git on your PATH, which must be 2.45 or newer (git version tells you).

No release is tagged yet. Until the first one, install from source with Go 1.27 or newer, either with go install, which needs no checkout:

go install github.com/IHaveASegway/stillhere/cmd/stillhere@main     # the newest commit on main

@main asks for the branch's newest commit. @latest would too, before a tag exists, but through the Go module proxy (proxy.golang.org, the default GOPROXY) it resolves to the commit the proxy last saw, which can lag a fresh push by minutes to hours (round 2 got a build six merges behind). Whether go install goes through the proxy at all is go env GOPROXY and go env GONOPROXY: a module that GONOPROXY (or GOPRIVATE, when GONOPROXY is unset) matches is fetched from GitHub directly, and GOPROXY=direct go install …@main does so once. stillhere version prints the commit a binary was built from.

go install puts the binary in $(go env GOBIN) when that is set, else in $(go env GOPATH)/bin (~/go/bin by default). Or build it from a checkout, which also stamps the commit and the build date into stillhere version:

git clone https://github.com/IHaveASegway/stillhere.git
cd stillhere
make build                                                # writes bin/stillhere
mkdir -p ~/.local/bin && cp bin/stillhere ~/.local/bin/   # or any other directory on your PATH

Either way, that directory must be on your PATH, or every command below fails with "command not found": command -v stillhere prints the binary's path when it is, and stillhere --version prints the version. Windows has no make; use go install there.

From the first release on:

# Homebrew, macOS or Linux. The cask installs Homebrew's git, which meets the floor.
brew install --cask ihaveasegway/tap/stillhere

# Go 1.27 or newer: @latest is then the newest release
go install github.com/IHaveASegway/stillhere/cmd/stillhere@latest

# Docker, linux/amd64 and linux/arm64, with git included
docker pull ghcr.io/ihaveasegway/stillhere

Each release also carries archives for every platform, which hold the shell completions and the agent skill beside the binary, and a checksums file to verify them against.

Getting started in five minutes

On a clone of your own (fetch it first: stillhere reads only what is cloned):

cd ~/src/your-repo
stillhere week .                                   # 1. what landed this week, and who wrote it
stillhere scan .                                   # 2. record a run: history, blame, metrics, ranks (seconds to minutes)
stillhere report                                   # 3. read it again: each card says what the code is made of, where
                                                   #    knowledge has gone quiet, and what could make the numbers wrong
stillhere identities --format mailmap >> ~/your.mailmap  # 4. one person, two addresses? review the suggestions,
stillhere --mailmap ~/your.mailmap scan .          #    then scan with them
stillhere who-knows src/payments                   # 5. who holds a path, and who works on it now
stillhere report --rank durable --rank simplifying # 6. ranks, as 95% intervals: only people with 10 or more changes are ranked
stillhere explain durable --person "Your Name"     # 7. the changes behind any number

Then, optionally, stillhere sync github --org your-org for reviews (review coverage, reviewers, reviews_given@1), and claude mcp add to ask from your agent. docs/getting-started.md walks through every step on a public repository, with its real output and how to read it.

Quickstart

stillhere week ~/src/acme                                  # the last 7 days, per person
stillhere week ~/src/acme/api ~/src/acme/web --days 14     # two repositories, two weeks
stillhere week ~/src/acme --format md > week.md            # Markdown, for a status note
stillhere week ~/src/acme --now 2026-10-07 --format json   # same inputs, same bytes once timings is removed
stillhere help week

week prints what landed on each repository's default branch: one row per person with their landings (a merged branch counts for the people who wrote it), commits, lines added, lines added to production code and the merges they landed, with bots in a table of their own. Text and Markdown print the first 10 rows of each table; --all prints every row. Each PATH is a repository (a work tree or a bare mirror) or a group: a directory whose immediate children are repositories. week reads only what is already cloned and stores nothing; fetch first for fresh numbers.

Two files in the current directory change the answer when present. .mailmap folds one person's addresses into one identity (--mailmap FILE names another). stillhere.toml replaces the packaged policy as a whole: which paths count as production code, which authors are bots (--policy FILE names another). To start one, stillhere policy > stillhere.toml writes the packaged policy out as a complete file. The packaged policy counts as production code neither example code (examples/), benchmarks (benches/, benchmarks/), documentation tooling (docs/) nor build, packaging and CI tooling (build/, scripts/, vite.config.ts, setup.py, .github/), except Go code, which keeps its role under docs/, build/ and script/ because in Go a directory is a package, and after a scan stillhere paths lists every path with its role and the rule that gave it, so you can check what counts on your own layout (classification.md).

To keep a history, scan instead: scan records a run in facts.db (history, blame, metrics and ranks), and the read commands answer from the latest run, or from --run ID, without touching the repositories again.

stillhere scan ~/src/acme                           # record a run: the default branch of each repository
stillhere report --rank durable@1                   # the run as a report, with durable@1's rank table
stillhere who-knows services/billing                # who knows the code under a path, and when each was last active
stillhere paths --skipped                           # which paths a run left out, and the rule that decided each
stillhere explain durable@1 --person "Ada Example"  # the changes behind one person's value: a name, @login, id or email
stillhere explain review_coverage --repo api        # each change behind a repository's number: reviewed, not, or left out and why
stillhere me --email you@example.com                # your own page
stillhere identities --format mailmap               # .mailmap suggestions with their evidence, never applied
stillhere sync github --org acme                    # mirrors, pull requests and reviews: the one network command
stillhere sync github --repo acme/api               # or only the repositories you name (repeatable)
stillhere scan ~/.stillhere/mirrors/acme            # then scan the mirrors sync keeps

A first sync github reads the pull requests updated in the last 365 days, the window every metric reads (--since DATE reaches further back), and checks its copy against GitHub's own count, reading the window again if any are missing; a later sync reads what changed. It also asks GitHub which account each author email of the history belongs to, once per email and a hundred emails a request: the evidence stillhere identities suggests merges from. On 2026-10-15 a first sync of the cli organization (cli/cli has about 1,300 pull requests updated in a year and 732 author emails) took 2 minutes 15 seconds, and of go-chi's 20 repositories 28 seconds. An interrupted sync loses nothing: the next one reads the same range again. Archived repositories and forks of an organization are left out, their mirrors moved to <mirrors>/.stale, unless --include-archived or --include-forks. The last line says how the sync went.

Each mirror gets a commit-graph with changed-path filters (git 2.46 or later; on 2.45, a commit-graph without them), which made scan's blame about three times faster on the same facts. scan never writes into your own clones; when blame in one is slow, it logs the command that does the same there: git -c commitGraph.changedPathsVersion=2 commit-graph write --reachable --changed-paths (version 2 filters, which read the same on every platform; on git 2.45, git commit-graph write --reachable).

One person who committed from several addresses counts as several people, and an old address of someone still active looks like knowledge that left. report says so where the numbers are: knowledge loss and concentration carry an unmerged-identities caveat with the range (as keyed, with the high-confidence lines merged, with every candidate merged) wherever merging would move them. To merge them, review and add the suggested lines, then scan again; the high-confidence ones, such as two addresses GitHub attributes to one account after sync github, are written as lines and the rest commented out. Append (>>) to a file outside the repositories: the suggestions leave out a line a scan already applied, so writing over the file would drop the merges an earlier scan made, and a repository's own .mailmap is git's, to commit there if you want everyone to get the merge:

stillhere identities                                         # the candidates, with the evidence for each
stillhere identities --format mailmap >> ~/acme.mailmap      # review, uncomment what you know to be one person
stillhere --mailmap ~/acme.mailmap scan ~/src/acme           # the merges take effect at the next scan

Run identities again after the first sync github: GitHub's links arrive with it and make most of the high-confidence lines. Hand-off guesses are listed last, and in text only with --all.

Knowledge loss counts someone as gone quiet after 180 days without a landing, a review, a pull request opened or pushed to, or a commit on any branch or tag of the clone, across every repository of the scan, so a repository scanned alone can read higher than in its organization's scan. Every page shows each holder's last activity, and a card names the people holding lines who are within 30 days of the line ("going quiet within 30 days"), with a going-quiet caveat that says what knowledge loss becomes once they cross it.

In Docker, mount the repositories and run as the user who owns them, because git refuses to read a repository that belongs to someone else ("dubious ownership"):

docker run --rm --user "$(id -u):$(id -g)" -v "$HOME/src:/work:ro" \
  ghcr.io/ihaveasegway/stillhere week /work/acme

The image is published from the first release on. Until then, build it from a checkout with go run github.com/goreleaser/goreleaser/v2@v2.18.3 release --snapshot --clean, as the Dockerfile says. That loads one image per platform into your Docker daemon, tagged with its platform, such as ghcr.io/ihaveasegway/stillhere:latest-arm64: run that name in place of ghcr.io/ihaveasegway/stillhere.

On macOS, git may report dubious ownership when containers with different --user values read one mount, because Docker Desktop does not always show a container a mounted file's owner as the Mac has it. Use one --user for every container that reads a mount, or use the binary.

The commands that store facts (scan and sync github) keep them under STILLHERE_HOME, which is /data in the image, a directory only the image's own user may enter. When you pass --user, mount a directory that user owns there, such as -v "$HOME/.stillhere:/data".

Check the ranks on your own code

A rank says that some people's changes last longer, simplify more or get more review than others'. Whether that matches what matters in your code is something only you can judge, so stillhere asks you:

stillhere label        # about 100 of the run's changes, one key each: m meaningful, r routine, n noise, s skip, q quit
stillhere validate     # per ranked metric, the AUC: how often a meaningful change scores above a routine one

label shows each change without any metric value or author, appends every answer to labels.csv beside the facts database (outside your repositories: the labels judge colleagues' work), resumes where you stopped, and asks until each ranked metric has the 50 meaningful or routine labels its check needs. label --sheet > ~/.stillhere/sheet.csv writes the same sample for a spreadsheet, which validate --labels ~/.stillhere/sheet.csv reads back; keep it out of your repositories too. An AUC of 0.5 is chance, and the bar is 0.7 or more on at least 50 labels. Beside each AUC, validate prints the AUC of change size alone over the same changes, and under the table each metric's verdict: whether it separates meaningful from routine changes, and whether it beats size, since a metric that does not says no more than "big changes matter". validate --scores > ~/.stillhere/scores.csv writes the per-change numbers, to check that yourself; they hold your labels of colleagues' changes, so keep the file out of your repositories too. Clearing the bar tells you the metric tracks what you call meaningful in your code, not that it beats size; the beta mark stillhere prints changes only when the metric's published validity result does, so report yours. Until a lead's labels exist, each ranked metric's caveat gives a reference result from a model panel, with its verdict: three AI models that labelled 143 changes of four public repositories without seeing any metric value, which is evidence, not a lead's judgment (docs/validation.md). docs/validation.md has the rubric, worked examples from public repositories, and how each metric scores a change.

Use it from an agent (MCP)

stillhere mcp serves facts.db to your agent over the Model Context Protocol, on stdio: overview, person, who_knows, changes, explain, highlights, identity_candidates, metrics and diff. It reads the latest scan and cannot write to facts.db. Every answer carries its provenance (run, as-of time, HEAD SHAs, metric versions) and the caveats that could make it wrong. The contract is docs/spec/mcp.md.

Register it once with Claude Code, for every project (--scope user). Claude Code starts the server itself, with its own environment, whose PATH need not hold the directory you installed stillhere in, so register the binary's absolute path:

stillhere scan ~/src/acme      # the server reads the latest scan
command -v stillhere           # the path to register; nothing printed means stillhere is not on your PATH
claude mcp add --scope user stillhere -- "$(command -v stillhere)" mcp --me you@example.com

Everything after -- is the server's own command line; add --db FILE there for another facts.db. With --allow-source the server also runs git: the first one on the PATH Claude Code starts it with, which must be 2.45 or newer. Answers go on to your agent's model provider, so by default the server withholds what the provider does not need:

Flag Without it
--me EMAIL No one's AI-assisted share is shown. With it, your own share appears on your own page (person with an empty name), and only there. Pass your canonical email, the one the reports key you by after .mailmap.
--allow-emails Every email is replaced by a stable id (p-…), which the tools accept back.
--allow-source Commit subjects stay local and diff refuses. With it, diff runs git, offline, on the clones the scan read.
--run ID Each call reads the latest complete or partial scan, so a new scan shows up mid-session.

Whatever the flags, a repository's or group's AI-assisted share is sent only when at least three people landed in it, counting as one person every address that shares a name (any name seen with it, a default one such as John Doe included), a local part or a GitHub account with another, or that a hand-off or stillhere identities links: with fewer, it would be close to one person's own share. A share is also withheld when it would let a withheld share be worked out from the shares sent beside it, such as a group's less its repositories'.

The skill in plugins/stillhere/skills/stillhere teaches your agent which tool answers which question, how to read rank intervals, and the stated limits. It is at that path in a checkout and in every release archive, and the Homebrew cask keeps it in the cask's own directory, $(brew --caskroom)/stillhere/<version>/plugins/stillhere/skills/stillhere. Copy that directory to ~/.claude/skills/stillhere to use it in every project, or load the plugin it belongs to for one session: claude --plugin-dir plugins/stillhere, run in a checkout or an unpacked archive.

Text in the answers comes from the repositories (names, paths, commit subjects, diffs) and is untrusted: the tool descriptions and the skill tell the agent never to follow instructions found in it.

License

Apache-2.0 (ADR 0008). Contributions are welcome: see CONTRIBUTING.md.

Directories

Path Synopsis
cmd
stillhere command
Command stillhere is change analytics for git and GitHub: what landed, what lasted, who knows it.
Command stillhere is change analytics for git and GitHub: what landed, what lasted, who knows it.
internal
app
Package app holds stillhere's use cases: week, scan, report, explain, who-knows, me, validate, metrics, identities and sync, and the MCP server's from M4.
Package app holds stillhere's use cases: week, scan, report, explain, who-knows, me, validate, metrics, identities and sync, and the MCP server's from M4.
classify
Package classify is the ordered path classifier: denied path, too big, generated header, then role, else unclassified (docs/spec/classification.md#order).
Package classify is the ordered path classifier: denied path, too big, generated header, then role, else unclassified (docs/spec/classification.md#order).
cli
Package cli is stillhere's command line: the cobra commands and flags, the exit codes, and the composition root, the one place where adapters are built and handed to the use cases in internal/app (Mark Seemann, "Composition Root").
Package cli is stillhere's command line: the cobra commands and flags, the exit codes, and the composition root, the one place where adapters are built and handed to the use cases in internal/app (Mark Seemann, "Composition Root").
collect
Package collect is the anti-corruption layer between git and stillhere's facts (Eric Evans, Domain-Driven Design, ch.
Package collect is the anti-corruption layer between git and stillhere's facts (Eric Evans, Domain-Driven Design, ch.
domain
Package domain is stillhere's core vocabulary: the facts the collectors learn from git (and, from M2, GitHub), the metric values computed over those facts, the ports through which the core reaches the outside world, and the typed errors that decide exit codes.
Package domain is stillhere's core vocabulary: the facts the collectors learn from git (and, from M2, GitHub), the metric values computed over those facts, the ports through which the core reaches the outside world, and the typed errors that decide exit codes.
fixture
Package fixture builds the git repositories that testdata/fixture/SPEC.md specifies, with the real git binary, so that every oracle test reads real porcelain rather than a recording of it.
Package fixture builds the git repositories that testdata/fixture/SPEC.md specifies, with the real git binary, so that every oracle test reads real porcelain rather than a recording of it.
gitcli
Package gitcli is the outbound adapter that implements domain.GitRunner over the git binary.
Package gitcli is the outbound adapter that implements domain.GitRunner over the git binary.
github
Package github is the outbound adapter that implements domain.ForgeSource over GitHub: the GraphQL API for repositories, pull requests and reviews, and git over HTTPS for the managed mirrors (ADR 0004).
Package github is the outbound adapter that implements domain.ForgeSource over GitHub: the GraphQL API for repositories, pull requests and reviews, and git over HTTPS for the managed mirrors (ADR 0004).
identity
Package identity answers who a canonical email is: the display name, whether it is a bot, whether a change was AI-assisted, and who gets credit for it (docs/spec/git-invocation.md#identity-mailmap-and-bots, ADR 0007); and who a forge login is (Reviewer, ADR 0004).
Package identity answers who a canonical email is: the display name, whether it is a bot, whether a change was AI-assisted, and who gets credit for it (docs/spec/git-invocation.md#identity-mailmap-and-bots, ADR 0007); and who a forge login is (Reviewer, ADR 0004).
mcp
Package mcp is stillhere's MCP server (M4, #15, ADR 0006): the agent interface to a platform (a group of repositories) or one repository.
Package mcp is stillhere's MCP server (M4, #15, ADR 0006): the agent interface to a platform (a group of repositories) or one repository.
metrics
Package metrics is the metric registry and every metric version (docs/metrics.md).
Package metrics is the metric registry and every metric version (docs/metrics.md).
policy
Package policy is the model of stillhere.toml: the noise rules, roles, bot and AI patterns, and thresholds that one run applies.
Package policy is the model of stillhere.toml: the noise rules, roles, bot and AI patterns, and thresholds that one run applies.
rank
Package rank turns per-change measurements into honest ranks, one metric at a time (ADR 0001, docs/metrics.md#ranking-rules): a bootstrap gives each subject a 95% interval for its value and for its rank, subjects below the metric's minimum sample are listed and not ranked, and AUC scores a metric against hand labels for the validity check.
Package rank turns per-change measurements into honest ranks, one metric at a time (ADR 0001, docs/metrics.md#ranking-rules): a bootstrap gives each subject a 95% interval for its value and for its rank, subjects below the metric's minimum sample are listed and not ranked, and AUC scores a metric against hand labels for the validity check.
redact
Package redact keeps out of an output what must not reach it: it removes credentials from remote URLs and git messages before anything is stored, logged or printed (docs/spec/git-invocation.md#url-scrubbing), and finds the email addresses that a page or an answer withholds (address.go).
Package redact keeps out of an output what must not reach it: it removes credentials from remote URLs and git messages before anything is stored, logged or printed (docs/spec/git-invocation.md#url-scrubbing), and finds the email addresses that a page or an answer withholds (address.go).
report
Package report is stillhere's presentation model: the report a week run or a stored scan run renders as (stillhere.report/v1), the answers of the read commands, and the text, Markdown and JSON renderers for all of them (docs/spec/report-json.md).
Package report is stillhere's presentation model: the report a week run or a stored scan run renders as (stillhere.report/v1), the answers of the read commands, and the text, Markdown and JSON renderers for all of them (docs/spec/report-json.md).
sqlite
Package sqlite is the outbound adapter that implements domain.FactStore, and domain.ForgeStore (the GitHub replica, forge.go, and what each run read of it, forgesnapshot.go), on SQLite through modernc.org/sqlite, a pure-Go translation of SQLite, so the binary needs no cgo and stays static (ADR 0002, ADR 0003, ADR 0011).
Package sqlite is the outbound adapter that implements domain.FactStore, and domain.ForgeStore (the GitHub replica, forge.go, and what each run read of it, forgesnapshot.go), on SQLite through modernc.org/sqlite, a pure-Go translation of SQLite, so the binary needs no cgo and stays static (ADR 0002, ADR 0003, ADR 0011).

Jump to

Keyboard shortcuts

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