ADR 0024: Local-only deterministic project index
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-06-01 |
| Deciders | Project maintainers |
| Supersedes | — |
| Superseded by | — |
| Related ADRs | ADR-0023 (goal contract) |
Summary
@aidokit/core gains a buildProjectIndex(root) function that produces a
deterministic, local-only index of a project: its source files, exported symbol
names, test files, and docs. A new aidokit index build command writes the
index to .aidokit/index.json with stable sorted keys and 2-space indentation.
The index is built by a read-only filesystem scan. No external service, no model call, no network. It is an orientation aid — a fast, deterministic map of "what exists where" that agents and tooling can read instead of re-walking the tree.
Context
Agents repeatedly need a cheap answer to "where is symbol X defined?" and "what test covers this file?". Walking the tree on every question is slow and non-deterministic in output ordering. A committed-or-regenerated index gives a stable artifact the agent can grep without a filesystem walk, and gives tooling a single sorted source of truth.
The constraint (CLAUDE.md non-goals) is absolute: zero LLM calls, zero telemetry, zero network. So the index must be a pure deterministic scan.
Decision
1. buildProjectIndex(root) in @aidokit/core
Returns { files, exports, tests, docs }:
- files — every
.ts/.jsunderpackages/<pkg>/src(recursive, skippingnode_modules/dist/.git). - exports —
{ file, name }pairs extracted by regex fromexport (const|function|class|interface|type|enum) <Name>andexport { ... }(alias-aware). Test files are not scanned for exports. - tests — files matching
*.test.ts. - docs —
*.mdunderdocs/and.docs/.
All paths are project-root-relative with forward slashes; every array is sorted; export pairs are de-duplicated. The result is byte-stable across runs and across operating systems.
2. aidokit index build
Writes buildProjectIndex(cwd) to .aidokit/index.json via
JSON.stringify(index, null, 2) plus a trailing newline. Honors --json and
--quiet. Read-only scan; the only write is the index file.
Consequences
Positive
- Deterministic, hermetic, offline. Fits the zero-network principle exactly.
- Regex extraction is cheap and dependency-free — no TypeScript compiler API.
- One core function, one thin command; small surface.
Negative
- Regex extraction is approximate: it will miss dynamically-constructed exports and default exports (the repo bans default exports in core, so this is acceptable). It is an orientation aid, not a type-accurate symbol table.
Neutral
- The index is not auto-refreshed; it is regenerated on demand. Staleness is the user's responsibility, consistent with other emitted artifacts.
Alternatives considered
Alternative 1: TypeScript compiler API for exact symbols
Rejected — heavyweight, slow, and pulls a large dependency into a function that only needs a "what exists where" map. The regex pass is good enough for orientation and stays dependency-free.
Alternative 2: Index the whole tree, not just packages/*/src
Rejected for v1.0 — packages/*/src is the authored-source surface; build
output and fixtures would add noise. Scope can widen later via a non-breaking
option.
Validation
This decision is validated when:
- [ ]
buildProjectIndexreturns sorted, root-relative, forward-slash paths. - [ ] Test files appear in
testsand are excluded fromexports. - [ ]
aidokit index buildwrites.aidokit/index.jsonwith 2-space indent. - [ ] Output is byte-identical across repeated runs.