Lightweight agent-orientation maps and AST code graphs for code and documentation.
Project description
Skyhook
Skyhook builds a small, readable map of a repository — backed by a tree-sitter AST symbol + call graph — and uses it to create task-specific route packs for coding agents, answer structural queries (callers, callees, blast radius), and serve those queries over MCP.
It is not a vector database or full semantic index. It is a fast wayfinding
layer plus a precise structural graph: where to start reading, which docs and
tests matter, which files an edit impacts, and what calls what — without the
agent grep-exploring the repo. A route pack is a fraction of the size of reading
the relevant files by hand (skyhook bench measures that ratio for your repo).
That is an artifact-size number, though. What it buys a real coding agent,
measured on real bugs, is more modest and more useful: roughly a quarter to a
third less cost and about half the turns — mostly by clamping the worst-case
exploration — without changing whether the fix is correct. The honest write-up
is in docs/benchmarking.md.
The Problem
Large repositories burn time and tokens before useful work starts. A new agent session often has to rediscover the same things:
- where the main entrypoints are
- which directories own which features or domains
- where architecture docs, ADRs, C4 diagrams, and design notes live
- which tests matter for a change
- which files are likely edit targets for a bug, review, or implementation task
- what local conventions are worth reading before touching code
Normal search helps after you know what to search for. A full index can help too, but it is heavier than many teams need and can still bury the starting point in too much detail. Skyhook is intentionally smaller. It produces a compact map that agents can read before they open a broad set of files.
How Skyhook Solves It
skyhook init scans the repository and writes generated orientation artifacts under .skyhook/.
The important artifacts are:
.skyhook/INDEX.md: short entrypoint for agents.skyhook/map.md: full generated overview.skyhook/map.json: canonical machine-readable map.skyhook/docs.md: documentation inventory.skyhook/architecture.md: architecture and design references.skyhook/tests.md: discovered tests and verification hints.skyhook/areas/<area>.md: focused notes for detected code areas.skyhook/graph.json: diffable export of the AST symbol + call graph (committed).skyhook/graph.db: the SQLite graph queried byskyhook graphandskyhook mcp(regenerable; gitignored).skyhook/mcp.json: ready-to-use MCP registration (withalwaysLoad) so a coding agent can call the graph tools — seeskyhook mcp
skyhook init builds the map and the graph in one pass. skyhook graph query answers structural questions (callers, callees, blast-radius,
exists, symbols-in-file, search, defs); skyhook mcp serves the same as
read-only MCP tools for any agent/editor.
skyhook route takes a task, issue, review note, or bug report and builds a smaller route pack from the map. Route packs are shaped by profile:
product_planningrequirements_planningtechnical_breakdownimplementationcode_reviewbug_hunt
The route pack is the part an agent should read for a specific job. The map is the durable repo orientation.
Why It Works This Way
Skyhook favors generated files over a service because agents and humans can inspect the output, commit it if they want, diff it in review, and use it on local machines or CI runners without extra infrastructure.
The map is navigational by design — good enough to point to the right code and
docs quickly. The AST graph underneath it adds the precise layer: real
symbols (functions, classes, methods with scope), imports, member calls with
their receivers, and package declarations, parsed with tree-sitter (Python,
Swift, Kotlin, Java, JavaScript, TypeScript, Go, Elixir). Calls resolve through
a staged ladder — same file, qualifier (Foo.bar() → bar inside Foo),
imports, same package, then a repo-wide name match as the last resort — and
every edge carries its resolution grade, so consumers can tell a
scope-resolved binding from a heuristic guess instead of treating everything as
approximate. The graph is what lets skyhook route embed call chains and blast
radius so agents stop guessing.
Upgrading from an older Skyhook: the graph schema changed — run
skyhook graph build once per repo to regenerate graph.db/graph.json
(kotlin member calls need tree-sitter-kotlin>=1.1).
Model usage is optional. With an OpenAI-compatible provider, Skyhook can produce better summaries. Without a key, static mode still produces deterministic output from filenames, docs, symbols, imports, tests, and repository structure.
Install
From a checkout:
python3 -m pip install -e .
From another repository or a remote runner:
python3 -m pip install "git+https://github.com/KalebKE/Skyhook.git"
Run without installing:
python3 -m skyhook --help
First Run
From the repository you want to map:
skyhook init
Use deterministic offline mode:
skyhook init --provider static
Use a model when an API key is available:
OPENAI_API_KEY=... skyhook init --provider openai
After this, point agents at .skyhook/INDEX.md first. For task-specific work, use skyhook route.
Wire It Into Your Agent
Connecting the tools is not enough on its own — the agent also has to be told to reach for
them, in context it actually reads, or it falls back to grep. skyhook wire does both: it
registers the MCP server and writes the query-first protocol into your agent's always-on
context. It detects Claude Code, Codex, and Cursor, shows exactly what it will change, and
asks before writing:
skyhook wire # detect agents, confirm, write (project scope)
skyhook wire --dry-run # preview only, write nothing
skyhook wire --agent cursor --yes # one agent, no prompt
Writes are idempotent (re-run to update) and never touch content outside their own markers:
- Claude Code —
skyhookin the repo's.mcp.json(withalwaysLoad) + a marked block inCLAUDE.md. - Cursor —
skyhookin.cursor/mcp.json+ a.cursor/rules/skyhook.mdcrule. - Codex — a per-repo
[mcp_servers.skyhook-<repo>]in the global~/.codex/config.toml(the one global write, flagged before it happens) + a marked block in the repo'sAGENTS.md.
Restart your agent session afterward so it picks up the new config.
Configuration
Optional config lives at .skyhook/config.yaml.
version: 1
outputDir: .skyhook
model:
provider: auto
model: auto
scan:
include:
- .
exclude:
- build
- dist
- node_modules
- .git
- .gradle
- .sim-worktrees
maxFiles: 5000
docs:
extraGlobs:
- "docs/**/*.md"
- "adr/**/*.md"
- "architecture/**/*.md"
- "**/*ADR*.md"
- "**/*C4*.md"
The YAML parser intentionally supports a small subset. Skyhook does not require PyYAML.
Use include to keep the scan focused in monorepos. Use exclude for generated output, dependency directories, build artifacts, and large local worktrees.
Model Provider
Skyhook supports an OpenAI-compatible chat completions endpoint through the Python standard library.
Environment variables:
OPENAI_API_KEYorSKYHOOK_API_KEYOPENAI_BASE_URLorSKYHOOK_BASE_URLSKYHOOK_MODEL
Default model: gpt-4.1-mini.
If no API key is available and provider is auto, Skyhook uses static mode.
Tools built on Skyhook can reuse the same provider plumbing through the public
helper skyhook.model.chat_json(config, messages, provider_override=None).
It returns a parsed JSON object, or None when the resolved provider is
static so callers can fall back to deterministic behavior. Provider resolution
is exposed as skyhook.model.resolve_provider(config, provider_override=None).
Commands
skyhook init
Generate or refresh the map:
skyhook init
Preview whether output would change:
skyhook init --provider static --dry-run
skyhook route
Route a task through the map:
skyhook route --task "add retry handling to sync failures"
Use a task file:
skyhook route --profile implementation --task-file issue.md
Use a profile that matches the work:
skyhook route --profile technical_breakdown --task-file issue.md
skyhook route --profile code_review --task-file pr-notes.md
skyhook route --profile bug_hunt --task "diagnose empty dashboard cards"
Emit JSON for a harness:
skyhook route --task-file issue.md --format json
Persist the route under .skyhook/routes/:
skyhook route --task-file issue.md --save
skyhook graph
Build and query the AST symbol + call graph. skyhook init builds it
automatically; skyhook graph build rebuilds it on its own (incremental by
default, --full to rebuild from scratch).
skyhook graph build
skyhook graph query callers BillingService # who calls it
skyhook graph query callees BillingService # what it calls
skyhook graph query blast-radius src/billing.py # what an edit impacts
skyhook graph query exists src/billing.py # does this path exist?
skyhook graph query symbols-in-file src/billing.py
skyhook graph query search Billing
skyhook graph stats
Add --json for a harness. Every call edge carries a resolution grade
(same_file/qualified/imported/same_package are precise; global and
candidate are heuristic and keep the approximate flag). skyhook graph stats breaks resolution down by stage.
skyhook mcp
Serve the graph as read-only MCP tools for any MCP client. Requires the mcp
extra (pip install 'skyhook[mcp]', Python >= 3.10).
route— task → orientation pack (where to start, likely edit targets, tests, call chains, blast radius). Call this first for a coding task, instead of grepping to find where to work.find_symbol,search,symbols_in_file— locate definitions.callers_of,callees_of,blast_radius— trace structure and impact.file_exists,graph_stats— coverage checks.
skyhook mcp --repo .
skyhook init writes a ready-to-use registration to .skyhook/mcp.json. Point
your client at it (Claude Code: claude --mcp-config .skyhook/mcp.json):
{ "mcpServers": { "skyhook": { "command": "skyhook", "args": ["mcp", "--repo", "/abs/path/to/repo"], "alwaysLoad": true } } }
Keep alwaysLoad: true. It is not cosmetic: without it, clients start the
session before the stdio server finishes connecting, so Skyhook's tools never
enter the agent's toolset — the server stays pending and the agent silently
falls back to grep. With it, the client waits for the connection and every tool
is present from the first turn.
skyhook bench
Estimate the context reduction of a graph route pack versus reading the files an agent would otherwise open:
skyhook bench --task "fix retry handling in BillingService"
This measures the artifact — how much smaller the pack is than the files it points at — and assumes the agent uses the pack instead of grep-exploring. It is not a measurement of what an agent actually does. For that, and for an honest account of what Skyhook does and does not change (efficiency and consistency, not correctness or a headline token multiplier), see docs/benchmarking.md.
skyhook check
Use this when .skyhook/ artifacts are committed and you want CI to catch stale
maps and stale graphs (graph.json):
skyhook check
check fails when .skyhook/map.json does not match the current repository scan digest.
When To Rerun
Run skyhook init:
- when adding Skyhook to a repository
- after large directory moves or module splits
- after adding or removing major entrypoints
- after changing important architecture, ADR, design, or testing docs
- before handing a stale repository to a new agent session
- before opening a PR if
.skyhook/artifacts are part of the repo contract
Run skyhook route for each meaningful task. A route is task-specific and should not be treated as a permanent repo map.
Run skyhook check in CI only if you commit .skyhook/ artifacts and want freshness enforced.
CI Example
This example assumes the repository commits .skyhook/ artifacts and wants pull requests to fail when the map is stale.
name: skyhook
on:
pull_request:
jobs:
check-map:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: python3 -m pip install "git+https://github.com/KalebKE/Skyhook.git"
- run: skyhook check
Relationship To BurnPlan
Skyhook answers: where should an agent start reading for this repo or task?
BurnPlan answers: what does the project want to become, what weak points are emerging, and how should agent teams use the map over time?
Use Skyhook by itself when you only need wayfinding. Add BurnPlan when you want onboarding interviews, code health notes, documentation proposals, and team routing.
Development
python3 -m unittest
python3 -m skyhook init --provider static --dry-run
python3 -m skyhook route --profile implementation --task "add retry handling to sync failures"
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 skyhook_graph-0.4.0.tar.gz.
File metadata
- Download URL: skyhook_graph-0.4.0.tar.gz
- Upload date:
- Size: 76.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a5463ac4ef2315f9e77d00854f5ff009775d9d92e5eaa94aaf01ddb9a88f8fd
|
|
| MD5 |
6ab6373efd30478fdc080d753bc4c883
|
|
| BLAKE2b-256 |
3de714806049a3d8f03c05d488e38c786e5aa44aec2b2d28fc7538e232038e9a
|
File details
Details for the file skyhook_graph-0.4.0-py3-none-any.whl.
File metadata
- Download URL: skyhook_graph-0.4.0-py3-none-any.whl
- Upload date:
- Size: 70.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79c93e60f297925f91cacc59660624feb0d40760fa21d13e42f8e0cf870b6b5e
|
|
| MD5 |
6d1dd5fb64de260e56423c6776ecd21b
|
|
| BLAKE2b-256 |
9a4a198667fa856cd104fd79c5078e329455548e64f2a70f0099ebaf9101c4b2
|