argus

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 30 Imported by: 0

README

Argus

Go Reference

Argus is a Go library that indexes source code into a knowledge graph and answers natural language questions about it. It combines semantic search, BM25, call-graph traversal, and cross-encoder reranking to surface relevant symbols with citations.

// 1. Create an engine
engine, _ := argus.New(argus.Config{
    // Required
    FalkorDBAddr: "localhost:6382",
    TEIURL:       "http://localhost:8085",

    // Optional: cross-encoder reranker for sharper relevance scoring
    RerankURL: "http://localhost:8086",

    // Optional: LLM for synthesized answers and component enrichment
    LLMBaseURL: "http://localhost:11434",
    LLMModel:   "llama3.1:8b",
    LLMAPIKey:  "", // set if your LLM requires auth
})
defer engine.Close()

// 2. Index a codebase
result, _ := engine.Indexer.Index(ctx, argus.IndexRequest{Path: "./my-project"})

// 3. Ask a question
answer, _ := engine.Ask(ctx, "How does error handling work?")
fmt.Println(answer.Answer) // synthesized answer with file:line citations

Features

  • Multi-language parsing — Go, Rust, JavaScript/TypeScript, Python via tree-sitter
  • Hybrid retrieval — Semantic + BM25 + call-graph BFS, fused with RRF
  • Cross-encoder reranking — TEI-powered joint relevance scoring
  • Component detection — Auto-groups symbols into architectural components with dependency edges
  • Component-scoped queries — Narrow search to a single component and its dependencies
  • Call graph traversal — Follow caller/callee chains from any function
  • Optional LLM synthesis — Ollama/OpenAI integration for natural language answers

Installation

# CLI (interactive TUI)
go install github.com/akhilparakka/argus/cmd/argus@latest

# Library (import into your own code)
go get github.com/akhilparakka/argus

Quick Start

1. Start FalkorDB and TEI

Argus needs two services: FalkorDB (graph database) and TEI (text embeddings inference).

# Option A: Docker Compose (local)
docker compose up -d

# Option B: Your own instances
# export FALKORDB_ADDR="falkordb.mydomain.com:6379"
# export TEI_URL="https://tei.mydomain.com"
2. Create an engine
engine, err := argus.New(argus.Config{
    // Required
    FalkorDBAddr: "localhost:6382",
    TEIURL:       "http://localhost:8085",

    // Optional: LLM for synthesized answers (Ollama, OpenAI, etc.)
    LLMBaseURL: "http://localhost:11434",
    LLMModel:   "llama3.1:8b",
    LLMAPIKey:  "", // set if your LLM requires auth

    // Optional: reranker for sharper relevance scoring
    RerankURL: "http://localhost:8086",
})
if err != nil {
    log.Fatal(err)
}
defer engine.Close()
3. Index a codebase
result, err := engine.Indexer.Index(ctx, argus.IndexRequest{
    Path: "/path/to/codebase",
})
fmt.Printf("Indexed %d files, %d symbols\n", result.FilesProcessed, result.NodesCreated)
4. Ask questions
// With LLM configured: returns a synthesized natural language answer
result, _ := engine.Ask(ctx, "How does the agent loop work?")
fmt.Println(result.Answer)

// Without LLM: returns raw formatted context
result, _ := engine.Ask(ctx, "Show me error handling code")
fmt.Println(result.Context) // ranked symbols with file:line citations

// Scoped to a single component
result, _ := engine.Ask(ctx, "What does the embedding service do?",
    &argus.ComponentScope{Name: "Embedding Service"},
)

Configuration

Field Required Default Description
FalkorDBAddr FalkorDB host:port (Redis protocol)
TEIURL TEI (text-embeddings-inference) endpoint
GraphName "argus" FalkorDB graph name
RerankURL Cross-encoder reranker endpoint
RerankToken Bearer token for authenticated reranker
LLMBaseURL LLM API base URL (Ollama, OpenAI, etc.)
LLMModel Model name for LLM synthesis
LLMAPIKey API key for authenticated LLM endpoints
Workers 8 Concurrent indexer workers
SemanticLimit 20 Semantic search top-K
BM25Limit 20 BM25 search top-K
CallGraphDepth 2 Call-graph BFS expansion depth
ResultCap 30 Final result cap before rerank

Low-Level API

For custom pipelines, use the client directly:

// Search APIs
semantic, _ := client.SearchCodeSemantic(ctx, embedding, "", nil, 20)
bm25, _ := client.SearchCodeBM25(ctx, "error handling", "", nil, 20)
cg, _ := client.GetCallGraph(ctx, "pkg.(*Server).Handle", "forward", 3, 0)

// Component APIs
comps, _ := client.GetComponentTree(ctx)

CLI

A terminal UI is included for interactive exploration.

# Setup (one-time)
argus init

# Run
argus

Features: component picker (Tab), source expansion (Ctrl+E), text copy (Ctrl+Y), markdown rendering.

Graph Schema

Nodes (:Code): functions, methods, structs, interfaces, files, packages, components.

Edges: DEFINES, CALLS, IMPLEMENTS, IMPORTS, CONTAINS, PART_OF, HAS_METHOD.

Indexes: vector index on code_embedding, full-text index on doc_comment.

Development

git clone https://github.com/akhilparakka/argus
cd argus
docker compose up -d
go test ./...

License

MIT

Documentation

Index

Constants

View Source
const CodeChatSystemPrompt = `` /* 418-byte string literal not displayed */

CodeChatSystemPrompt instructs the synthesis LLM to answer strictly from retrieved code context. Exported so downstream chat implementations (TUI, Terminalator) share one prompt instead of drifting copies.

View Source
const (
	DefaultRRFK = 60
)

Retrieval tuning constants.

View Source
const DefaultVectorDimension = 768

DefaultVectorDimension is the fallback embedding dimension used when the caller does not specify one. It matches the BAAI/bge-base-en-v1.5 TEI model.

Variables

View Source
var GoPlugin = &LanguagePlugin{
	Name:         LangGo,
	Extensions:   []string{".go"},
	NewParser:    func() (*Parser, error) { return NewParser(LangGo) },
	NewExtractor: func() (*Extractor, error) { return NewExtractor(LangGo) },
	Qualifier:    &GoQualifier{},

	RuntimeEntryNames: []string{"main", "init"},
	TestFileSuffixes:  []string{"_test.go"},
	FuncDefPattern:    `(?m)^\s*func\s+(?:\([^)]*\)\s*)?%s\s*\(`,
}
View Source
var JavaScriptPlugin = &LanguagePlugin{
	Name:         LangJavaScript,
	Extensions:   []string{".js", ".jsx", ".mjs"},
	NewParser:    func() (*Parser, error) { return NewParser(LangJavaScript) },
	NewExtractor: func() (*Extractor, error) { return NewExtractor(LangJavaScript) },
	Qualifier:    &JSQualifier{},

	RuntimeEntryNames: nil,
	TestFileSuffixes: []string{
		".test.js", ".test.jsx", ".test.mjs",
		".spec.js", ".spec.jsx", ".spec.mjs",
	},
	FuncDefPattern: `(?m)^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s*%s\s*\(`,
}
View Source
var PythonPlugin = &LanguagePlugin{
	Name:         LangPython,
	Extensions:   []string{".py", ".pyw"},
	NewParser:    func() (*Parser, error) { return NewParser(LangPython) },
	NewExtractor: func() (*Extractor, error) { return NewExtractor(LangPython) },
	Qualifier:    &PythonQualifier{},

	RuntimeEntryNames: nil,
	TestFileSuffixes:  []string{"_test.py"},
	TestFilePrefixes:  []string{"test_"},
	FuncDefPattern:    `(?m)^\s*(?:async\s+)?def\s+%s\s*\(`,
}
View Source
var RustPlugin = &LanguagePlugin{
	Name:         LangRust,
	Extensions:   []string{".rs"},
	NewParser:    func() (*Parser, error) { return NewParser(LangRust) },
	NewExtractor: func() (*Extractor, error) { return NewExtractor(LangRust) },
	Qualifier:    &RustQualifier{},

	RuntimeEntryNames: []string{"main"},

	TestFileSuffixes: nil,
	FuncDefPattern:   `(?m)^\s*(?:pub\s+)?fn\s+%s\b`,
}

Functions

func EnrichComponents

func EnrichComponents(
	ctx context.Context,
	p LLMProvider,
	components []CodeNode,
	symbolSummaries map[string][]string,
) (map[string]ComponentEnrichment, error)

EnrichComponents calls the LLM once with a batched prompt to generate human-readable names, roles, and descriptions for each component. Returns a map from directory path → enrichment. On failure the caller should keep the path-based names.

func FetchTEIDimension

func FetchTEIDimension(baseURL string) (int, error)

FetchTEIDimension discovers the model's embedding dimension by sending a single probe embedding request and measuring the returned vector length.

func PingFalkorDB

func PingFalkorDB(addr string) error

PingFalkorDB verifies that a FalkorDB instance is reachable at addr. It returns a clear error for connection failures vs non-FalkorDB servers.

func PingOllama

func PingOllama(baseURL, apiKey string) error

PingOllama verifies the Ollama endpoint is reachable. It checks the /api/tags endpoint (local) or simply that the base URL responds. Model availability is checked at runtime when the user actually sends a chat request.

func PingTEI

func PingTEI(baseURL string) error

PingTEI sends a lightweight request to verify the TEI endpoint is alive.

func PluginsByLanguage added in v0.2.0

func PluginsByLanguage() map[Language]*LanguagePlugin

PluginsByLanguage returns one plugin per registered language (extensions share a plugin; the map is deduplicated by Name).

func RegisterLanguage

func RegisterLanguage(p *LanguagePlugin)

RegisterLanguage registers a language plugin for all its file extensions.

func RegisteredExtensions

func RegisteredExtensions() []string

RegisteredExtensions returns all registered file extensions.

func VerifyComponentImports added in v0.2.0

func VerifyComponentImports(root string, components []ComponentNode, pairs map[string]map[string]int) map[string]bool

VerifyComponentImports checks each claimed component dependency (fromComponent → toComponent) against the claiming component's sources: does ANY file under the from-directory import the to-package's path?

Import path derivation (Go-first): module name from go.mod at root + "/" + toDir relative path. Root-level components map to the bare module. If go.mod is absent (non-Go workspace), nothing can be corroborated — every pair is returned as verified=true to preserve legacy behavior, since dropping real edges is worse than tolerating phantoms.

Returns a set of "from/to" keys that passed corroboration.

Types

type AnalysisService added in v0.2.0

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

AnalysisService runs structural analyses over an existing code graph. Safe to share across goroutines; the underlying client pools connections.

workspaceRoot enables text-level verification: the code graph only sees call expressions, so method-values (`mux.Handle("/", s.chat)`), runtime entry points (main/init), and interface dispatch are invisible to it and would otherwise surface as false "dead" candidates. When a workspace root is provided, candidates are confirmed against raw text references before being reported.

func NewAnalysisService added in v0.2.0

func NewAnalysisService(client *FalkorClient, workspaceRoot string) *AnalysisService

NewAnalysisService creates an analyzer bound to a connected client. Pass workspaceRoot (the indexed project path) to enable text-level verification of dead-code candidates; pass "" to skip it.

func (*AnalysisService) DeadCandidates added in v0.2.0

func (a *AnalysisService) DeadCandidates(ctx context.Context, limit int) ([]DeadCandidate, DeadCodeSummary, error)

DeadCandidates lists unexported functions/methods with zero inbound CALLS edges AND — when a workspace root was provided — zero textual references in the indexed sources. Two-stage verification kills the dominant false positives that pure call-graph analysis cannot see:

  • runtime entry points (main/init) are excluded up front
  • method-value registrations (`mux.Handle("/", s.chat)`) carry no CALLS edge but DO contain the name as text → dropped as referenced
  • same-file helpers referenced before/after definition → dropped

Remaining candidates survived both structural AND raw-text checks; they still warrant a human glance (implicit interface satisfaction leaves no textual trace), but precision jumps from ~7% to near-total.

func (*AnalysisService) Hubs added in v0.2.0

func (a *AnalysisService) Hubs(ctx context.Context, t HubThresholds, limit int) ([]HubScore, error)

Hubs returns callable symbols ranked by total degree (fan-in + fan-out), most-connected first. Symbols below t.Medium total degree are omitted; pass limit ≤ 0 for the default 25.

func (*AnalysisService) Impact added in v0.2.0

func (a *AnalysisService) Impact(ctx context.Context, symbol string, maxDepth int) (*ImpactReport, error)

Impact computes the blast radius of changing a symbol: every function/ method that transitively calls it, within maxDepth hops, grouped by depth and by owning component, with a transparent risk rating.

Resolution: pass an exact qualified_name, or a bare name — bare names are unambiguous only when exactly one definition exists; otherwise an error lists the candidates so the caller can disambiguate (never guess).

type BuilderOption

type BuilderOption func(*CodeContextBuilder)

BuilderOption configures optional CodeContextBuilder behavior.

func WithBuilderLogger

func WithBuilderLogger(l *slog.Logger) BuilderOption

WithBuilderLogger sets a logger for retrieval diagnostics (search failures, degenerate embeddings, rerank problems). If unset, the builder runs silent.

func WithWorkspaceRoot added in v0.2.0

func WithWorkspaceRoot(root string) BuilderOption

WithWorkspaceRoot enables import-corroboration for component dependency lists fetched during retrieval scope resolution.

type CallGraph

type CallGraph struct {
	Root      string     `json:"root"`      // qualified_name
	Direction string     `json:"direction"` // forward | reverse
	Depth     int        `json:"depth"`
	Nodes     []CodeNode `json:"nodes"`
	Edges     []CodeEdge `json:"edges"`
}

CallGraph represents a function's call hierarchy.

type CallSite

type CallSite struct {
	CalleeName   string // function/method name being called: "HandleLogin", "GetUser"
	Object       string // receiver/object for method calls: "db", "s", "" for bare calls
	Line         int    // line where the call happens (1-based)
	IsMethodCall bool   // true = obj.Method(), false = Function()
}

CallSite represents a function or method call found in source code.

type CallerTier added in v0.2.0

type CallerTier struct {
	Depth   int         `json:"depth"`
	Callers []SymbolRef `json:"callers"`
}

CallerTier groups every distinct caller found at a given hop distance. Depth 1 = direct callers; 2 = their callers; etc.

type CodeChatResult

type CodeChatResult struct {
	Query   string
	Context string // raw context sent to the LLM
	Answer  string // LLM-synthesized answer (or context if no LLM)
	Sources []CodeNode
	Error   string

	// ScopeResolved is false when a ComponentScope was requested but matched
	// no component in the graph (stale name after re-enrichment, typo, or
	// removed directory). Results are then empty for that reason — UIs should
	// refresh their component list instead of showing "not enough context".
	ScopeResolved bool
}

CodeChatResult is the output of a single code-chat query.

type CodeContext

type CodeContext struct {
	Nodes   []CodeNode
	Query   string
	Content string // formatted context string for LLM consumption

	// ScopeResolved reports whether a requested ComponentScope actually
	// matched a component in the graph. False + non-nil scope means the
	// caller used a stale/unknown name (e.g. after LLM enrichment renamed
	// it) — results are empty for that reason, NOT because the question
	// was hard. Surfaces in CodeChatResult so UIs can self-heal.
	ScopeResolved bool
}

CodeContext is the result of a code retrieval pipeline.

type CodeContextBuilder

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

CodeContextBuilder retrieves relevant code symbols for a natural language query. Pipeline: embed query -> semantic + BM25 (concurrent) -> callgraph BFS -> RRF fusion -> cross-encoder rerank -> format.

func NewCodeContextBuilder

func NewCodeContextBuilder(client *FalkorClient, embedder Embedder, rerankURL, rerankToken string, opts ...BuilderOption) *CodeContextBuilder

NewCodeContextBuilder creates a builder backed by the given FalkorDB client and embedder. rerankURL and rerankToken are optional; if empty, reranking is skipped.

func (*CodeContextBuilder) Build

Build runs the code retrieval pipeline.

type CodeContextBuilderParams

type CodeContextBuilderParams struct {
	Query string
	Scope *ComponentScope // nil = unscoped (search all symbols)
}

CodeContextBuilderParams holds the user query and workspace information.

type CodeEdge

type CodeEdge struct {
	SourceUID string `json:"source_uid"`
	TargetUID string `json:"target_uid"`
	Type      string `json:"type"`           // DEFINES | CALLS | IMPLEMENTS | IMPORTS | CONTAINS | PART_OF | HAS_METHOD
	Line      int    `json:"line,omitempty"` // for CALLS: line where call happens

	// CALLS-edge extras. SourceQName/TargetQName carry qualified names so
	// consumers never have to join UIDs against the node list (GetCallGraph
	// populates them; creation paths leave them empty). Object records the
	// receiver expression of a method call (`db` in db.SaveUser(x)) as a
	// disambiguation hint for same-name methods.
	SourceQName string `json:"source_qname,omitempty"`
	TargetQName string `json:"target_qname,omitempty"`
	Object      string `json:"object,omitempty"`
}

CodeEdge represents a relationship between two code entities.

type CodeNode

type CodeNode struct {
	UID           string    `json:"uid,omitempty"`
	Name          string    `json:"name"`
	QualifiedName string    `json:"qualified_name"`
	Kind          string    `json:"kind"` // function | method | struct | interface | class | trait | constant | variable | file | package | component
	FilePath      string    `json:"file_path"`
	StartLine     int       `json:"start_line"`
	EndLine       int       `json:"end_line"`
	Signature     string    `json:"signature"`
	Receiver      string    `json:"receiver,omitempty"` // methods only
	Language      string    `json:"language"`
	IsExported    bool      `json:"is_exported"`
	DocComment    string    `json:"doc_comment"`
	Role          string    `json:"role,omitempty"`
	FileHash      string    `json:"file_hash,omitempty"` // file nodes only
	IndexedAt     int64     `json:"indexed_at"`
	Embedding     []float32 `json:"code_embedding,omitempty"`
	Score         float64   `json:"score,omitempty"` // search relevance score (not stored in DB)
}

CodeNode represents a code entity in the knowledge graph.

func DetectComponents

func DetectComponents(nodes []CodeNode, rootPath string) []CodeNode

DetectComponents groups symbols by directory into component CodeNodes. Each unique directory becomes a component. The name is set to the relative directory path; if an LLM provider is configured the indexer overwrites it with a human-readable name via EnrichComponents.

type ComponentEnrichment

type ComponentEnrichment struct {
	Dir         string `json:"dir"`
	Name        string `json:"name"`
	Role        string `json:"role"`
	Description string `json:"description"`
}

ComponentEnrichment holds the LLM-generated metadata for a component.

type ComponentImpact added in v0.2.0

type ComponentImpact struct {
	Name    string `json:"name"`
	Callers int    `json:"callers"`
}

ComponentImpact aggregates how many callers fall inside one component.

type ComponentNode

type ComponentNode struct {
	Name        string   `json:"name"`
	Role        string   `json:"role"`
	Description string   `json:"description"`
	Color       string   `json:"color"`
	FilePath    string   `json:"file_path"`
	FileCount   int      `json:"file_count"`
	Symbols     []string `json:"symbols"`
	Downstream  []string `json:"downstream"`
	Upstream    []string `json:"upstream"`

	// Dependency claims that could NOT be corroborated by an import in the
	// claiming component's sources. Same-name call resolution fabricates
	// these routinely (measured live: generic .Error()/.Subscribe() calls
	// wired onto wrong-package twins). Kept visible — hints, not silence —
	// but excluded from Downstream/Upstream so cards stay trustworthy.
	// Empty when no workspace root was available for verification.
	UnverifiedDownstream []string `json:"unverified_downstream,omitempty"`
	UnverifiedUpstream   []string `json:"unverified_upstream,omitempty"`
}

ComponentNode represents a detected component.

type ComponentScope

type ComponentScope struct {
	Name        string // component name to focus on
	IncludeDeps bool   // also search symbols from dependency components
	MaxDeps     int    // cap on dependency components (0 = no cap)
}

ComponentScope restricts retrieval to symbols belonging to a specific component. When passed to Engine.Ask, the retrieval pipeline filters semantic and BM25 results to only include symbols with PART_OF edges to the named component (and optionally its direct dependencies).

type Config

type Config struct {
	// Service URLs (required)
	FalkorDBAddr string // e.g. "localhost:6382"
	TEIURL       string // e.g. "http://localhost:8085"

	// Graph name (default: "argus")
	GraphName string

	// Reranker (optional)
	RerankURL   string
	RerankToken string

	// LLM (optional — only for chat synthesis and component enrichment)
	LLMBaseURL string
	LLMModel   string
	LLMAPIKey  string

	// Tuning
	Workers        int // Indexer workers (default 8)
	RRFK           int // RRF k parameter (default 60)
	SemanticLimit  int // Semantic search result limit (default 20)
	BM25Limit      int // BM25 search result limit (default 20)
	CallGraphDepth int // BFS expansion depth (default 2)
	ResultCap      int // Final result cap (default 30)

	// WorkspaceRoot enables text-level verification (dead-code scan) and
	// import-corroborated component dependencies. Empty = legacy behavior.
	WorkspaceRoot string

	// Optional logger for indexer and retrieval diagnostics.
	// If nil, both run silent.
	Logger *slog.Logger
}

Config holds all service configuration and tuning parameters for the Argus engine.

type DeadCandidate added in v0.2.0

type DeadCandidate struct {
	Name          string `json:"name"`
	QualifiedName string `json:"qualified_name"`
	Kind          string `json:"kind"`
	Language      string `json:"language"`
	FilePath      string `json:"file_path"`
	StartLine     int    `json:"start_line"`
	Reason        string `json:"reason"`
}

DeadCandidate is a symbol that appears unreachable from within the indexed project: zero inbound CALLS and unexported.

type DeadCodeSummary added in v0.2.0

type DeadCodeSummary struct {
	GraphCandidates     int             `json:"graph_candidates"`
	DroppedAsReferenced int             `json:"dropped_as_referenced"`
	TestOnly            []DeadCandidate `json:"test_only,omitempty"`
	VerifiedDead        []DeadCandidate `json:"verified_dead"`

	// Unverified > 0 means no workspace root was configured, so candidates
	// could not be text-checked. Treat VerifiedDead as raw graph hints only.
	Unverified int `json:"unverified,omitempty"`
}

DeadCodeSummary reports what the dead-code pass saw, so callers can show "N candidates, M confirmed after text verification" instead of a bare list.

type Embedder

type Embedder interface {
	Embed(ctx context.Context, texts []string) ([][]float32, error)
	Dimension() int
}

Embedder turns text into dense vectors for semantic search.

type Engine

type Engine struct {
	Client   *FalkorClient
	Embedder Embedder
	Indexer  *Indexer

	Config Config
	// contains filtered or unexported fields
}

Engine is the fully wired Argus engine with indexing, retrieval, and chat.

func New

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

New creates a fully wired Engine from the given Config.

Construction order matters: the TEI embedder is created first so its auto-detected embedding dimension can size the FalkorDB vector index. Creating the client first would hardcode the dimension and break every embedding write against non-768 TEI models (e.g. bge-m3).

func (*Engine) Ask

func (e *Engine) Ask(ctx context.Context, query string, scope ...*ComponentScope) (CodeChatResult, error)

Ask runs the code retrieval pipeline and optionally calls the LLM to synthesize an answer. If no LLM is configured, it returns the raw formatted context as the answer. An optional ComponentScope restricts retrieval to symbols belonging to the named component.

func (*Engine) Close

func (e *Engine) Close() error

Close cleans up all resources held by the Engine.

type ErrEmbeddingFailed

type ErrEmbeddingFailed struct {
	Cause error
}

ErrEmbeddingFailed is returned when text-to-vector conversion fails.

func (ErrEmbeddingFailed) Error

func (e ErrEmbeddingFailed) Error() string

func (ErrEmbeddingFailed) Unwrap

func (e ErrEmbeddingFailed) Unwrap() error

type ErrFalkorDBUnavailable

type ErrFalkorDBUnavailable struct {
	Addr string
	Err  error
}

ErrFalkorDBUnavailable is returned when FalkorDB cannot be reached.

func (ErrFalkorDBUnavailable) Error

func (e ErrFalkorDBUnavailable) Error() string

func (ErrFalkorDBUnavailable) Unwrap

func (e ErrFalkorDBUnavailable) Unwrap() error

type ErrInvalidQuery

type ErrInvalidQuery struct {
	Query string
}

ErrInvalidQuery is returned when the query is empty or too short.

func (ErrInvalidQuery) Error

func (e ErrInvalidQuery) Error() string

type ErrLLMUnavailable

type ErrLLMUnavailable struct {
	BaseURL string
	Err     error
}

ErrLLMUnavailable is returned when the LLM provider cannot be reached.

func (ErrLLMUnavailable) Error

func (e ErrLLMUnavailable) Error() string

func (ErrLLMUnavailable) Unwrap

func (e ErrLLMUnavailable) Unwrap() error

type ErrRetrievalTimeout

type ErrRetrievalTimeout struct {
	Duration time.Duration
}

ErrRetrievalTimeout is returned when the retrieval pipeline exceeds its deadline.

func (ErrRetrievalTimeout) Error

func (e ErrRetrievalTimeout) Error() string

type ErrSymbolNotFound

type ErrSymbolNotFound struct {
	QualifiedName string
}

ErrSymbolNotFound is returned when a specific symbol cannot be located.

func (ErrSymbolNotFound) Error

func (e ErrSymbolNotFound) Error() string

type ErrTEIUnavailable

type ErrTEIUnavailable struct {
	URL string
	Err error
}

ErrTEIUnavailable is returned when the Text Embeddings Inference service cannot be reached.

func (ErrTEIUnavailable) Error

func (e ErrTEIUnavailable) Error() string

func (ErrTEIUnavailable) Unwrap

func (e ErrTEIUnavailable) Unwrap() error

type ErrWorkspaceNotFound

type ErrWorkspaceNotFound struct{}

ErrWorkspaceNotFound is returned when a workspace has no indexed code.

func (ErrWorkspaceNotFound) Error

func (e ErrWorkspaceNotFound) Error() string

type ExtractResult

type ExtractResult struct {
	FileNode CodeNode   // kind: "file"
	Symbols  []CodeNode // functions, methods, structs, interfaces
	Edges    []CodeEdge // DEFINES edges (file → symbol)
	Calls    []CallSite // function/method call sites
}

ExtractResult holds everything extracted from a single file.

type Extractor

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

Extractor runs tree-sitter queries against parsed AST to produce CodeNodes and CodeEdges.

func NewExtractor

func NewExtractor(lang Language) (*Extractor, error)

NewExtractor creates an extractor for a given language.

func NewExtractorFromBytes

func NewExtractorFromBytes(query []byte) *Extractor

NewExtractorFromBytes creates an extractor from raw query bytes (useful for tests).

func NewExtractorFromBytesWithLang

func NewExtractorFromBytesWithLang(query []byte, lang Language) *Extractor

NewExtractorFromBytesWithLang creates an extractor from raw query bytes for a specific language.

func (*Extractor) Extract

func (e *Extractor) Extract(result *ParseResult) (*ExtractResult, error)

Extract runs the query against the parsed tree and builds nodes + edges.

type FalkorClient

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

FalkorClient manages a connection to FalkorDB for code intelligence operations.

func NewFalkorClient

func NewFalkorClient(addr, graphName string) (*FalkorClient, error)

NewFalkorClient creates a new client connected to the given FalkorDB instance with the default vector dimension (768). Use NewFalkorClientDim when the embedder produces a different dimension, or the vector index will reject every embedding write.

func NewFalkorClientDim

func NewFalkorClientDim(addr, graphName string, dim int) (*FalkorClient, error)

NewFalkorClientDim creates a client whose :Code vector index matches dim. The dimension MUST equal the embedder's Dimension() — TEI models vary (bge-base=768, bge-m3=1024, ...) and a mismatch fails all embedding writes.

func (*FalkorClient) Close

func (c *FalkorClient) Close() error

Close closes the connection to FalkorDB.

func (*FalkorClient) CreateCodeEdge

func (c *FalkorClient) CreateCodeEdge(_ context.Context, edge CodeEdge) error

CreateCodeEdge creates a direct edge between two :Code nodes.

func (*FalkorClient) CreateCodeEdgeBatch

func (c *FalkorClient) CreateCodeEdgeBatch(_ context.Context, edges []CodeEdge) error

CreateCodeEdgeBatch creates multiple edges in batches, grouped by edge type.

func (*FalkorClient) DeleteCodeByFile

func (c *FalkorClient) DeleteCodeByFile(_ context.Context, filePath string) error

DeleteCodeByFile removes all :Code nodes for a given file path. DETACH DELETE also removes all connected edges automatically.

func (*FalkorClient) DeleteOrphanComponents

func (c *FalkorClient) DeleteOrphanComponents(_ context.Context) (int64, error)

UpdateComponentMeta updates a component's name, role, description, and qualified_name after LLM enrichment.

The node is matched on file_path — the stable natural key shared with UpsertCodeNode's MERGE — NEVER on the mutable qualified_name. Renaming component A to a name component B will later also receive makes qname-based matching hijack the wrong node (observed live: both api dirs DeleteOrphanComponents removes :Code component nodes that no longer have any symbols attached via PART_OF (ghosts left when a directory's last file disappears). Returns the number removed.

func (*FalkorClient) GetCallGraph

func (c *FalkorClient) GetCallGraph(_ context.Context, qualifiedName, direction string, depth int, maxResults int) (*CallGraph, error)

GetCallGraph traverses CALLS edges forward or reverse from a function.

func (*FalkorClient) GetComponentTree

func (c *FalkorClient) GetComponentTree(ctx context.Context, workspaceRoot string) ([]ComponentNode, error)

GetComponentTree returns all components with their child symbols and dependency lists. When workspaceRoot is non-empty, cross-component CALLS claims are corroborated against imports in the claiming component's sources: uncorroborated pairs (typically same-name resolution artifacts) move to UnverifiedDownstream/Upstream instead of polluting the trusted lists. Pass "" for legacy unfiltered behavior.

func (*FalkorClient) GetComponentsSymbolSummaries

func (c *FalkorClient) GetComponentsSymbolSummaries(_ context.Context) (map[string][]string, error)

GetComponentsSymbolSummaries returns each component's CURRENT full symbol list as one-line descriptions, keyed by component file_path.

func (*FalkorClient) GetFileHashes

func (c *FalkorClient) GetFileHashes(_ context.Context) (map[string]string, error)

GetFileHashes returns a map[filePath]fileHash for incremental reindexing.

func (*FalkorClient) Graph

func (c *FalkorClient) Graph() *falkordb.Graph

Graph returns the underlying FalkorDB graph for raw queries.

func (*FalkorClient) Name

func (c *FalkorClient) Name() string

Name returns the graph name this client operates on.

func (*FalkorClient) Schema

func (c *FalkorClient) Schema() error

Schema creates all required indexes for :Code nodes.

func (*FalkorClient) SearchCodeBM25

func (c *FalkorClient) SearchCodeBM25(_ context.Context, queryText, kind string, scopeComponentNames []string, limit int) ([]CodeNode, error)

SearchCodeBM25 performs full-text search over :Code node names. Unlike the ANN path, the fulltext yield is not candidate-capped, so every text match flows through the scope filter before ordering — scoped BM25 has no starvation problem and needs no over-fetch.

func (*FalkorClient) SearchCodeSemantic

func (c *FalkorClient) SearchCodeSemantic(_ context.Context, embedding []float64, kind string, scopeComponentNames []string, limit int) ([]CodeNode, error)

SearchCodeSemantic performs vector similarity search over :Code nodes. When scopeComponentNames is non-empty, results are restricted to symbols with PART_OF edges into those components. Scope filtering happens AFTER the ANN query (FalkorDB cannot pre-filter vector search), so scoped calls over-fetch internally — see scopeOverfetchK.

func (*FalkorClient) UpdateComponentMeta

func (c *FalkorClient) UpdateComponentMeta(_ context.Context, filePath, newName, role, description string) error

converged onto one node). file_path cannot collide.

func (*FalkorClient) UpsertCodeNode

func (c *FalkorClient) UpsertCodeNode(_ context.Context, node CodeNode) (string, error)

UpsertCodeNode creates or updates a :Code node using its natural key. Nodes with an empty Embedding are stored without the code_embedding property (Cypher NULL assignment removes the attribute).

func (*FalkorClient) UpsertCodeNodeBatch

func (c *FalkorClient) UpsertCodeNodeBatch(_ context.Context, nodes []CodeNode) ([]string, error)

UpsertCodeNodeBatch creates or updates multiple :Code nodes in batches. Returns the UID for each node in the same order as the input slice.

type GoQualifier

type GoQualifier struct{}

GoQualifier produces Go-style qualified names: "module/pkg.(*Receiver).Name"

func (*GoQualifier) DiscoverRoot

func (q *GoQualifier) DiscoverRoot(projectPath string) (string, error)

func (*GoQualifier) Qualify

func (q *GoQualifier) Qualify(pkg, receiver, name, kind string) string

type HubScore added in v0.2.0

type HubScore struct {
	Name          string    `json:"name"`
	QualifiedName string    `json:"qualified_name"`
	Kind          string    `json:"kind"`
	FilePath      string    `json:"file_path"`
	StartLine     int       `json:"start_line"`
	FanIn         int       `json:"fan_in"`
	FanOut        int       `json:"fan_out"`
	TotalDegree   int       `json:"total_degree"`
	Junction      bool      `json:"junction"` // high fan-in AND high fan-out
	Risk          RiskLevel `json:"risk"`

	// SharedDefinitions > 1 means multiple symbols share this name. Call
	// resolution merges same-name call sites onto one node, so FanIn/FanOut
	// may be inflated by traffic belonging to siblings. Generic method names
	// (String/Close/Error/Get…) hit this constantly. Confidence: treat
	// numbers as upper bounds; distinctive names are reliable.
	SharedDefinitions int `json:"shared_definitions"`
}

HubScore describes a symbol's positional importance in the call graph.

fan-in  = distinct callers      (how much depends on it)
fan-out = distinct callees      (how much it depends on)

High values on BOTH axes mark junction points — busy intersections where a change fans out in every direction.

type HubThresholds added in v0.2.0

type HubThresholds struct {
	Medium         int // total degree ≥ this → medium
	High           int // total degree ≥ this → high
	Critical       int // total degree ≥ this → critical
	JunctionFanIn  int // fan-in ≥ this AND…
	JunctionFanOut int // …fan-out ≥ this → junction escalation
}

HubThresholds sets the degree cutoffs for hub classification. Zero fields fall back to defaults (see DefaultHubThresholds).

func DefaultHubThresholds added in v0.2.0

func DefaultHubThresholds() HubThresholds

DefaultHubThresholds returns transparent, documented cutoffs rather than magic numbers buried in queries.

type ImpactReport added in v0.2.0

type ImpactReport struct {
	Query        string            `json:"query"`
	Root         SymbolRef         `json:"root"`
	MaxDepth     int               `json:"max_depth"`
	Tiers        []CallerTier      `json:"tiers"`
	TotalCallers int               `json:"total_callers"`
	Components   []ComponentImpact `json:"components"`
	Risk         RiskLevel         `json:"risk"`
	RiskReasons  []string          `json:"risk_reasons"`
}

ImpactReport answers: "this symbol changed — what is affected, how far does it reach, and how risky is that?"

type IndexRequest

type IndexRequest struct {
	Path      string
	Languages []string // e.g. ["go", "rust"]
	Ignore    []string // e.g. [".git", "vendor"]
}

IndexRequest configures what to index.

type IndexResult

type IndexResult struct {
	FilesProcessed int
	NodesCreated   int
	EdgesCreated   int
	Errors         []error
	Duration       time.Duration
}

IndexResult reports what was indexed.

type Indexer

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

Indexer orchestrates code indexing: walk → parse → extract → qualify → embed → store.

func NewIndexer

func NewIndexer(client *FalkorClient, embedder Embedder, workers int, opts ...IndexerOption) (*Indexer, error)

NewIndexer creates a new indexer. embedder is required for semantic search.

func (*Indexer) Index

func (idx *Indexer) Index(ctx context.Context, req IndexRequest) (*IndexResult, error)

Index indexes a directory of source code into FalkorDB.

type IndexerOption

type IndexerOption func(*Indexer)

IndexerOption configures optional Indexer behavior.

func WithLogger

func WithLogger(l *slog.Logger) IndexerOption

WithLogger sets a logger for diagnostic output. If nil, the indexer runs silently.

func WithProvider

func WithProvider(p LLMProvider) IndexerOption

WithProvider enables LLM-based component enrichment during indexing.

type JSQualifier

type JSQualifier struct{}

JSQualifier produces JS-style qualified names: "src/api.js::handleLogin"

func (*JSQualifier) DiscoverRoot

func (q *JSQualifier) DiscoverRoot(projectPath string) (string, error)

func (*JSQualifier) Qualify

func (q *JSQualifier) Qualify(pkg, receiver, name, kind string) string

type LLMEvent

type LLMEvent struct {
	Type  LLMEventType
	Delta string
	Error error
}

LLMEvent is a single streaming event from an LLM provider.

type LLMEventType

type LLMEventType string

LLMEventType identifies the kind of streaming event.

const (
	LLMEventTextDelta LLMEventType = "text_delta"
	LLMEventFinish    LLMEventType = "finish"
	LLMEventError     LLMEventType = "error"
)

type LLMMessage

type LLMMessage struct {
	Role    string // "user", "assistant", "system"
	Content string
}

LLMMessage is a single message in an LLM conversation.

type LLMParams

type LLMParams struct {
	Messages    []LLMMessage
	Temperature *float64
	Seed        *int
}

LLMParams configures an LLM completion request.

type LLMProvider

type LLMProvider interface {
	Stream(ctx context.Context, params LLMParams) (iter.Seq[*LLMEvent], error)
}

LLMProvider is the interface for LLM backends used during component enrichment. Implement this with Ollama, OpenAI, Anthropic, or any other provider.

type Language

type Language string

Language represents a supported programming language.

const (
	LangGo         Language = "go"
	LangRust       Language = "rust"
	LangJavaScript Language = "javascript"
	LangPython     Language = "python"
)

func DetectLanguage

func DetectLanguage(ext string) (Language, bool)

DetectLanguage guesses the language from a file extension.

type LanguagePlugin

type LanguagePlugin struct {
	Name         Language
	Extensions   []string
	NewParser    func() (*Parser, error)
	NewExtractor func() (*Extractor, error)
	Qualifier    SymbolQualifier

	// RuntimeEntryNames are functions invoked by the language runtime
	// rather than by project code (invisible to any call graph).
	RuntimeEntryNames []string

	// Test-file conventions, used to split references into production vs
	// test buckets. Empty = the language has no file-level test convention
	// (e.g. Rust tests are inline via #[cfg(test)]).
	TestFileSuffixes []string
	TestFilePrefixes []string

	// FuncDefPattern is a regexp template (%s = quoted symbol name)
	// matching a DEFINITION line for a symbol — used by text verification
	// to ignore a candidate's own declaration while counting references.
	FuncDefPattern string
}

LanguagePlugin bundles everything needed to support one programming language.

func LookupByExtension

func LookupByExtension(ext string) (*LanguagePlugin, bool)

LookupByExtension returns the plugin for a file extension (e.g. ".go").

func (*LanguagePlugin) DefinitionLineRe added in v0.2.0

func (p *LanguagePlugin) DefinitionLineRe(symbol string) *regexp.Regexp

DefinitionLineRe compiles this language's definition-line pattern for the given symbol name.

func (*LanguagePlugin) IsTestFileName added in v0.2.0

func (p *LanguagePlugin) IsTestFileName(fileName string) bool

IsTestFileName reports whether fileName matches this language's test-file conventions. A plugin with no conventions never classifies tests.

type OllamaProvider

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

OllamaProvider implements LLMProvider using Ollama's native /api/chat endpoint.

func NewOllamaProvider

func NewOllamaProvider(baseURL, model, apiKey string) *OllamaProvider

NewOllamaProvider creates an Ollama provider. baseURL: e.g. "http://localhost:11434" or Ollama Cloud URL. model: e.g. "qwen2.5-coder:14b". apiKey: optional Bearer token for authenticated endpoints.

func (*OllamaProvider) Stream

func (p *OllamaProvider) Stream(ctx context.Context, params LLMParams) (iter.Seq[*LLMEvent], error)

Stream sends a chat request to Ollama and yields text-delta events.

type ParseResult

type ParseResult struct {
	Tree   *sitter.Tree
	Src    []byte
	Lang   Language
	TsLang *sitter.Language // raw tree-sitter language for query execution
}

ParseResult holds the output of parsing a single file.

type Parser

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

Parser wraps tree-sitter with language-specific setup.

func NewParser

func NewParser(lang Language) (*Parser, error)

NewParser creates a parser for the given language.

func (*Parser) Close

func (p *Parser) Close()

Close releases the underlying tree-sitter parser.

func (*Parser) ParseBytes

func (p *Parser) ParseBytes(src []byte) (*ParseResult, error)

ParseBytes parses source code from a byte slice (useful for tests).

func (*Parser) ParseFile

func (p *Parser) ParseFile(filePath string) (*ParseResult, error)

ParseFile reads and parses a single source file.

type PythonQualifier

type PythonQualifier struct{}

PythonQualifier produces Python-style qualified names: "package.module.ClassName.method"

func (*PythonQualifier) DiscoverRoot

func (q *PythonQualifier) DiscoverRoot(projectPath string) (string, error)

func (*PythonQualifier) Qualify

func (q *PythonQualifier) Qualify(pkg, receiver, name, kind string) string

type RerankClient

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

RerankClient calls a TEI (text-embeddings-inference) cross-encoder /rerank endpoint. A cross-encoder scores each (query, text) pair jointly, giving far sharper relevance than the bi-encoder cosine used for the candidate search — so it pulls the answer-bearing facts to the top, letting us safely keep a focused top-K instead of dumping the full RRF tail.

func NewRerankClient

func NewRerankClient(url, token string) *RerankClient

NewRerankClient creates a client for a TEI cross-encoder reranking endpoint. The token is optional and used for authenticated endpoints (e.g. Hugging Face Inference).

func (*RerankClient) Rerank

func (r *RerankClient) Rerank(ctx context.Context, query string, texts []string) ([]int, error)

Rerank returns the input text indices reordered by descending cross-encoder relevance to the query. The returned slice indexes into the original texts.

type RiskLevel added in v0.2.0

type RiskLevel string

RiskLevel classifies how risky/interesting a structural finding is.

const (
	RiskLow      RiskLevel = "low"
	RiskMedium   RiskLevel = "medium"
	RiskHigh     RiskLevel = "high"
	RiskCritical RiskLevel = "critical"
)

type RustQualifier

type RustQualifier struct{}

RustQualifier produces Rust-style qualified names: "crate::module::Name"

func (*RustQualifier) DiscoverRoot

func (q *RustQualifier) DiscoverRoot(projectPath string) (string, error)

func (*RustQualifier) Qualify

func (q *RustQualifier) Qualify(pkg, receiver, name, kind string) string

type SymbolQualifier

type SymbolQualifier interface {
	// Qualify builds a globally unique qualified name.
	// Go:   "github.com/user/repo/internal/api.(*Server).HandleChat"
	// Rust: "crate::auth::service::AuthService::validate"
	// JS:   "src/api.js::handleLogin"
	Qualify(pkg, receiver, name, kind string) string

	// DiscoverRoot finds the project module/package root.
	// Go: reads go.mod → module path
	// Rust: reads Cargo.toml → crate name
	// JS: reads package.json → name (or uses directory)
	DiscoverRoot(projectPath string) (modulePath string, err error)
}

SymbolQualifier builds language-specific qualified names.

type SymbolRef added in v0.2.0

type SymbolRef struct {
	Name          string `json:"name"`
	QualifiedName string `json:"qualified_name"`
	Kind          string `json:"kind"`
	FilePath      string `json:"file_path"`
	StartLine     int    `json:"start_line"`
	Component     string `json:"component,omitempty"`
}

SymbolRef identifies a code symbol with its citation and home component.

type TEIEmbedder

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

TEIEmbedder calls a Text Embeddings Inference (TEI) service.

func NewTEIEmbedder

func NewTEIEmbedder(baseURL string, dimension int) (*TEIEmbedder, error)

NewTEIEmbedder creates a TEI embedder with the given endpoint and dimension. dimension must match the model loaded in TEI and the FalkorDB vector index.

func NewTEIEmbedderAuto

func NewTEIEmbedderAuto(baseURL string) (*TEIEmbedder, error)

NewTEIEmbedderAuto queries TEI for its model dimension and creates an embedder automatically.

func (*TEIEmbedder) Close

func (e *TEIEmbedder) Close() error

Close closes the HTTP client used by this embedder.

func (*TEIEmbedder) Dimension

func (e *TEIEmbedder) Dimension() int

Dimension returns the vector dimension this embedder produces.

func (*TEIEmbedder) Embed

func (e *TEIEmbedder) Embed(ctx context.Context, texts []string) ([][]float32, error)

Embed generates vectors for the given texts. It batches internally.

Directories

Path Synopsis
cmd
argus command

Jump to

Keyboard shortcuts

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