axon

package module
v0.20.1 Latest Latest
Warning

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

Go to latest
Published: Apr 11, 2026 License: MIT Imports: 25 Imported by: 0

README

Go Reference Go Report Card CI Latest Release

AI-native graph database and indexing system — built for agents, runs locally.


  • 🗄️ Graph storage — typed nodes and edges with JSON data fields, labels, and full relationship tracking across sessions
  • 🔍 AQL — Axon Query Language: SQL-style table queries, Cypher-inspired pattern matching, and variable-length path traversal, all in one unified syntax
  • 📁 Universal indexing — filesystem, Git repositories, Markdown documents, and Go source code out of the box
  • 🧠 Context generationaxon context finds the most relevant code for a task and fits it into an AI token budget automatically
  • 🔎 Semantic search — vector similarity search via Ollama or Hugot, fully local with no external APIs
  • 📡 Watch mode — live re-indexing on file changes with axon index --watch
  • 🔒 Local-first — everything stored in a single SQLite file; no cloud, no data transmission

Contents


🤖 For AI Agents

Axon is built to be used directly by AI agents as a persistent knowledge tool. Copy and paste the prompt below into Claude, opencode, Cursor, or any agent to get started:


You have access to axon — a graph-based knowledge CLI for AI agents that indexes your codebase into a queryable graph.

Step 1 — Install (requires Go 1.25+):

go install github.com/codewandler/axon/cmd/axon@latest

Step 2 — Build your skill. Read and internalise the following files from this repository:

  • README.md — complete CLI and AQL reference
  • .agents/skills/axon/SKILL.md — compact command cheatsheet
  • .agents/skills/axon/references/aql.md — AQL syntax reference
  • .agents/skills/axon/references/aql_go_querybuilder.md — Go builder API (only if working in Go)

Step 3 — Index the project you're working on:

axon index .

You can now query the graph with axon query, explore with axon tree, search with axon find, and generate context for tasks with axon context.


opencode users: the skill at .agents/skills/axon/ is compatible with opencode and can be installed directly from this repository.

Installation

go install github.com/codewandler/axon/cmd/axon@latest

Requires Go 1.25 or later.

Quickstart

Initialize and index a directory:

# Index current directory (creates .axon/graph.db in the current directory)
axon index .

# Check what was indexed
axon tree

# Query for Go files
axon query "SELECT * FROM nodes WHERE type = 'fs:file' AND data.ext = 'go'"

Basic CLI commands:

  • axon index [path] - Index a directory (alias: axon init)
  • axon index --embed [path] - Index and generate embeddings for semantic search
  • axon index --watch [path] - Watch for changes and keep graph up to date
  • axon query "<aql>" - Execute AQL queries
  • axon tree [path] - Display graph as tree
  • axon find - Search nodes with flags (--type, --name, --ext, …)
  • axon find "<query>" - Semantic similarity search (requires --embed)
  • axon show <node-id> - Show node details
  • axon neighbors <uri> - Show immediate neighbors via edges (direction, edge-type filter)
  • axon impact <symbol> - Show blast radius of changing a symbol
  • axon context --task "<description>" - Generate AI-optimised context for a task
  • axon info - Database status and statistics dashboard
  • axon stats - Database statistics

AQL: Axon Query Language

AQL is a SQL-like query language with graph pattern matching. It supports both flat table queries and relationship traversal.

Basic Table Queries

Query nodes and edges like traditional database tables:

-- All files
SELECT * FROM nodes WHERE type = 'fs:file'

-- Go files larger than 1KB
SELECT * FROM nodes 
WHERE type = 'fs:file' 
  AND data.ext = 'go' 
  AND data.size > 1000

-- Count nodes by type
SELECT type, COUNT(*) FROM nodes GROUP BY type

JSON Field Access: Use dot notation to query nested data:

SELECT * FROM nodes WHERE data.ext = 'go'
SELECT * FROM nodes WHERE data.size BETWEEN 100 AND 1000
SELECT * FROM nodes WHERE data.mode = 755

Operators: =, !=, <, >, <=, >=, LIKE, GLOB, IN, BETWEEN, IS NULL

Label Operations:

SELECT * FROM nodes WHERE labels CONTAINS ANY ('important', 'reviewed')
SELECT * FROM nodes WHERE labels CONTAINS ALL ('test', 'verified')
SELECT * FROM nodes WHERE labels NOT CONTAINS ('archived')

Pattern Matching

Query the graph using Cypher-inspired patterns:

-- Files in directories
SELECT file FROM (dir:fs:dir)-[:contains]->(file:fs:file)

-- Go files in specific directory
SELECT file 
FROM (dir:fs:dir)-[:contains]->(file:fs:file)
WHERE dir.name = 'cmd' AND file.data.ext = 'go'

-- Branches in repositories
SELECT branch 
FROM (repo:vcs:repo)-[:has]->(branch:vcs:branch)
WHERE repo.name = 'myproject'

Pattern Syntax:

  • (var:type) - Node with variable and type
  • -> - Outgoing edge
  • <- - Incoming edge
  • - - Undirected (either direction)
  • [var:type] - Edge with variable binding

Multi-Type Edges (OR logic):

-- Match contains OR has edges
SELECT child FROM (parent)-[:contains|has]->(child)

-- Match any of three edge types
SELECT n FROM (root)-[:contains|has|references]->(n)

Multiple Patterns (implicit JOIN):

-- Files in repos located in specific dirs
SELECT file
FROM (repo:vcs:repo)-[:located_at]->(dir:fs:dir),
     (dir)-[:contains]->(file:fs:file)
WHERE repo.name = 'myrepo' AND file.data.ext = 'go'

Variable-Length Paths

Traverse relationships recursively:

-- All descendants (1 or more hops)
SELECT desc FROM (root:fs:dir)-[:contains*]->(desc)

-- 1 to 3 hops deep
SELECT child FROM (parent:fs:dir)-[:contains*1..3]->(child)

-- Exactly 2 hops
SELECT node FROM (start)-[:contains*2]->(node)

-- At least 2 hops (unbounded)
SELECT desc FROM (root)-[:contains*2..]->(desc)

-- Multi-type recursive traversal
SELECT node FROM (root)-[:contains|has*1..5]->(node)

Aggregation and Sorting

-- Count files per directory
SELECT dir.name, COUNT(*)
FROM (dir:fs:dir)-[:contains]->(file:fs:file)
GROUP BY dir.name
ORDER BY COUNT(*) DESC

-- Directories with many files
SELECT dir.name, COUNT(*)
FROM (dir:fs:dir)-[:contains]->(file:fs:file)
GROUP BY dir.name
HAVING COUNT(*) > 10

-- Top 10 largest files
SELECT name, data.size 
FROM nodes 
WHERE type = 'fs:file'
ORDER BY data.size DESC
LIMIT 10

Existence Checks

Test for pattern existence without returning matches:

-- Directories containing Go files
SELECT dir
FROM (dir:fs:dir)
WHERE EXISTS (dir)-[:contains]->(:fs:file WHERE data.ext = 'go')

-- Repos with no branches
SELECT repo
FROM (repo:vcs:repo)
WHERE NOT EXISTS (repo)-[:has]->(:vcs:branch)

Advanced Patterns

Edge Variables:

-- Examine edge properties
SELECT e.type, from.name, to.name
FROM (from)-[e:contains]->(to)
WHERE from.type = 'fs:dir'

Inline WHERE Clauses:

-- Filter inside patterns
SELECT file
FROM (dir:fs:dir WHERE dir.name = 'src')
     -[:contains]->
     (file:fs:file WHERE file.data.ext = 'go')

Complex Boolean Logic:

SELECT * FROM nodes
WHERE (type = 'fs:file' OR type = 'fs:dir')
  AND labels CONTAINS ANY ('important', 'reviewed')
  AND labels NOT CONTAINS ('archived')
  AND (data.size > 1000 OR data.size IS NULL)

CLI Reference

Global Flags

  • --db-dir <path> - Use a specific database directory
  • --global - Walk up from CWD to find an existing .axon/graph.db, then fall back to ~/.axon/graph.db

Database Resolution: By default, axon uses <cwd>/.axon/graph.db — no directory traversal. Pass --global to search parent directories and fall back to ~/.axon/graph.db if nothing is found locally.

axon index

Index a directory and create the graph (alias: axon init):

axon index .                    # Index current dir → creates .axon/graph.db here
axon index --no-gc /path/to/dir # Skip garbage collection
axon index --embed .            # Index + generate embeddings for semantic search

What gets indexed:

  • Filesystem structure (files, directories)
  • Git repositories (repos, branches, tags, commits)
  • Markdown documents (structure, sections, links)
  • Go modules and packages (structs, interfaces, funcs, imports, implementations)

axon query

Execute AQL queries:

# Basic query
axon query "SELECT * FROM nodes WHERE type = 'fs:file'"

# With output format
axon query --output json "SELECT * FROM nodes LIMIT 10"
axon query --output table "SELECT type, COUNT(*) FROM nodes GROUP BY type"
axon query --output count "SELECT * FROM nodes"

# See query execution plan
axon query --explain "SELECT file FROM (dir)-[:contains]->(file)"

axon tree

Display the graph as a tree structure:

axon tree                      # Current directory subtree (depth 3, IDs + types shown by default)
axon tree /path/to/dir         # Specific path
axon tree nI3NDos              # Subtree rooted at node by ID prefix
axon tree --depth 2            # Limit depth (0 = unlimited; default 3)
axon tree --type fs:file       # Filter by node type (glob: 'fs:*', 'md:*')
axon tree --no-color           # Disable colored output
axon tree --no-emoji           # Disable emoji icons

axon find

Search nodes with flags, or pass a text argument for semantic similarity search (requires embeddings — run axon index --embed first):

# Semantic search
axon find "error handling"
axon find "concurrency and goroutines" --type go:func
axon find "recent logo commits"        --type vcs:commit --limit 5
axon find "storage interface design"   --type go:interface --global
axon find "error handling"             --output json
axon find "caching todos"              --type code:todo   # search annotation comments

# Flag-only (unchanged)
axon find --type fs:file               # All files
axon find --name "main.go"             # Exact name match
axon find --ext go                     # By extension (repeatable: --ext go --ext py)
axon find --query "README*"            # Name wildcard pattern
axon find --label important            # By label (repeatable, OR logic)
axon find --data key=value             # Match on a data field
axon find --global                     # Search entire graph, not just CWD subtree
axon find --type vcs:branch --count    # Just show the count
axon find --output json                # Output format: path, uri, json, table
axon find --show-parent                # Show parent chain to CWD or root
axon find --show-query                 # Print the generated AQL query
axon find --limit 20                   # Limit number of results
axon find --exclude-type vcs:commit    # Exclude a node type (repeatable)
axon find --exclude-type vcs:commit --exclude-type fs:dir  # Exclude multiple types

axon show

Display detailed node information:

axon show <node-id>            # Show node details

axon neighbors

Return the immediate neighbors of a node — all nodes connected by a single edge. Useful for answering "what calls this?", "what implements this?", "what does this depend on?":

# All neighbors (both directions, all edge types)
axon neighbors Storage

# Who implements this interface? (incoming edges only)
axon neighbors Storage --direction in --edge-type implements

# What does this struct depend on? (outgoing edges only)
axon neighbors Server --direction out

# Resolve by URI
axon neighbors "go:func:github.com/codewandler/axon.New" --direction in

# Structured output for scripting
axon neighbors Storage --output json
axon neighbors Storage --output table

# Limit results
axon neighbors Storage --max 10

Output (text):

3 neighbor(s) of "Storage":

  <- implements  [abc1234] SQLiteStorage  (go:struct)
  -> defines     [def5678] PutNode        (go:func)
  -> defines     [ghi9012] GetNode        (go:func)

Flags:

  • --direction in|out|both — edge direction (default: both)
  • --edge-type <type> — restrict to edge type; repeatable
  • --max <n> — maximum results (default: 50; 0 = unlimited)
  • --output text|table|json — output format (default: text)

axon context

Generate AI-optimised context for a task description — finds relevant definitions, dependencies, callers, and related symbols, then fits them within a token budget:

axon context --task "add caching to Storage interface"
axon context --task "refactor Query method" --tokens 8000
axon context --task "fix NewNode" --output json
axon context --task "explain Indexer" --no-source   # manifest only, no source
axon context --task "improve performance" --symbols Storage --symbols Query
echo "add error handling to Flush" | axon context   # task from stdin

axon describe

Show the schema of the graph — all node types with counts, edge types with from/to connection patterns, and (with --fields) the JSON data field names stored on each node type. Useful for discovering what types and fields exist before writing AQL queries or using axon find --type.

axon describe              # schema overview (text)
axon describe -o json      # machine-readable JSON
axon describe --fields     # include data field names per node type

axon info

Show a dashboard of database status, location, statistics, and last index details:

axon info
axon info -o json

Other Commands

axon stats                     # Database statistics
axon labels                    # List all labels with counts
axon types                     # List all node types with counts
axon edges                     # List all edge types with counts
axon gc                        # Remove expired nodes/edges + orphaned edges (verbose)
axon gc --dry-run              # Preview what would be cleaned without making changes
axon gc --dry-run -q           # Dry-run quiet mode: summary count only
axon write-node --uri <uri> --type <type> [--name <name>] [--data <json>] [--ttl <dur>]
                               # Persist a custom node (same URI = upsert; --ttl = ephemeral)

Watch Mode

Keep the graph up to date as files change:

axon index --watch .                   # Watch current directory
axon index --watch ./src               # Watch specific subtree
axon index --watch --watch-quiet .     # Suppress per-change output
axon index --watch --watch-debounce 300ms .  # Custom debounce duration
axon index --watch --embed .            # Watch + re-embed on each change

On each file change, axon re-indexes the affected directory and prints:

↻  Re-indexed ./pkg/util — 12 files, 3 dirs (done)

Impact Analysis

Understand the blast radius of changing a symbol:

axon impact Storage            # Show what depends on Storage
axon impact NewNode            # Show callers and importers
axon impact IndexResult        # Find all usages

Output:

Impact analysis: Storage (go:interface)

Direct references (17):
  adapters/sqlite               12 refs  [call, field, type]
  cmd/axon                       2 refs  [call]
  context                        3 refs  [type]

Packages importing affected packages:
  axon                  imports adapters/sqlite
  cmd/axon              imports sqlite, graph

axon search (deprecated)

axon search is deprecated — use axon find "<query>" instead.

Semantic search is now built directly into axon find. Any positional text argument triggers vector similarity search:

# Before (deprecated)
axon search --semantic "handles token budget overflow"
axon search --semantic "error recovery" --type go:func

# After
axon find "handles token budget overflow"
axon find "error recovery" --type go:func

Embeddings

# First generate embeddings during indexing
axon index --embed .                              # uses Ollama by default
axon index --embed --embed-provider=hugot .       # in-process, no daemon needed

# Then search semantically via axon find
axon find "handles token budget overflow"
axon find "error recovery" --type go:func
axon find "storage interface" --limit 5
Provider: Ollama (default)

Requires the Ollama daemon running locally.

ollama pull nomic-embed-text
axon index --embed .
Provider: Hugot (in-process, no daemon)

Runs ONNX sentence-embedding models fully inside the axon process. No external service needed. Model is downloaded once (~90 MB) and cached.

# Hugot provider — downloads model on first run, then cached at ~/.axon/models/
axon index --embed --embed-provider=hugot .

# Custom model directory
axon index --embed --embed-provider=hugot --embed-model-path=/data/models/MiniLM .

# Via environment variable
AXON_EMBED_PROVIDER=hugot axon index --embed .
Hugot Ollama
External daemon ❌ none ✅ required
CGO / shared libs ❌ none ❌ none
First-run setup ~90 MB model download ollama pull <model>
Throughput — single embed ~114 ms (CPU, pure Go) ~23 ms (GPU via HTTP)
Throughput — batched (32 nodes) ~21 ms/node ~12 ms/node
Best for offline / CI / Docker existing Ollama users

Environment variables:

  • AXON_EMBED_PROVIDER — provider name: ollama (default) or hugot
  • AXON_OLLAMA_URL — Ollama base URL (default: http://localhost:11434)
  • AXON_OLLAMA_MODEL — Ollama model name (default: nomic-embed-text)
  • AXON_HUGOT_MODEL — HuggingFace repo slug (default: KnightsAnalytics/all-MiniLM-L6-v2)
  • AXON_HUGOT_MODEL_PATH — local model directory (default: ~/.axon/models/<model>)

Go Library

Axon is available as an embeddable Go library.

go get github.com/codewandler/axon

Index and Query

axon.New returns an *Axon that handles both indexing and querying:

import (
    "context"
    "fmt"
    "log"

    axon "github.com/codewandler/axon"
    "github.com/codewandler/axon/aql"
    "github.com/codewandler/axon/graph"
)

func main() {
    ax, err := axon.New(axon.Config{Dir: "."})
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()
    if _, err := ax.Index(ctx, "."); err != nil {
        log.Fatal(err)
    }

    // AQL string — parse + execute in one call
    result, err := ax.QueryString(ctx, `SELECT type, COUNT(*) FROM nodes GROUP BY type ORDER BY COUNT(*) DESC`)
    if err != nil {
        log.Fatal(err)
    }
    for _, c := range result.Counts {
        fmt.Printf("%s: %d\n", c.Name, c.Count)
    }

    // Builder API — type-safe, no strings
    q := aql.Nodes.Select(aql.Type, aql.Count()).
        Where(aql.Type.Glob("go:*")).
        GroupBy(aql.Type).
        OrderByCount(true).
        Build()
    result, err = ax.Query(ctx, q)

    // Structural filter
    nodes, err := ax.Find(ctx, graph.NodeFilter{Type: "go:func"}, graph.QueryOptions{Limit: 20})
    for _, n := range nodes {
        fmt.Println(n.Name)
    }
}

Writing Nodes

Use WriteNode to persist a custom node, flush it to storage, and automatically embed it (if an EmbeddingProvider is configured) — all in one call:

node := graph.NewNode("memory:decision").
    WithName("Use PostgreSQL for user data").
    WithURI("memory:decision:db-choice").       // same URI = upsert
    WithData(map[string]any{"reason": "JSONB support and familiarity"}).
    WithLabels("architecture", "reviewed")

if err := ax.WriteNode(ctx, node); err != nil {
    log.Fatal(err)
}

// Node is immediately queryable and searchable:
nodes, _ := ax.Find(ctx, graph.NodeFilter{Type: "memory:decision"}, graph.QueryOptions{})
got, _   := ax.GetNodeByURI(ctx, "memory:decision:db-choice")
Ephemeral nodes with TTL

Call WithTTL to give a node a time-to-live. After the duration elapses the node is treated as non-existent by all read paths (queries, AQL, semantic search). Run axon gc to physically remove expired rows.

// Automatically invisible after 2 hours:
node := graph.NewNode("memory:note").
    WithName("Current focus").
    WithURI("memory://session-abc/current-task").
    WithData(map[string]any{"text": "investigating auth bug"}).
    WithTTL(2 * time.Hour)

if err := ax.WriteNode(ctx, node); err != nil {
    log.Fatal(err)
}

// Renew by writing the same URI with a new TTL:
node.WithTTL(2 * time.Hour)       // resets ExpiresAt
ax.WriteNode(ctx, node)            // upserts

Edges can carry a TTL too:

edge := graph.NewEdge("references", noteID, fileID).WithTTL(30 * time.Minute)
_ = ax.storage.PutEdge(ctx, edge)
axon write-node CLI

The write-node command lets you persist a custom node from the shell (or from a CI script) without writing Go code:

# Immortal node:
axon write-node --uri memory://s1/task --type memory:note --name "current task"

# Ephemeral node — expires in 2 hours:
axon write-node \
  --uri  memory://session-abc/current-task \
  --type memory:note \
  --name "Current focus" \
  --data '{"text":"investigating auth bug"}' \
  --ttl  2h

# After nodes expire, clean up physically:
axon gc

For bulk writes where you want to control the flush boundary yourself, use PutNode + Flush:

for _, n := range batch {
    _ = ax.PutNode(ctx, n)
}
_ = ax.Flush(ctx)

Pass an EmbeddingProvider in Config to enable semantic search. WriteNode will automatically embed custom nodes so they appear in search results immediately:

import "github.com/codewandler/axon/indexer/embeddings"

ax, _ := axon.New(axon.Config{
    Dir:               ".",
    EmbeddingProvider: embeddings.NewOllama("", "", 0),
})
ax.Index(ctx, ".")

// Search with options— MinScore trims noise without a manual post-filter loop.
results, err := ax.Search(ctx, []string{"authentication logic"}, axon.SearchOptions{
    Limit:    10,
    MinScore: 0.5,
})

// Scope to a type (applies the correct URI scheme automatically).
results, err = ax.Search(ctx, []string{"token budget"}, axon.SearchOptions{
    Limit:  5,
    Filter: &graph.NodeFilter{
        Type:      "go:func",
        URIPrefix: types.URIPrefixForType("go:func"), // infers CWD
    },
})

URI Prefix Scoping

types.URIPrefixForType maps a node type to the correct URI scheme for local scoping. Each indexer uses a different scheme, so the prefix must match:

import "github.com/codewandler/axon/types"

// Explicit directory
prefix := types.URIPrefixForType("go:func", "/home/user/myrepo")
// go+file:///home/user/myrepo

// Infer from current working directory
prefix = types.URIPrefixForType("vcs:commit")
// git+file:///current/working/dir

// Scheme mapping:
// go:*   → go+file://
// vcs:*  → git+file://
// md:*   → file+md://
// fs:*, * → file://

The Querier Interface

*Axon satisfies axon.Querier, so you can depend on the interface in your own code for easier testing and decoupling:

func printNodeTypes(ctx context.Context, q axon.Querier) error {
    result, err := q.QueryString(ctx, "SELECT type, COUNT(*) FROM nodes GROUP BY type")
    if err != nil {
        return err
    }
    for _, c := range result.Counts {
        fmt.Printf("%s: %d\n", c.Name, c.Count)
    }
    return nil
}

printNodeTypes(ctx, ax)

Querier exposes:

Method Description
Query(ctx, *aql.Query) Execute a pre-built AQL query (from the builder or aql.Parse)
QueryString(ctx, string) Parse an AQL string and execute it in one call
Explain(ctx, *aql.Query) Return the execution plan without running the query
Find(ctx, NodeFilter, QueryOptions) Structural node filter search
Search(ctx, queries, SearchOptions) Semantic similarity search (requires embedding provider)

Watch Mode

Keep the graph live with (*Axon).Watch:

err := ax.Watch(ctx, ".", axon.WatchOptions{
    OnReady: func(result *axon.IndexResult, err error) {
        fmt.Printf("ready: %d files\n", result.Files)
    },
    OnReindex: func(path string, result *axon.IndexResult, err error) {
        fmt.Printf("re-indexed %s\n", path)
    },
})

Node Types

Axon uses typed nodes with domain:name format:

Filesystem

  • fs:file - File node
    • Data: ext (extension), size (bytes), mode (permissions)
  • fs:dir - Directory node

Version Control

  • vcs:repo - Git repository
  • vcs:remote - Git remote
  • vcs:branch - Branch
  • vcs:tag - Tag
  • vcs:commit - Commit
    • Data: sha, message, body, author_name, author_email, author_date, files_changed, insertions, deletions, parents
    • Conventional Commits fields (when the message follows the spec): commit_type (feat/fix/chore/…), scope, breaking (bool), subject, footers, refs

Documents

  • md:document - Markdown document
  • md:section - Document section (heading-based; data.level = 1–6)
  • md:codeblock - Fenced code block
  • md:link - External link
  • md:image - Image reference

Go Code

  • go:module - Go module (go.mod root)
  • go:package - Go package
  • go:func - Function or method
  • go:method - Method on a type
  • go:struct - Struct type
  • go:field - Struct field
  • go:interface - Interface type
  • go:const - Constant
  • go:var - Package-level variable
  • go:ref - Symbol reference (call site, type usage, etc.)
    • Data: caller_uri, caller_name, caller_type — enclosing function/method for call-graph queries

Project

  • project:root - Project root detected from a manifest file (go.mod, package.json, Cargo.toml, etc.)

    • Data: type (language: go, node, rust, python, java, ruby, php), name, version, dep_count
  • project:license - Software licence detected from a LICENSE / LICENCE / COPYING file

    • Data: spdx_id (SPDX identifier, e.g. MIT, Apache-2.0; empty string if unrecognised), confidence (high for a known licence, unknown if the file was detected but not identified), file (absolute path to the licence file)
    • Edge: project:root -[has]-> project:license (falls back to fs:dir -[has]-> project:license when no manifest is present)
    • URI: license+file:///abs/path/LICENSE
    • Examples:
      axon find --type project:license              # list all detected licences
      axon query "SELECT name, data.spdx_id FROM nodes WHERE type = 'project:license'"
      

Annotations

  • code:todo - TODO/FIXME/HACK/XXX/NOTE annotation comment in any source file
    • Data: kind (TODO/FIXME/HACK/XXX/NOTE), text (annotation text), file (absolute path), line (1-based), context (preceding source line for orientation)
    • Labels: lowercase kind (todo, fixme, hack, xxx, note)
    • URI: file+todo:///abs/path/file.go#L42
    • Examples:
      axon find --type code:todo --global              # all annotations
      axon find --type code:todo --label fixme         # only FIXMEs
      axon find "error handling" --type code:todo      # semantic search over annotation text
      axon query "SELECT data.kind, COUNT(*) FROM nodes WHERE type = 'code:todo' GROUP BY data.kind"
      

Edge Types

Common edge types follow generic semantics:

  • contains / contained_by - Structural containment (dir → file)
  • has / belongs_to - Logical ownership (repo → branch)
  • located_at - Physical location (repo → dir)
  • references - Soft cross-reference
  • links_to - Explicit hyperlink
  • depends_on - Dependency relationship
  • imports - Import statement (go:package → go:package)
  • implements - Struct implements interface (go:struct → go:interface)
  • tests - Test package tests source package (go:package → go:package)
  • defines - Package defines symbol (go:package → go:func/struct/etc.)
  • calls - Function/method calls another (go:func/go:method → go:func/go:method); deduplicated per call-site
  • embeds - Struct anonymously embeds another struct (go:struct → go:struct)
  • parent_of - Commit DAG parent-to-child (vcs:commit → vcs:commit)
  • modifies - Commit modified a file (vcs:commit → fs:file)

Architecture

Axon consists of:

  • Graph Core (graph/) - Node, Edge, Storage interface
  • SQLite Adapter (adapters/sqlite/) - Persistent storage with buffered writes
  • AQL Engine (aql/) - Parser, AST, query compiler
  • Indexers (indexer/) - Pluggable indexers for different data sources
    • fs - Filesystem indexer
    • git - Git repository indexer (branches, tags, commits, conventional commit parsing)
    • markdown - Markdown document indexer
    • golang - Go source code indexer (symbols, call graph, embed graph)
    • project - Project manifest indexer (go.mod, package.json, …)
    • tagger - Rule-based label tagger (applies labels by name/path pattern)
    • todo - Language-agnostic TODO/FIXME/HACK/XXX/NOTE annotation indexer
    • embeddings - Embedding providers (Ollama, Hugot, null)
  • Context Engine (context/) - Token-budget-aware context gathering for AI agents
  • CLI (cmd/axon/) - Command-line interface
  • TUI (cmd/axonui/) - Terminal UI explorer

Key Features:

  • Generation-based garbage collection (tracks stale nodes across index runs)
  • Event-driven cascade deletion (when files are deleted, dependent data is cleaned up)
  • Buffered writes for performance (5000 items or 100ms batches)
  • Pluggable indexer architecture (subscribe to events, handle specific URI schemes)

Use Cases

For AI Agents:

  • Persistent memory across sessions
  • Context-aware file retrieval
  • Relationship tracking (imports, dependencies, references)
  • Structured knowledge graphs from unstructured data
  • Multi-hop reasoning (variable-length paths)

For Developers:

  • Code navigation and exploration
  • Dependency analysis
  • Git history queries
  • Documentation cross-referencing
  • Project structure understanding

License

MIT

Contributing

Contributions welcome! See the codebase structure in AGENTS.md for development guidelines.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultFSIgnore = []string{

	".git",

	"node_modules",
	"__pycache__",
	"target",
	"vendor",
	"venv",
	"env",
	"dist",
	"build",
	"site-packages",

	".devspace",
	".DS_Store",

	"*.log",
}

DefaultFSIgnore contains the default patterns to exclude when indexing. Dot-prefixed paths are no longer blanket-excluded; specific entries are listed here so that useful dotfiles (.agents/, .claude/, etc.) remain visible to the graph.

View Source
var ErrNoEmbeddingProvider = errors.New("axon: no embedding provider configured; use axon init --embed to generate embeddings")

ErrNoEmbeddingProvider is returned when SemanticSearch is called but no EmbeddingProvider was configured.

Functions

This section is empty.

Types

type Axon

type Axon struct {
	// contains filtered or unexported fields
}

Axon is the main entry point for the axon library.

func New

func New(cfg Config) (*Axon, error)

New creates a new Axon instance with the given configuration.

func (*Axon) DeleteByPath added in v0.6.0

func (a *Axon) DeleteByPath(ctx context.Context, path string) error

DeleteByPath removes the graph node(s) for the given filesystem path and cleans up orphaned edges. For directory paths it removes all nodes whose URI has the directory URI as a prefix (entire subtree). Returns nil when no node exists for the path (idempotent).

func (*Axon) Describe added in v0.10.0

func (a *Axon) Describe(ctx context.Context, includeFields bool) (*graph.SchemaDescription, error)

Describe returns a schema description of the current graph: every node type with its count, every edge type with its count and the from/to node-type pairs it connects, and (when includeFields is true) the top-level JSON data field names discovered in each node type.

Field discovery samples up to 500 nodes per type so the cost is bounded even on large graphs. Pass includeFields=false when you only need type/count information and want the fastest possible response.

Describe calls Flush before querying so that any buffered writes are visible in the results.

func (*Axon) Explain added in v0.9.0

func (a *Axon) Explain(ctx context.Context, q *aql.Query) (*graph.QueryPlan, error)

Explain returns the execution plan for a pre-built AQL query.

func (*Axon) Find added in v0.9.0

func (a *Axon) Find(ctx context.Context, filter graph.NodeFilter, opts graph.QueryOptions) ([]*graph.Node, error)

Find returns nodes matching the structural filter.

func (*Axon) FindPath added in v0.15.0

func (a *Axon) FindPath(ctx context.Context, fromID, toID string, opts PathOptions) ([]*Path, error)

FindPath finds the shortest paths between two nodes in the knowledge graph.

fromID and toID are node IDs (as returned by Find, Search, or GetNodeByURI). Paths are discovered by bidirectional BFS: both outgoing and incoming edges are followed so that structural and semantic relationships are bridged.

The search is bounded by opts.MaxDepth edges (default 6) and returns at most opts.MaxPaths results (default 3), ordered by ascending length.

Returns an empty slice (no error) when no connecting path exists within the depth limit. Returns an error only when the origin node cannot be loaded or the storage layer fails.

func (*Axon) Flush added in v0.9.1

func (a *Axon) Flush(ctx context.Context) error

Flush flushes any buffered writes to the underlying storage.

func (*Axon) GetNodeByURI added in v0.9.1

func (a *Axon) GetNodeByURI(ctx context.Context, uri string) (*graph.Node, error)

GetNodeByURI returns the node with the given URI, or an error if not found.

func (*Axon) Graph

func (a *Axon) Graph() *graph.Graph

Graph returns the underlying graph.

func (*Axon) Index

func (a *Axon) Index(ctx context.Context, path string) (*IndexResult, error)

Index indexes the given path and updates the graph. If path is empty, indexes the configured directory.

func (*Axon) IndexWithOptions

func (a *Axon) IndexWithOptions(ctx context.Context, opts IndexOptions) (*IndexResult, error)

IndexWithOptions indexes with the provided options.

The library never writes to stdout or stderr unless opts.ShowProgress is true, in which case compact progress lines are written to stderr. Use opts.Progress for structured event access.

func (*Axon) IndexWithProgress

func (a *Axon) IndexWithProgress(ctx context.Context, path string, prog chan<- progress.Event) (*IndexResult, error)

IndexWithProgress indexes the given path and reports progress on the provided channel. If progress is nil, progress reporting is disabled.

func (*Axon) Neighbors added in v0.18.0

func (a *Axon) Neighbors(ctx context.Context, uri string, opts NeighborsOptions) ([]*NeighborResult, error)

Neighbors returns the immediate neighbors of the node identified by uri, following edges in the requested direction.

uri is the node URI (e.g. "file:///path/to/file.go" or "go:func:pkg.Func"). Direction defaults to "both" when opts.Direction is empty.

Results are ordered: outgoing edges first, then incoming, as returned by the storage layer (no additional sorting is applied). When opts.Max > 0 the result slice is truncated to that length after filtering.

Returns a non-nil error if the node cannot be found or the storage layer fails. An isolated node (no matching edges) returns an empty slice without an error.

func (*Axon) PutNode added in v0.9.1

func (a *Axon) PutNode(ctx context.Context, node *graph.Node) error

PutNode writes a node to the storage layer without flushing or embedding. Use WriteNode for the full write-flush-embed cycle.

func (*Axon) Query added in v0.9.0

func (a *Axon) Query(ctx context.Context, q *aql.Query) (*graph.QueryResult, error)

Query executes a pre-built AQL query against the graph.

func (*Axon) QueryString added in v0.9.0

func (a *Axon) QueryString(ctx context.Context, q string) (*graph.QueryResult, error)

QueryString parses the AQL string and executes it. Convenience wrapper around aql.Parse + Query.

func (*Axon) RegisterIndexer

func (a *Axon) RegisterIndexer(idx indexer.Indexer)

RegisterIndexer adds a custom indexer.

func (*Axon) Search added in v0.9.0

func (a *Axon) Search(ctx context.Context, queries []string, opts SearchOptions) ([]*SemanticSearchResult, error)

Search performs semantic vector similarity search with the given options. It wraps SemanticSearch and applies MinScore filtering after the search.

func (*Axon) SemanticSearch added in v0.6.0

func (a *Axon) SemanticSearch(ctx context.Context, queries []string, limit int, filter *graph.NodeFilter) ([]*SemanticSearchResult, error)

SemanticSearch embeds each query string using the configured EmbeddingProvider and runs vector similarity search for each. Results across all queries are merged and deduplicated — the best score per node wins. Returns up to limit results sorted by score descending.

Returns ErrNoEmbeddingProvider if no provider is set in Config.

func (*Axon) Watch added in v0.5.0

func (a *Axon) Watch(ctx context.Context, path string, opts WatchOptions) error

Watch watches the given path for filesystem changes and re-indexes affected files automatically. It performs an initial full index, then blocks until ctx is cancelled, re-indexing individual changed files/directories on each batch of changes after the debounce window elapses.

func (*Axon) WriteNode added in v0.9.1

func (a *Axon) WriteNode(ctx context.Context, node *graph.Node) error

WriteNode writes a node to the graph, flushes it to storage, and automatically generates and stores an embedding if an EmbeddingProvider is configured. This is the preferred way to persist custom nodes programmatically — the node will be immediately findable via Search without requiring a full re-index run.

type Config

type Config struct {
	// Dir is the working directory. Defaults to current directory.
	Dir string

	// Storage is the storage backend. Defaults to in-memory storage.
	Storage graph.Storage

	// FSExclude contains glob patterns to exclude from indexing.
	// When empty, DefaultFSIgnore is used. To clear all defaults, set FSExclude
	// to a non-nil empty slice: []string{}.
	// Patterns matched against file name and full absolute path.
	FSExclude []string

	// FSInclude contains glob patterns to include. When non-empty, only files
	// matching at least one pattern are indexed (directories always traversed).
	FSInclude []string

	// FSIgnore is a deprecated alias for FSExclude.
	// If both are set they are merged. Prefer FSExclude.
	FSIgnore []string

	// EmbeddingProvider is an optional embedding provider for semantic search.
	// When set, a PostIndexer will generate and store embeddings for Go symbols
	// and Markdown sections after each indexing run.
	// If nil (default), no embeddings are generated.
	EmbeddingProvider embeddings.Provider

	// GitConfig holds configuration for the git indexer.
	// Controls how many commits are indexed per repository (default: 500).
	GitConfig git.Config
}

Config holds configuration for an Axon instance.

type ErrNoIndexer

type ErrNoIndexer struct {
	URI string
}

ErrNoIndexer is returned when no indexer can handle a URI.

func (*ErrNoIndexer) Error

func (e *ErrNoIndexer) Error() string

type IndexOptions

type IndexOptions struct {
	// Path is the path to index. If empty, uses the configured directory.
	Path string

	// Progress is an optional channel for reporting indexing progress.
	// Events are sent as indexers start, make progress, and complete.
	// The caller owns the channel; it is never closed by the library.
	// Mutually exclusive with ShowProgress — if both are set, Progress
	// takes precedence and ShowProgress is ignored.
	Progress chan<- progress.Event

	// ShowProgress writes a compact human-readable progress log to
	// os.Stderr. Intended for programmatic callers that want feedback
	// without wiring up a full progress channel or a bubbletea UI.
	//
	// Default: false (completely silent).
	ShowProgress bool

	// SkipGC skips garbage collection (orphaned edge cleanup) after indexing.
	// This can speed up indexing when you know cleanup isn't needed,
	// or when you plan to run `axon gc` separately.
	SkipGC bool
}

IndexOptions configures the indexing behavior.

type IndexResult

type IndexResult struct {
	Files        int
	Directories  int
	Repos        int
	StaleRemoved int
	RootURI      string
	Generation   string
	Errors       []error // Errors from individual indexers (non-fatal)
}

IndexResult contains statistics from an indexing operation.

type NeighborResult added in v0.18.0

type NeighborResult struct {
	// Node is the connected graph node.
	Node *graph.Node

	// EdgeType is the type of the edge connecting origin and Node.
	EdgeType string

	// Direction is "in" when the edge points from Node toward the origin,
	// or "out" when it points from the origin toward Node.
	Direction string

	// EdgeID is the internal ID of the connecting edge.
	EdgeID string
}

NeighborResult is one entry returned by Neighbors: the connected node together with the edge that links it to the origin node.

type NeighborsOptions added in v0.18.0

type NeighborsOptions struct {
	// Direction controls which edges to follow.
	// Accepted values: "in", "out", "both". Default (empty string) is "both".
	Direction string

	// EdgeTypes restricts results to edges whose type matches one of these
	// values. When nil or empty, all edge types are included.
	EdgeTypes []string

	// Max caps the number of results returned. 0 means no limit.
	Max int
}

NeighborsOptions configures a call to (*Axon).Neighbors.

type Path added in v0.15.0

type Path struct {
	Steps []PathStep
}

Path is an ordered sequence of PathSteps representing a route through the knowledge graph from one node to another.

func (*Path) Length added in v0.15.0

func (p *Path) Length() int

Length returns the number of edges in this path (one less than the number of steps). Returns 0 for a degenerate single-node path.

type PathOptions added in v0.15.0

type PathOptions struct {
	// MaxDepth caps the BFS search depth in number of edges. Default: 6.
	MaxDepth int

	// MaxPaths limits the number of distinct paths returned. Default: 3.
	MaxPaths int

	// EdgeTypes restricts traversal to edges of these types only.
	// When nil or empty, all edge types are traversed.
	EdgeTypes []string
}

PathOptions configures a call to (*Axon).FindPath.

type PathStep added in v0.15.0

type PathStep struct {
	// Node is the graph node at this position in the path.
	Node *graph.Node

	// EdgeType is the type of the edge that connects the previous node to this
	// one. Empty string for the first step (the origin node).
	EdgeType string

	// Incoming is true when this step was reached by following an incoming
	// edge in reverse (i.e. the underlying edge points FROM this node TOWARD
	// the previous node). False for outgoing edges and for the origin step.
	Incoming bool
}

PathStep is one node in a path through the knowledge graph, annotated with the edge that was followed to arrive at it.

type Querier added in v0.9.0

type Querier interface {
	// Query executes a pre-built AQL query (from aql.Builder.Build or aql.Parse).
	Query(ctx context.Context, q *aql.Query) (*graph.QueryResult, error)

	// QueryString parses an AQL string and executes it in one call.
	QueryString(ctx context.Context, q string) (*graph.QueryResult, error)

	// Explain returns the execution plan for a pre-built AQL query without
	// running it. Useful for debugging and performance analysis.
	Explain(ctx context.Context, q *aql.Query) (*graph.QueryPlan, error)

	// Find returns nodes matching the structural filter.
	Find(ctx context.Context, filter graph.NodeFilter, opts graph.QueryOptions) ([]*graph.Node, error)

	// Search performs semantic vector similarity search.
	// Returns ErrNoEmbeddingProvider if no embedding provider is configured.
	Search(ctx context.Context, queries []string, opts SearchOptions) ([]*SemanticSearchResult, error)

	// FindPath finds the shortest paths between two nodes identified by their
	// node IDs. Returns an empty slice (no error) when no path exists within
	// the configured depth limit.
	FindPath(ctx context.Context, fromID, toID string, opts PathOptions) ([]*Path, error)
}

Querier is the read-only interface for executing queries against an axon graph. *Axon satisfies it, allowing integrators to depend on the interface for easier testing and decoupling.

type SearchOptions added in v0.9.1

type SearchOptions struct {
	// Limit is the maximum number of results to return. Defaults to 20.
	Limit int

	// Filter restricts the search to nodes matching the given criteria.
	// nil = no filter (search all nodes).
	Filter *graph.NodeFilter

	// MinScore drops results whose similarity score is below this threshold.
	// Range 0.0–1.0. Default 0 = return all results.
	MinScore float32
}

SearchOptions configures a call to (*Axon).Search.

type SemanticSearchResult added in v0.6.0

type SemanticSearchResult struct {
	*graph.NodeWithScore
	MatchedQuery string
}

SemanticSearchResult is a node with its best-match score and the query that produced it.

type WatchOptions added in v0.5.0

type WatchOptions struct {
	IndexOptions

	// Debounce is how long to wait after the last file event before
	// triggering a re-index. Default: 150ms.
	Debounce time.Duration

	// GCInterval is how often the background GC ticker runs DeleteExpired.
	// Default: 5 minutes. Set to 0 to disable background expiry GC.
	GCInterval time.Duration

	// OnReady is called once after the initial full index completes.
	// It is NOT called on subsequent change-triggered re-indexes.
	// If nil, the initial result is silently discarded.
	OnReady func(result *IndexResult, err error)

	// OnReindex is called after each change-triggered re-index.
	// It is NOT called for the initial index (use OnReady for that).
	// If nil, re-index results are silently discarded.
	OnReindex func(path string, result *IndexResult, err error)
}

WatchOptions configures the Watch behavior.

Directories

Path Synopsis
adapters
Package aql provides a type-safe fluent API and parser for the Axon Query Language (AQL).
Package aql provides a type-safe fluent API and parser for the Axon Query Language (AQL).
cmd
axon command
axonui command
Package context provides the budget fitting algorithm for the Context Window Optimizer.
Package context provides the budget fitting algorithm for the Context Window Optimizer.
Package graph provides the core graph data structures and operations for Axon.
Package graph provides the core graph data structures and operations for Axon.
embeddings
Package embeddings provides the Provider interface and built-in implementations for generating vector embeddings from text.
Package embeddings provides the Provider interface and built-in implementations for generating vector embeddings from text.
fs
git
git/commitparser
Package commitparser implements a lenient parser for Conventional Commits (https://www.conventionalcommits.org/).
Package commitparser implements a lenient parser for Conventional Commits (https://www.conventionalcommits.org/).
golang
Package golang provides an indexer for Go modules and packages.
Package golang provides an indexer for Go modules and packages.
project
Package project provides an indexer for detecting project types from manifest files.
Package project provides an indexer for detecting project types from manifest files.
todo
Package todo indexes TODO/FIXME/HACK/XXX/NOTE annotation comments from source files, emitting one code:todo graph node per matched annotation.
Package todo indexes TODO/FIXME/HACK/XXX/NOTE annotation comments from source files, emitting one code:todo graph node per matched annotation.
Package progress provides progress reporting for indexing operations.
Package progress provides progress reporting for indexing operations.

Jump to

Keyboard shortcuts

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