codemogger

repository·main·Indexed 43 days ago

https://github.com/glommer/codemogger

A local code indexing library for AI coding agents featuring tree-sitter semantic chunking and combined vector and full-text search (FTS) via SQLite and Turso. It provides a CLI, a TypeScript SDK, and an MCP server implementation to enable natural language and precise identifier searches over codebases without external APIs.

Tokens
9.4K
Snippets
37
Records
45
Agent score
55%

What's inside codemogger

  1. How codemogger indexing and searching works

    main

    Codemogger follows a five-step pipeline to make code searchable:

    1. Scan: Walks the directory, respects .gitignore, and detects languages via file extensions.
    2. Chunk: Uses tree-sitter (WASM) to parse files and extract top-level definitions (functions, structs, classes, impl blocks). Items exceeding 150 lines are split into sub-items.
    3. Embed: Encodes chunks using a local embedding model (e.g., all-MiniLM-L6-v2).
    4. Store: Saves chunks and embeddings into a single SQLite database using Turso, utilizing FTS (Full-Text Search) and vector extensions.
    5. Search: Supports two modes:
      • Semantic: Uses vector cosine similarity for natural language queries.
      • Keyword: Uses FTS with weighted fields for precise identifier lookups.

    Indexing is incremental; only files with changed SHA-256 hashes are re-processed on subsequent runs.

  2. Quick start with the codemogger CLI

    main

    The CLI allows you to index a project and perform searches immediately.

    1. Index a project: Scans the directory and creates a local database.
    2. Search: Performs semantic or keyword searches over the indexed code.

    Note: It is recommended to add the .codemogger/ directory to your .gitignore to avoid committing the local database.

    # Index a project
    codemogger index ./my-project
    
    # Search
    codemogger search "authentication middleware"
  3. Integrate codemogger as an MCP server

    main

    You can add codemogger to your coding agent's Model Context Protocol (MCP) configuration (e.g., Claude Code, OpenCode). This exposes three tools:

    • codemogger_search: Semantic and keyword search over indexed code.
    • codemogger_index: Index a codebase for the first time.
    • codemogger_reindex: Update the index after modifying files.

    Add the following to your MCP config:

    {
      "mcpServers": {
        "codemogger": {
          "command": "npx",
          "args": ["-y", "codemogger", "mcp"]
        }
      }
    }
  4. Understand the codemogger database schema

    main

    The codemogger index is built on a relational schema consisting of three core tables and dynamic Full-Text Search (FTS) tables.

    Core Tables

    • codebases: Tracks indexed projects. Key fields include root_path (unique identifier for the project directory) and name.
    • chunks: Stores the actual code segments. It contains metadata such as language, kind (e.g., function, class), name, signature, snippet, and line numbers. It also supports vector search via the embedding BLOB.
    • indexed_files: Tracks the state of individual files within a codebase to facilitate incremental updates. It stores file_path, file_hash, and chunk_count.

    Full-Text Search (FTS)

    To ensure performance, codemogger uses per-codebase FTS tables. These tables are dynamically named using the pattern fts_{codebaseId} and index the name and signature fields of code chunks. The search weights are configured to prioritize name (weight 5.0) over signature (weight 3.0).

  5. How CodeIndex manages the database lifecycle

    main

    The CodeIndex class manages a persistent Store.

    • Initialization: The store is lazily opened via getStore() when the first operation (index, search, etc.) is called.
    • Verification: Before searching, the index performs a verifySearchable check to ensure the database file is not locked by another process and contains valid data (detecting issues like missing WAL files).
    • Cleanup: Always call await index.close() when finished to properly close the database connection and release locks.
  6. Use codemogger as an MCP server

    main

    Codemogger can be run as a Model Context Protocol (MCP) server, exposing code indexing and semantic/keyword search as tools for AI agents. This allows an AI to navigate, understand, and search through a codebase using natural language or precise identifiers.

    The server is typically started via the CLI mcp subcommand. Once running, it provides three primary tools: codemogger_search, codemogger_index, and codemogger_reindex.

    Workflow:

    1. Index: Use codemogger_index on a directory to perform the initial scan and embedding generation.
    2. Search: Use codemogger_search to find relevant code chunks.
    3. Maintain: Use codemogger_reindex after making significant changes to the codebase to keep the index fresh.
  7. Use the codemogger SDK

    main

    The codemogger SDK allows you to embed code indexing directly into your TypeScript applications. The SDK is model-agnostic; you must provide your own embedding function.

    Key steps:

    1. Define an embedder function that takes string[] and returns Promise<number[][]>.
    2. Instantiate CodeIndex with a dbPath, your embedder, and an embeddingModel name.
    3. Call .index(path) to process a directory.
    4. Use .search(query, options) to find code using either semantic or keyword modes.
    5. Call .close() when finished.
    import { CodeIndex } from "codemogger"
    import { pipeline } from "@huggingface/transformers"
    
    // Load embedding model (runs locally, no API keys)
    const extractor = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2", { dtype: "q8" })
    
    const embedder = async (texts: string[]): Promise<number[][]> => {
      const output = await extractor(texts, { pooling: "mean", normalize: true })
      return output.tolist() as number[][]
    }
    
    const db = new CodeIndex({
      dbPath: "./my-project.db",
      embedder,
      embeddingModel: "all-MiniLM-L6-v2",
    })
    
    await db.index("/path/to/project")
    
    // Semantic: "what does this codebase do?"
    const results = await db.search("authentication middleware", { mode: "semantic" })
    
    // Keyword: precise identifier lookup
    const results = await db.search("BTreeCursor", { mode: "keyword" })
    
    await db.close()
  8. Reference the codemogger CLI commands

    main

    The following commands are available in the codemogger CLI:

    • index <path>: Index a directory or project.
    • search <query>: Search the indexed codebase.
    • list: List all currently indexed codebases.

    Example usage:

    codemogger index ./my-project
    codemogger search "authentication middleware"
    codemogger list
    # Index a directory
    codemogger index ./my-project
    
    # Search
    codemogger search "authentication middleware"
    
    # List indexed codebases
    codemogger list
  9. Search code with CodeIndex.search()

    main

    The search(query, opts?) method allows you to find relevant code chunks using three different modes:

    • semantic: Uses vector search for natural language or conceptual queries.
    • keyword: Uses Full-Text Search (FTS) for precise identifier or term lookups.
    • hybrid: Combines both semantic and keyword results using Reciprocal Rank Fusion (RRF).

    Use SearchOptions to control the limit, threshold (minimum score), and whether to includeSnippet in the results.

    const results = await index.search('how is authentication handled?', {
      mode: 'hybrid',
      limit: 10,
      threshold: 0.5,
      includeSnippet: true
    });
    
    for (const res of results) {
      console.log(res.score, res.chunk.snippet);
    }
  10. Use localEmbed for local text embeddings

    main

    The localEmbed function provides a way to generate vector embeddings for text locally using the all-MiniLM-L6-v2 model.

    Key characteristics:

    • Model: Uses all-MiniLM-L6-v2 (384 dimensions).
    • Automatic Download: The model (~22MB) is downloaded on the first use and then cached locally.
    • Memory Management: It processes texts in batches of 128 to balance throughput and prevent Out-Of-Memory (OOM) errors on typical hardware.
    • Output: Returns an array of number arrays (number[][]), where each inner array represents the embedding vector for the corresponding input text.
    import { localEmbed } from "./path/to/local";
    
    const texts = ["Hello world", "Codemogger is great"];
    const embeddings = await localEmbed(texts);
    // embeddings is number[][]
    console.log(embeddings[0].length); // 384
  11. Format search results as JSON with formatJson()

    main

    The formatJson function converts search query metadata and results into a structured, pretty-printed JSON string. This is useful for creating machine-readable outputs or API responses from search operations.

    The resulting JSON object follows the JsonOutput interface structure:

    import { formatJson } from './src/format/json.ts';
    
    // Example usage:
    const query = "example search";
    const results = [{ /* ... SearchResult objects ... */ }];
    const elapsedMs = 42;
    
    const jsonString = formatJson(query, results, elapsedMs);
    console.log(jsonString);