Local-first CLI that mines git history for file-level co-change patterns and builds a queryable knowledge graph for AI coding agents, with optional enrichment from graphify's symbol/call-graph output when it is installed.
Project description
graphkeeper-cli (Python)
A local-only CLI and library that mines your git log for which files
actually change together, then hands an AI coding agent a queryable answer
instead of a grep across the whole history.
Why this exists
An AI coding agent working solo on a codebase it doesn't fully know yet
usually has no fast way to answer "what else changes when I touch this
file?" short of scrolling git log -p or grepping blindly. GraphKeeper
mines the commit history that's already sitting on disk and turns it into
one queryable, local answer -- no server, no account, no embeddings API,
nothing leaves the machine. This package is the Python distribution -- a
genuine, independent port of the npm package's TypeScript source, not a
wrapper around the Node binary. It has zero third-party runtime
dependencies: only the Python standard library (subprocess, argparse,
json) and the real git binary on PATH.
Install
pip install graphkeeper-cli
or with uv:
uv add graphkeeper-cli
Requires Python 3.9+ and git on your PATH. The complementary JS/TS
distribution installs the same way on the npm side: npm install -g graphkeeper-cli (or npx graphkeeper-cli build to run it once without
installing) -- see the
project README for
that package. Both are first-class and maintained together; neither is
deprecated in favor of the other. As of this writing the npm package's own
publish to the npm registry has not yet been attempted (a separate,
unstarted task), but its source and this Python port are both complete and
kept in parity.
Quickstart
Clone the repo and index it (works against any git repo, including this one):
git clone https://github.com/RudrenduPaul/GraphKeeper.git
cd GraphKeeper/python
pip install -e .
graphkeeper build ..
GraphKeeper build complete: /path/to/GraphKeeper
Co-change graph: 6 commit(s) analyzed, 80 file pair(s) found
graphify enrichment: skipped -- graphify was not found on PATH. Install it with
`uv tool install graphifyy` (or `pipx install graphifyy`) for symbol/call-graph
enrichment; GraphKeeper works fine without it, in co-change-only mode.
Wrote /path/to/GraphKeeper/.graphkeeper/graph.json
(Real output from running this Python CLI against the GraphKeeper repo's
own checkout, this early in its history -- co-change counts grow as the
codebase accumulates more commits. Byte-for-byte the same command against
the npm CLI, against the same checkout, produces the same numbers -- both
distributions mine the same git log.)
Now query it:
graphkeeper query co-change src/git.ts
Files that historically change alongside "src/git.ts":
1 src/store.ts
1 src/types.ts
1 test/git.test.ts
1 test/store.test.ts
1 test/test-helpers.ts
If graphify is installed
(uv tool install graphifyy), graphkeeper build automatically shells out
to its local, no-API-key graphify extract --code-only and merges its
symbol/call-graph into the same store, unlocking call-graph queries.
Without graphify installed, graphkeeper query calls <symbol> explains
exactly why the answer isn't available instead of crashing or returning an
empty result:
Call-graph query for "mineCoChange" is not available.
graphify was not found on PATH. Install it with `uv tool install graphifyy`
(or `pipx install graphifyy`) for symbol/call-graph enrichment; GraphKeeper
works fine without it, in co-change-only mode.
Every command also supports --json for scripts and agents.
Using the library instead of the CLI
from graphkeeper import build, query_co_change, BuildOptions
result = build(".", BuildOptions(skip_graphify=True))
print(f"{result.store.commits_analyzed} commit(s) analyzed")
co_change = query_co_change(result.store, "src/git.ts", limit=5)
for row in co_change.results:
print(row.count, row.file)
graphkeeper.cli is a thin argparse wrapper over these same functions,
so anything scriptable from the command line is also usable directly from
Python -- the agent-native path.
CLI reference
usage: graphkeeper [-h] [-V] {build,query} ...
Commands:
build [options] [path] Mine git history for co-change and (if available)
merge in graphify's symbol/call graph
query co-change <file> List files that historically change alongside
<file>, ranked by co-change frequency
query calls <symbol> Show callers/callees of <symbol> (requires
graphify enrichment)
graphkeeper build [path]
Walks path (default: current directory), runs git log --no-merges --name-only across the whole history, and counts how often each pair of
files was touched in the same commit. Writes the result to
.graphkeeper/graph.json.
| Option | Description |
|---|---|
--json |
emit machine-readable JSON instead of human-readable text |
--max-files-per-commit <n> |
skip commits touching more than this many files (default: 100) -- keeps a single mass-reformat or vendoring commit from drowning out real co-change signal |
--no-graphify |
skip graphify enrichment even if graphify is installed |
graphkeeper query co-change <file>
Lists files that historically changed alongside <file>, ranked by how
many commits touched both.
| Option | Description |
|---|---|
--json |
emit machine-readable JSON instead of human-readable text |
--limit <n> |
cap the number of results |
--graph <path> |
path to a specific graph.json (default: <cwd>/.graphkeeper/graph.json) |
Exit code 0 when results are found, 1 when there's no co-change data
for that file yet, 2 on a usage or filesystem error.
graphkeeper query calls <symbol>
Shows callers and callees of <symbol>, using graphify's calls edges
from the most recent build. Only meaningful when that build included
graphify enrichment -- if it didn't, this prints a clear explanation of why
(never a crash, never a silent empty result).
| Option | Description |
|---|---|
--json |
emit machine-readable JSON instead of human-readable text |
--graph <path> |
path to a specific graph.json (default: <cwd>/.graphkeeper/graph.json) |
Exit code 0 when the symbol is found, 1 when it isn't (or enrichment
wasn't available), 2 on a usage or filesystem error.
How it works
Same pipeline as the npm package (see the
project README
for the full narrative): git log --no-merges --name-only mined via a safe
argv-list subprocess call (never a shell string), pairs of co-changed files
counted per commit (commits touching more than --max-files-per-commit
files skipped), optionally merged with graphify's local symbol/call graph
when graphify is on PATH, and written once, atomically, to
.graphkeeper/graph.json. The on-disk JSON schema uses the same field
names (commitsAnalyzed, coChange, fileCommitCounts, etc.) as the npm
package's store, so a .graphkeeper/graph.json written by either
distribution can be read back by the other.
Security
- Every
gitandgraphifyinvocation uses an argv list passed directly to the OS (subprocess.run,shell=False), never a shell string, so commit messages, file names, or repo paths can't be interpreted as shell syntax. .graphkeeper/output paths are checked against the resolved repo root before every write (symlinks included), so a maliciously crafted repo can't redirect GraphKeeper's writes outside.graphkeeper/.- No telemetry, no network calls, no secrets, zero third-party runtime
dependencies. The only files GraphKeeper reads are
git logoutput and (optionally) graphify's owngraph.json; the only file it writes is.graphkeeper/graph.json. - See SECURITY.md for the private disclosure process.
Contributing
See CONTRIBUTING.md for the full guide, covering both the TypeScript and Python codebases (they must stay in behavioral parity). To build from source:
cd python
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
License
Apache 2.0, see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file graphkeeper_cli-0.1.0.tar.gz.
File metadata
- Download URL: graphkeeper_cli-0.1.0.tar.gz
- Upload date:
- Size: 27.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50a47d9c56cb96e3b682df8fe58d3b794f889f28ff89e43c4b2dead4a673a365
|
|
| MD5 |
a3bbdae05a802c479b2ee7c7577b142a
|
|
| BLAKE2b-256 |
f3a29be3feb1d208fc35322bdee12508f5effaec0918f8dcd9ca0463e7d81f3d
|
Provenance
The following attestation bundles were made for graphkeeper_cli-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on RudrenduPaul/GraphKeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
graphkeeper_cli-0.1.0.tar.gz -
Subject digest:
50a47d9c56cb96e3b682df8fe58d3b794f889f28ff89e43c4b2dead4a673a365 - Sigstore transparency entry: 2194928679
- Sigstore integration time:
-
Permalink:
RudrenduPaul/GraphKeeper@d6086e0c5ee52b0b63c9c0b34ead61b8cf4f47ca -
Branch / Tag:
refs/tags/python-v0.1.0 - Owner: https://github.com/RudrenduPaul
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@d6086e0c5ee52b0b63c9c0b34ead61b8cf4f47ca -
Trigger Event:
release
-
Statement type:
File details
Details for the file graphkeeper_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: graphkeeper_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
897e90a0e7c2c331fed9c3d7e01ecd9cd505de0b486cb00532b299d40c2fd236
|
|
| MD5 |
ce8509831b613577d279b52ea9fa8d8b
|
|
| BLAKE2b-256 |
ba65cffd7210230632b4e6a46714ec364306e1be353057f8a146f501c834959b
|
Provenance
The following attestation bundles were made for graphkeeper_cli-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on RudrenduPaul/GraphKeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
graphkeeper_cli-0.1.0-py3-none-any.whl -
Subject digest:
897e90a0e7c2c331fed9c3d7e01ecd9cd505de0b486cb00532b299d40c2fd236 - Sigstore transparency entry: 2194928691
- Sigstore integration time:
-
Permalink:
RudrenduPaul/GraphKeeper@d6086e0c5ee52b0b63c9c0b34ead61b8cf4f47ca -
Branch / Tag:
refs/tags/python-v0.1.0 - Owner: https://github.com/RudrenduPaul
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@d6086e0c5ee52b0b63c9c0b34ead61b8cf4f47ca -
Trigger Event:
release
-
Statement type: