VelaWind · Projects

Anchorfile

A command-line tool that reads a repository and writes the context file an AI agent needs.
npm package · Source

A terminal on the left runs npx anchorfile init --verbose in a small TypeScript project and logs each detection: React, Next.js and Prisma, npm, Vitest, ESLint, the top-level folders and key files. On the right is the start of the CLAUDE.md it wrote: the project overview and the tech stack, with Docker and CI marked yes.

The problem

An AI coding agent starts every session knowing nothing about the repository it is in. Claude Code reads a CLAUDE.md file at the root for that: the stack, the scripts, the layout, the conventions. Written by hand, that file is accurate on the day it is written and then goes stale, because nothing connects it to the files it describes. A dependency changes, a script is renamed, and the agent goes on acting on the old description.

Why the obvious approach was not used

The obvious tool for writing a description of a codebase now is a language model: point it at the repository and ask for the file. That replaces one unchecked text with another. A model's summary can be wrong in ways that read as right, and a context file is the one document every later session trusts without looking. A wrong line there is repeated session after session.

So Anchorfile reads only what is on disk: package.json, lockfiles, config files, requirements.txt, pyproject.toml and the file tree. Every line it writes comes from a file a reader can open, and the same repository always produces the same output. The judgement calls a file tree cannot answer, what to avoid and which files matter most, are left as marked sections for a person to fill in.

One trade-off

Detection is deliberately literal. Frameworks are matched by exact package name, a tsconfig.json makes a project TypeScript without counting anything, and the walk that counts file extensions stops at a fixed depth and file count so a very large repository stays fast. The cost is that Anchorfile misses things a model would have guessed. The reason to accept it is that a miss is visible: the file says "None detected" and a person can correct it, while a confident wrong guess looks exactly like a right one.

What should change

The package was on npm before it had a single test. The suite came later: fixture repositories for each kind of project the README says it reads, the documented limits pinned so they change only on purpose, a snapshot of the emitted file, and the built CLI run end to end. Writing it found one defect straight away: top-level folders were listed in whatever order the filesystem returned them, so the same repository could produce two different files on two machines. For a tool whose whole claim is that its output follows from the files, that is the wrong defect to have shipped.

The suite then made it safe to remove three of the limits it had pinned. Dependencies are now read from pyproject.toml as well as requirements.txt, ESLint flat config is recognised, and the language is counted across the tree instead of one folder. Turning those tests around found a second defect: .ts and .tsx files were counted as two languages, so a project split between them could lose to fewer JavaScript files.

What remains: the pyproject.toml reader is a small one and skips Poetry dependency groups, a Poetry project is still reported as using pip, and Python dependencies are not yet listed in the generated file's dependency section.

The rest of this page is fetched from the Anchorfile README at build time and rendered here, so it is maintained in the repository and not in this site. It covers the architecture, the tests and, under "Known limits", what does not work.

Architecture

654 lines of TypeScript in five files, with detection and emission kept apart:

The split is what makes the tool checkable. The scanner is a function of a directory, so each kind of project it claims to understand is a small fixture directory in test/fixtures/, and its report is compared field by field with what the README says it detects. The generator is a function of one object, so its whole output is pinned by a snapshot.

Tests

npm test builds dist/ and runs 58 Vitest tests in 4 files:

npm run typecheck checks the source and the tests under strict mode. .github/workflows/ci.yml runs typecheck, test and npm pack --dry-run on every push and pull request to main. There is no linter.

Writing the fixture tests turned up one defect: top-level folders were listed in the order the filesystem returned them, which differs between machines, so the same repository could produce two different files. They are now sorted. Rewriting language detection turned up a second: .ts and .tsx files were counted as separate extensions, so a project split between them could lose to fewer .js files. They now count as one language.

Known limits