OpenContextually
Give your coding agent the context it should read before it starts working.
Quickstart · Discussions · ContextBench · Ecosystem · Contributing · Community
OpenContextually turns a task into a small, ranked, explainable context package from your repository. It is not another coding agent — it is the open context layer before the agent.
Agent failed because it read the wrong context? Bring us the case.
Quickstart
pip install git+https://github.com/gammalex-ai/opencontextually
gctx "fix the authentication bug"
Not on PyPI yet. When the first release lands this becomes
pip install opencontextually; until then the line above is the one that works. Python 3.10+, one runtime dependency.
No model. No API key. No network. No database. No setup. One dependency. The same repository and task produce byte-identical output every time.
flowchart TB
T(["your task<br/>fix the authentication bug"]) --> OC
subgraph OC ["OpenContextually · local · deterministic · no model"]
direction LR
S["SELECT<br/>likely files"] --> F["FOLLOW<br/>imports"] --> B["BOUND<br/>repo limits"] --> C["CHECK<br/>gaps + conflicts"] --> E["EXPLAIN<br/>why each file"]
end
OC --> R(["task-ready context<br/>every file with a reason"])
What it looks like
gctx "fix the authentication bug"
Run from examples/auth_bug/ — a small fixture with an auth module, a
config file, docs, and a test — this is the real, unedited output:
fix the authentication bug
6 relevant · 19 excluded
src/auth/middleware.py defines AuthenticationError
README.md defines authentication requirements
tests/test_auth.py imports middleware.py ← via middleware.py
config/auth.yaml configuration referenced by authentication code
docs/security.md defines authentication requirements
src/users/session.py imported by middleware.py ← via middleware.py
⚠ session.timeout_minutes: config/auth.yaml:3 declares 60 minutes, but docs/security.md:6 says 30 minutes
○ No test references session timeout minutes (config/auth.yaml:3)
○ No test references session expired (src/users/session.py:67)
-v for code excerpts
Excluded: 19 files
19 files scanned, not relevant enough
Checks run: configuration_discrepancy, test_reference_gap
Three things happened beyond ranking:
session.pywas reached through an import, not through text. It matches none of the task's words.middleware.pyimports it, and the← viamarker records that edge.- A config/doc disagreement was surfaced — 60 minutes against 30.
- Every file carries a reason, and everything excluded is accounted for.
Search finds matches. Context needs relationships.
Search answers "where do these words occur?". That is a different question from "what should be read for this task?".
grep / ripgrep
task ──────────────────► keyword matches
OpenContextually
task ──► direct matches
│
├──► imported dependencies
├──► tests that exercise them
├──► relevant configuration
└──► governing documentation
│
▼
bounded context package
A task like fix the authentication bug can require a file that never
contains the words authentication or bug. OpenContextually starts from
direct relevance, follows Python's own import graph, applies repository
boundaries, runs two narrow deterministic checks, and explains every
inclusion.
Be honest about the split: relationship-following is the part search cannot do at all, but most of the value on a typical run is ranking and compression of files search could have found. Asking sqlfluff about "indentation rule fires on a templated line", a case-insensitive grep for any of the task's words matches 415 files; OpenContextually returns 12, each with a reason. Both numbers are reproducible from the corpus below.
Install
pip install git+https://github.com/gammalex-ai/opencontextually
Python 3.10+, one runtime dependency. PyPI release pending — this becomes
pip install opencontextually once it is published. To work on
OpenContextually itself:
git clone https://github.com/gammalex-ai/opencontextually
cd opencontextually
pip install -e ".[dev]"
Using it
CLI
| Command | What you get |
|---|---|
gctx "task" |
Ranked files, each with a reason |
gctx "task" -v |
Adds the code excerpt that justified each file |
gctx "task" --all |
Every included file, not just the top slice |
gctx "task" --json |
Full machine representation, for handing to an agent |
gctx "task" --root PATH |
Search somewhere other than the current directory |
gctx is short for GammaLex Context. The same command is also installed
as octx (the original name, kept working) and opencontextually. Flags
compose (-v --all); --json is unaffected by either and is always full
fidelity.
Write tasks the way you'd describe the bug. Naming a specific behavior or symbol beats a directory-shaped noun.
Python
from opencontextually import get_context
package = get_context("fix the authentication bug")
print(package.render()) # the text above
package.to_dict() # the same content as JSON
MCP
Agents that speak MCP can call it directly. Requires the optional extra:
pip install -e ".[mcp]"
Point your MCP client at the opencontextually-mcp command:
{
"mcpServers": {
"opencontextually": {
"command": "opencontextually-mcp"
}
}
}
It exposes exactly one tool — get_context(task, root=".") — returning the
same shape as gctx --json.
Tested on real repositories
Scripted tests lock in fixes; they do not find them. Most real defects in
this project were found by running it against unfamiliar repositories and
reading the output, so a standing corpus is part of the project
(benchmarks/, with a runner that also checks determinism and sweeps for
leaked secrets).
What it selected, and what it missed
Fourteen repositories, each with a hand-checked answer key: the files that
actually implement or test the behaviour the task names, read out of the
project at its pinned commit. The keys are committed in
benchmarks/answer-keys.json.
Six were used while tuning ranking, so their results are fitted to an unknown degree. Eight were held out — their keys were written and committed before the tool was run against them, and nothing was tuned afterwards.
| Group | Repositories | Key files found | In the default view |
|---|---|---|---|
| Tuned | httpx, requests, flask, click, sqlfluff, django | 18/19 (95%) | 16/19 (84%) |
| Held out | black, rich, pydantic, fastapi | 12/16 | 9/16 |
| Held out | attrs, urllib3, pytest, scrapy | 11/13 | 10/13 |
| Held out, combined | 23/29 (79%) | 19/29 (66%) | |
| All fourteen | 41/48 (85%) | 35/48 (73%) |
The two groups disagree by about 16 points, and the held-out figure is the one that predicts what happens on a repository this project has never seen. 79% and 66% are the numbers to argue with.
The default view matters more than the total: the compact output shows eight files, so a key file recovered at rank 15 was found but not delivered.
What holds everywhere: zero fixture, vendor, generated or CI files selected, every path inside the configured root, and 0.05%–2.1% of repository bytes delivered. sqlfluff's 5,249 test fixtures and django's 736 documentation files are excluded in full.
The held-out repositories found what the tuned six could not, which is the entire reason for holding them out:
- A bundled previous major version. pydantic ships Pydantic 1 inside
Pydantic 2. Six of eighteen slots went to
pydantic/v1/*whilemain.pywas missed. Fixed — av1/directory inside a v2 package is now damped. - A repository holding several copies of one document. rich spends five
slots on README translations; fastapi repeats one page across
docs/en,docs/hianddocs/tr; pytest has 50 release announcements. Not fixed. A family cap was written, measured, and reverted for collapsing genuinely different pages that share a filename. - Vocabulary collisions, as on django: rich ranks
progress.py(ProgressColumn) first for a table-width task, and scrapy misses its owntest_dupefilters.py. Tracked as issue #3.
The corpus
Ten public Python projects, each with a plausible task, all reproducible
with benchmarks/dogfood.py:
| Repository | Commit | Files | Time | Task |
|---|---|---|---|---|
| encode/httpx | b5addb64 |
125 | 0.18s | redirect loses the authorization header |
| psf/requests | 5460f467 |
128 | 0.12s | session cookie persists across redirects |
| pallets/click | 36baa15f |
166 | 0.25s | option prompt does not hide the input |
| pallets/flask | d318b683 |
236 | 0.19s | session cookie is not set on redirect |
| psf/black | 8947c48e |
482 | 0.48s | string normalization changes the wrong quotes |
| Textualize/rich | 9d8f9a37 |
553 | 0.63s | table column width ignores the terminal size |
| pydantic/pydantic | f512b087 |
824 | 1.70s | field validator not called on assignment |
| fastapi/fastapi | 49033471 |
3,139 | 2.11s | dependency override not applied in nested routers |
| sqlfluff/sqlfluff | 642e2e4a |
5,955 | 2.18s | indentation rule fires on a templated line |
| django/django | 73cc09f1 |
7,085 | 7.52s | queryset filter drops the second condition |
All ten are MIT- or BSD-licensed public projects, unaffiliated with this
one, chosen for a spread of size and layout rather than for flattering
results. Each was cloned with --depth 1 on 2026-08-30 at the commit
above; file counts and timings are specific to those commits.
18,693 files in total. Zero secret-shaped strings reached any package, and every result was byte-identical across repeat runs. Times are best-of-three on an M-series Mac running Python 3.13 with a warm page cache; treat them as orders of magnitude, not a benchmark.
A caveat on the file counts, because the honest number is smaller than the flattering one: these are all files in a clone. What actually gets read is what survives your ignore rules, and on a repository with heavy build output that is a small fraction. A 42,000-file checkout completing in two seconds sounds impressive and mostly means 41,000 files were gitignored and never opened. Of the 18,693 files above, 16,331 are actually scanned; django's real figure is 5,580 files in 7.5 seconds.
Speed is listed last on purpose. It is a property worth keeping, not the claim — a tool that walks a repository quickly and hands an agent the wrong eight files has not helped anyone.
For gctx "option prompt does not hide the input" against click, the top
three are core.py (defines Option), decorators.py (defines option),
and termui.py (defines _mask_hidden_input) — the third being a private
helper whose name no part of the task literally matches.
Ecosystem & Community
Works with today
Verified by the test suite and by hand against a clean install — nothing here is aspirational.
| Surface | What it is | Status |
|---|---|---|
gctx CLI |
gctx "task", plus --json, -v, --all, --root |
Supported |
| Python API | get_context(task, root=".") returning a ContextPackage |
Supported |
| MCP server | opencontextually-mcp, stdio, one tool: get_context(task, root) |
Supported — see MCP |
| Any MCP-speaking client | Anything that can launch a stdio MCP server and call one tool | Should work; only the server is tested |
Community integrations
Nothing here yet — this project is new and we would rather show an empty
table than a fictional one. The --json output and the MCP server are both
stable, documented interfaces, so anything below is buildable today by
anyone:
- an editor or IDE extension that runs
gctxon the current task - a wrapper for an agent harness — Cursor, Continue, OpenCode, Aider, or your own
- a GitHub Action that posts the context package for an issue onto its PR
- a shell or
tmuxintegration, a TUI, an alternative renderer - language support beyond Python's import graph (see GOOD_FIRST_CONTEXT.md)
Built something with gctx? Open an issue or a PR
and we may feature it here. Community projects are not maintained by us,
and we will say so next to each one.
The question this project is trying to answer
What should an agent know before it acts, and how do we prove it got the right context?
The second half is the hard half, and it is why ContextBench exists: every claim in the section above is checkable against committed answer keys, and the honest number — the held-out one — is the one quoted.
Come argue with the numbers, bring a case where the wrong files were selected, or add a benchmark case from a repository we have never seen: COMMUNITY.md is the map of every way in.
What it deliberately does not do
- Follow imports outside Python. Expansion uses the stdlib
astmodule; other languages get lexical matching only. - Understand your code. Ranking is lexical scoring plus import
expansion. It is weakest when your task's words are also the repo's
naming convention — asking about "the context agent" where many files are
named
*context*— because filename matches then dominate. - Find problems for you. The two checks are narrow, named rules that
expect to stay quiet: they flag detectable gaps and conflicts —
a config value contradicting a documented one, a config key or symbol no
test references — not arbitrary missing context. Across the six
answer-key corpus tasks they produced zero findings, which is the
honest scope: they fire on the patterns they name, and
examples/is where you can watch them do it. Across eleven real repositories, one produced a false positive (since fixed) — the honest measure of how much "high precision" has actually been tested. Silence is the normal outcome; a footer always names which checks ran. - Guarantee secrets stay out of excerpts. Redaction masks secret-shaped keys and high-entropy strings, but it is best-effort pattern matching, not a secrets scanner. See SECURITY.md.
Scope, determinism, and safety
Discovery reads everything under --root minus what git already ignores —
honoring nested .gitignore files, .git/info/exclude, and the global
core.excludesFile, plus an optional .opencontextuallyignore. All
resolved without shelling out to git, so it works in directories that
aren't repositories at all.
Runs are deterministic: the same task and repository produce byte-identical output, which is asserted in the test suite and re-checked by the corpus runner. Nothing is written anywhere, and no network call is ever made.
Contributing
Bug reports, context failures, ContextBench cases, integrations, language support and documentation fixes are all welcome — CONTRIBUTING.md covers the workflow and the scope boundaries, GOOD_FIRST_CONTEXT.md lists concrete places to start, and COMMUNITY.md is where to find people.
License
MIT
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 opencontextually-0.2.0.tar.gz.
File metadata
- Download URL: opencontextually-0.2.0.tar.gz
- Upload date:
- Size: 156.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4795a8f8e4fcd76e52b40bacb3d9828fef09bcd61ce6942c517e2aa2aa14f2c
|
|
| MD5 |
9d0b17aad59daef43911f57366ad2835
|
|
| BLAKE2b-256 |
5ebe4ad6e1e624a9dab7ffc1afa2b6a1a0f7d907032888cf613b9302ff510301
|
Provenance
The following attestation bundles were made for opencontextually-0.2.0.tar.gz:
Publisher:
release.yml on gammalex-ai/opencontextually
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opencontextually-0.2.0.tar.gz -
Subject digest:
b4795a8f8e4fcd76e52b40bacb3d9828fef09bcd61ce6942c517e2aa2aa14f2c - Sigstore transparency entry: 2665048678
- Sigstore integration time:
-
Permalink:
gammalex-ai/opencontextually@925b0e9ec61df981ec2e9ecd6bd40cd2ff990ced -
Branch / Tag:
refs/heads/main - Owner: https://github.com/gammalex-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@925b0e9ec61df981ec2e9ecd6bd40cd2ff990ced -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file opencontextually-0.2.0-py3-none-any.whl.
File metadata
- Download URL: opencontextually-0.2.0-py3-none-any.whl
- Upload date:
- Size: 88.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca62c55992f458784ef50bcff331ad491cea00f799a5fe2c704f92847bf70513
|
|
| MD5 |
0f4d78446f8a756f31a64ab55812dbe5
|
|
| BLAKE2b-256 |
82e3ecba2568534fcc5c0c91d6c6d8c4398ea477a9fde12ffa1dbe8ed7c14a2e
|
Provenance
The following attestation bundles were made for opencontextually-0.2.0-py3-none-any.whl:
Publisher:
release.yml on gammalex-ai/opencontextually
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opencontextually-0.2.0-py3-none-any.whl -
Subject digest:
ca62c55992f458784ef50bcff331ad491cea00f799a5fe2c704f92847bf70513 - Sigstore transparency entry: 2665048733
- Sigstore integration time:
-
Permalink:
gammalex-ai/opencontextually@925b0e9ec61df981ec2e9ecd6bd40cd2ff990ced -
Branch / Tag:
refs/heads/main - Owner: https://github.com/gammalex-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@925b0e9ec61df981ec2e9ecd6bd40cd2ff990ced -
Trigger Event:
workflow_dispatch
-
Statement type: