Skip to main content

Lightweight agent-orientation maps and AST code graphs for code and documentation.

Project description

Skyhook tractor

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 by skyhook graph and skyhook mcp (regenerable; gitignored)
  • .skyhook/mcp.json: ready-to-use MCP registration (with alwaysLoad) so a coding agent can call the graph tools — see skyhook 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_planning
  • requirements_planning
  • technical_breakdown
  • implementation
  • code_review
  • bug_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 Codeskyhook in the repo's .mcp.json (with alwaysLoad) + a marked block in CLAUDE.md.
  • Cursorskyhook in .cursor/mcp.json + a .cursor/rules/skyhook.mdc rule.
  • 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's AGENTS.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_KEY or SKYHOOK_API_KEY
  • OPENAI_BASE_URL or SKYHOOK_BASE_URL
  • SKYHOOK_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

skyhook_graph-0.4.0.tar.gz (76.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

skyhook_graph-0.4.0-py3-none-any.whl (70.0 kB view details)

Uploaded Python 3

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

Hashes for skyhook_graph-0.4.0.tar.gz
Algorithm Hash digest
SHA256 9a5463ac4ef2315f9e77d00854f5ff009775d9d92e5eaa94aaf01ddb9a88f8fd
MD5 6ab6373efd30478fdc080d753bc4c883
BLAKE2b-256 3de714806049a3d8f03c05d488e38c786e5aa44aec2b2d28fc7538e232038e9a

See more details on using hashes here.

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

Hashes for skyhook_graph-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 79c93e60f297925f91cacc59660624feb0d40760fa21d13e42f8e0cf870b6b5e
MD5 6d1dd5fb64de260e56423c6776ecd21b
BLAKE2b-256 9a4a198667fa856cd104fd79c5078e329455548e64f2a70f0099ebaf9101c4b2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page