# @verevoir/context

In-process content + symbol cache for LLM context windows. Keyed `(sourceId, version, itemId)`. Tree-sitter symbol extraction + grep + a cached-read bridge to `@verevoir/sources`, all as optional subpath imports.

## Purpose

Lets an LLM agent navigate a codebase (or any versioned content source) without re-fetching the same files every turn. The cache is purpose-built for LLM-context flows: lazy population, cheap repeated lookups, symbol-aware indexing for files where that helps.

Pairs naturally with `@verevoir/sources` for the read side, but does not require it — consumers can populate the store from any source.

## Most consumers reach this via MCP

If you're driving an LLM agent and want cached source reads + symbol search as tools, run the `@verevoir/mcp` server — it wires `@verevoir/context/github` + `@verevoir/context/fs` under the hood, so its tools transparently get the in-process cache + `wrapWithCache`'s read-through-with-validation (default 10s TTL). See [`@verevoir/mcp`](https://github.com/verevoir/mcp) for client config; key setting `"alwaysLoad": true` keeps tools out of the deferred-tools pool.

Direct in-process use (below) is for: writing your own MCP server, customising `validationTtlMs`, sharing a single `ContextStore` across multiple wrapped sources, or non-MCP runtimes.

## Subpaths

- `@verevoir/context` — `ContextStore` (content + symbol cache), `grep`, `wrapWithCache` decorator over any `SourceAdapter`, `wrapWorkflowWithCache` decorator over any `@verevoir/workflows` `WorkflowAdapter` (board reads share the same store, so they park/restore with `serialize()`), key + symbol types. No external dependencies. Optional peer dep on `@verevoir/workflows` (type-only unless `wrapWorkflowWithCache` is called).
- `@verevoir/context/code` — tree-sitter `parseCode` (symbols + import/call edges), `parseSymbols`, `detectLanguage`, `findSymbols` / `edgesForItem` over the store. Languages: TypeScript/TSX/JavaScript, Python, Java, C#, Go, Scala, C, C++. Optional peer deps on the tree-sitter grammar packages.
- `@verevoir/context/github` — `wrapWithCache(github)` drop-in. Optional peer dep on `@verevoir/sources@^0.4.0`.
- `@verevoir/context/fs` — `wrapWithCache(fs)` drop-in. Optional peer dep on `@verevoir/sources@^0.4.0`.
- `@verevoir/context/notion` — `wrapWithCache(notion)` drop-in. Optional peer dep on `@verevoir/sources@^0.4.0` + transitively on `@notionhq/client`.

## Install

```bash
npm install @verevoir/context
# Optional add-ons:
npm install tree-sitter tree-sitter-typescript tree-sitter-javascript
# + any of: tree-sitter-python tree-sitter-java tree-sitter-c-sharp tree-sitter-go tree-sitter-scala tree-sitter-c tree-sitter-cpp
npm install @verevoir/sources
```

## Canonical usage

```ts
import { contextStore, grep, wrapWithCache, createContextStore } from '@verevoir/context';
import { findSymbols } from '@verevoir/context/code';
import { readFile } from '@verevoir/context/github';

// Cached source: read-through + freshness-validated.
const r = await readFile(env, 'https://github.com/acme/repo', 'README.md');

// Or wrap a custom adapter.
const cached = wrapWithCache(mySource, { validationTtlMs: 10_000 });

// Grep across cached items.
const hits = grep('Auth', { sources: [{ sourceId: '...', version: '' }] });

// Find symbols by name (lazy-parses tree-sitter on miss).
const syms = findSymbols('Auth', { sources: [{ sourceId: '...', version: '' }] });
```

## Freshness validation

`wrapWithCache` uses `SourceAdapter.isFresh` (added in `@verevoir/sources@0.3.0`) to validate cached entries instead of returning forever within the process.

- Cache hits inside `validationTtlMs` (default **10s**) serve without asking the source.
- After the window, the wrapper calls `source.isFresh(env, url, path, version, ref)`. True → touch cachedAt + serve cache. False → re-fetch.
- `validationTtlMs: 0` validates on every hit; `Infinity` never validates.
- The 10s default covers a tool-loop's burst of correlated reads; it's a dial to tune from observed cost vs staleness.

`store.getCached(key)` returns the full `{ content, version, cachedAt }` triple. `store.getContent(key)` returns just the content (used by `grep`).

## Key shape

```ts
interface IndexKey {
  sourceId: string;  // typically a URL
  version: string;   // git ref / etag / content hash; '' = default
  itemId: string;    // path within the source
}
```

## Symbol shape

```ts
interface SymbolEntry {
  name: string;
  kind: 'function' | 'class' | 'method' | 'interface' | 'type' | 'enum';
  startLine: number;
  endLine: number;
}
```

## Per-instance + singleton

Default singleton `contextStore` shared across imports. Isolated instances via `createContextStore()`. `grep`, `findSymbols`, `cachedReadFile` accept a `store` option to target an isolated instance.

## What this is NOT

- Not LSP. Structural symbols only; no type resolution.
- Not a fetch layer. Reads come from external sources (e.g. `@verevoir/sources`); the store caches what is handed to it.
- Not persistent. Per-process, in-memory.

## License

Apache-2.0.
