npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@archangel-tools/agent-ctx

v0.10.0

Published

Engine-independent context compiler for coding agents. One neutral source (.agent/ + context repos) compiles to CLAUDE.md, GEMINI.md, and engine configs.

Readme

agent-ctx

An engine-independent context compiler for coding agents. It takes a neutral source — .agent/ folders plus context repositories — and compiles it into engine-specific artifacts: CLAUDE.md for Claude Code, GEMINI.md for Gemini CLI. One source → many engines, no vendor lock-in.

It compiles, and does nothing else. Scaffolding a layer, advising on its structure, and delivering skills into an organization all need judgement and a human at the boundary, so they are skills — not commands. What is left here is what must be deterministic, verifiable, and runnable with no model and no network: the CI gate, the pre-commit rebuild, and the first git clone by someone who has never heard of this tool.

agent-ctx knows nothing about any particular content backend. A content backend (Notion, a wiki, anything) is just one of the MCP servers described in the layers, which the tool renders into engine config. It compiles a map; the live content is read by the agent at runtime through MCP, never compiled.

See CONTRACTS.md for the full format contract — it is the core document every component refers back to.

Install

npm install -g @archangel-tools/agent-ctx
# or, from a clone:
npm install && npm run build

Requires Node 22.5+ (the mirror uses node:sqlite). The published package is a pinned dependency for consuming repos and CI — it is not vendored/copied into each repo.

Public on purpose. The tool is MIT, holds no secrets, and knows nothing about any organization's content — it compiles whatever layers it is pointed at. A private registry would add an authentication step to every consumer (container, CI job, laptop) and buy nothing, because the thing worth protecting is the layers, and those are protected by git access. Isolation between organizations is enforced there, not here.

A version is published only by pushing a v* tag; the release workflow refuses a tag that disagrees with package.json. main is not a release channel, so a consumer pinning a version is pinning something that was deliberately cut.

The layer model

agent-ctx composes the three layers that are properties of the repository: org / solution / repo.

  • org<org>/agent-context. Holds _schema.md (the lens, sorts first), solutions/<name>/, skills/, mcp/, and research/ (a working zone, NOT a layer).
  • solution<org>/solutions/<name>/: operational material only (conventions, skills, subagents) plus notion-ref.md. The durable unit of value. The term is solutions/, never projects/.
  • repo — the workplace's own .agent/.

There is a fourth layer conceptually — personal — but agent-ctx does not build it. See personal is outside the tool.

Isolation between orgs is enforced by git access, not engine privacy. agent-ctx is open and holds no secrets. No access → the build fails closed.

Manifest

A directory with .agent/manifest.yaml is a compilable workplace.

extends:
  - org: acme
  - solution: billing        # → <org>/solutions/billing/
sources:
  org:acme:
    repo: https://github.com/acme/agent-context.git
    ref: v2026.06.1          # a pin = reproducibility
compose:
  precedence: [repo, solution, org]
  context: [repo, solution, org]   # optional — which layers contribute PROSE
  skills:
    exclude: [deliver-skill]       # optional — skills this place declines
targets: [claude, gemini]
decisions_cache: false             # optional — embed the org's Active decisions

Org layers with a sources entry are cloned to a cache before composition (the ref is resolved to a concrete SHA, recorded in the artifact header). A local --registry path is an override over org sources — iterate without committing; no clone happens.

compose.context, compose.skills.exclude and decisions_cache are optional and each defaults to what a manifest written before it meant: every layer contributes prose, every skill is installed, and no decisions are embedded. They are explained below — the first under a place above other places, the second under a place declines a skill it cannot use, the third under memory splits by volatility.

A place above other places bounds its prose

compose.context names the layers whose context docs are composed. Omit it and that is compose.precedence — all of them.

Narrow it only for a place that is a parent directory of other places. Claude Code reads CLAUDE.md up the tree, so such a place lends its prose to every session held beneath it, including another organization's. The constellation root is the case that produced the field: it sets context: [repo], because inlining the vendor's org layer there would hand a client's session the vendor's schema lens and the ids of a backend that session must never write to.

Capabilities are deliberately unaffected — skills, subagents, MCP servers and hooks still come from every layer in precedence. They are read per project and do not travel upward, so they leak nowhere, and a root that wants log-activity should have log-activity. Where a place wants fewer skills than its layers carry, the key for that is the next one.

A place declines a skill it cannot use

A skill costs its name and description in the standing prefix of every request made in that place, whether or not it can ever fire there. A skill whose work lands in another repository — a rollout across an organization's layer, a delivery from a catalog shelf — cannot fire in a workplace at all, and still bills it. compose.skills.exclude lets the place decline it by name:

compose:
  skills:
    exclude: [deliver-skill, schema-generation]

Exclude-only, and never an allow-list: a skill added upstream arrives everywhere until a place declines it deliberately. Nothing is deleted — the skill stays in the layer and stays installed in every place that did not decline it, so declining is a statement about where a description is paid for, not about what the organization can do. One line and a rebuild puts it back.

Two mistakes are reported rather than guessed at. An entry that matches no installed skill — the shape of a skill renamed upstream, where the place goes on believing it declined something. And an entry declining a skill whose compiled .claude/skills/<name>/ directory something else this place composes names: a hook command, an MCP server's command, a subagent's prompt, another skill's body, or a context doc. The build prunes that directory, so a hook whose script is missing fails on every event and a document that named a file there sends its reader to open nothing.

An allow-list is refused, not ignored: compose.skills.include fails to load, by name, rather than parsing and doing nothing.

personal is outside the tool

The manifest is committed and identical for everyone. org and solution are properties of the repository; personal is a property of the person. Naming personal in a committed manifest breaks in three ways: a colleague can't build (no access to your private layer → fail-closed), your preferences leak into the whole team's artifact, and CI — which has nobody's personal — can't build at all.

So agent-ctx doesn't know about personal at all. personal in extends or in sources is a validation error — the build fails rather than quietly producing an artifact that only one person can reproduce.

Personal is handled by a different system, one layer below:

  • your dotfiles (chezmoi / stow / a dotfiles repo) own ~/.claude/CLAUDE.md;
  • the engine applies it automatically, in every project, on top of the project CLAUDE.md that agent-ctx generates.

Personal still wins on precedence — that's the engine's doing, not ours. agent-ctx compiles what the team shares; your dotfiles compile what is yours.

Commands

# Compile a place into the committed artifacts (repo + solution + org):
agent-ctx build ./work --registry ./registry

# CI gate — writes nothing, exits non-zero if the artifact is stale:
agent-ctx build ./work --registry ./registry --check

# Provenance & freshness:
agent-ctx status ./work          # built SHA vs live source, plus unbuilt worktrees
agent-ctx refresh ./work         # re-fetch sources, rebuild, show diff (no commit)
agent-ctx cache clear            # drop the source cache

# The read side (see below):
agent-ctx mirror ./work          # pull the org's backend into a local sqlite cache
agent-ctx recall ./work          # print what the artifact cannot know (fail-soft)
agent-ctx audit ./work           # check the decision log's integrity over that cache
agent-ctx build ./work --decisions-cache   # embed the decisions for this one build

Flags default from the environment: AGENT_REGISTRY, AGENT_CACHE, AGENT_STATE. The cache defaults to the OS cache directory, the mirror state dir to the XDG state directory (never a hardcoded ~).

--decisions-cache and --state-dir are accepted by build and refresh; recall takes --org, --scope, --limit and --state-dir, and derives the org and the scope from the place's manifest when they are not given. audit takes --org and --state-dir and derives the org the same way.

The read side: mirror, recall and audit

The compile guarantees are unchanged — mirror and recall sit outside them. mirror is the ONLY command that speaks a backend API (Notion first, behind a narrow adapter), driven by the org's own ## Pattern bindings in _schema.md; it pulls the org's activity log, decisions and knowledge into one sqlite file per org under the state dir, outside every repository. The token comes from NOTION_TOKEN at call time and goes to HTTPS and nowhere else. recall reads that cache and is fail-soft: any failure is a one-line HTML-comment diagnostic and exit 0 — a missing cache must never block a session. The mirror is a dated cache; the backend always wins. See the read-side section of CONTRACTS.md.

audit reads that same cache and does the opposite of fail-soft. It runs the five decision-log integrity checks specified by the decisions-consistency-check skill — supersession pointing back in time, an Active record replaced by an older one, one live answer per topic, chains that resolve, and projections that keep their source — and prints the reach it actually got to before any finding, naming every check it could not make in full. It classifies and never repairs: each finding carries the records, the remedy and who may perform it. Because an audit that cannot read its input and exits 0 has reported a clean log it never opened, a missing or unreadable mirror is exit 2 with a diagnostic, never a report; findings are exit 1 and a sound log exit 0.

A sync pulls only what changed since the last one (with a margin, because the backend reports edit times to the minute), so refreshing an unchanged backend costs one empty page per database however large the history has grown. Nothing is aged out — the whole history is kept. The one thing an incremental pull cannot see is a DELETION, because the backend returns no tombstone for a trashed row, so a full unfiltered pass runs on the first sync, on agent-ctx mirror --full, and automatically once a week.

Memory splits by volatility

Decisions used to reach a session one way only: the recall hook printed all of them, at every start, and every session paid for the lot again. They are the stable half of an org's memory — a decision recorded in June is the same text in August — and stable content billed per session is content in the wrong place.

So the artifact carries them. With decisions_cache: true in the manifest, agent-ctx build appends the org's in-scope Active decisions as a synthetic org context doc, dated by the mirror's own snapshot rather than by "now". It is a property of the place, not an argument of the command, on purpose: a block that appears or disappears depending on who typed what makes two people building the same source produce different bytes, and --check stops meaning anything. The --decisions-cache flag still exists on build and refresh and ORs with the field — it is for trying the block out before a manifest commits to it.

recall then prints only the delta. It looks in the place's artifact for the decisions-cache: marker and shows the decisions dated on or after that snapshot, plus recent activity. The date comparison is by day and deliberately generous: a decision recorded on the day of the build is reprinted rather than risked, because printing one row twice is cheap and dropping one that landed after the build is not. A place whose artifact carries no cache gets every Active decision from recall, exactly as before — the split degrades to the old behaviour, never to silence. Measured on this repository, the recall block went from 5797 to 2059 bytes, with 28 of the 30 in-scope decisions moving into the artifact where a rebuild pays for them once.

Both halves are scoped the same way, from the manifest: the solution named in extends is the place's decision scope, so a rule governing a sibling tool stops arriving. A scope matches its own decisions plus general and unscoped ones: narrowing a view never hides what applies everywhere. recall --scope and --org override the derivation for a one-off look at somebody else's slice.

The price of the cache is that the mirror db becomes an input to the build. A rebuild after each agent-ctx mirror writes a fresh as-of line even when no decision changed, and a machine with no mirror for the org builds the artifact without the block at all — a capability warning, an otherwise normal build, and a --check that reports the committed artifact as stale. Rebuild a place with decisions_cache: true where its mirror lives.

A decision written down leaves both halves

EncodedIn is an optional field on the decision-log role: it names the layer file where that decision's rule is now stated as prose. When it is set, the decision drops out of the artifact's cache and out of recall — the agent already reads the rule in the layer, and saying it twice costs tokens and adds nothing.

That turns what is left into a backlog rather than a bill: the block's remaining length is "decided, but not yet written down". The way to shorten it is to write the rule into a layer doc and record where, not to trim the output.

EncodedIn is not Artifacts/Commits. Those say the rule is implemented — some code obeys it. EncodedIn says the rule is legible — an agent reading the compiled context will find it stated. A decision can be either, both, or neither, and only the second one makes repeating it redundant.

The org's ## Pattern bindings may alias the field like any other; in the mirror it is the decisions.encoded_in column. A mirror written by an older engine gains that column by an additive ALTER the next time agent-ctx mirror opens it — the mirror is a disposable cache, so that is the whole migration. Until then recall over the old db fails soft with its usual diagnostic, and a build asking for the decisions cache fails on it; one agent-ctx mirror run settles both.

Skills may carry assets

A skill is not always one file. A vendored third-party skill points its body at scripts and reference documents by relative path, and those paths only resolve if they sit next to the rendered skill. Put them in skills/<name>/files/:

skills/docx/
  skill.yaml
  body.md
  files/
    scripts/validate.py
    reference.md

They are copied verbatim into .claude/skills/<name>/, read recursively and sorted for determinism, and pruned per file when the source drops them. Assets are TEXT: one that is not valid UTF-8 aborts the build rather than being written back mangled. Gemini has nowhere to put them — a command is a single prompt — so it emits a capability warning instead of dropping them silently (invariant 5).

Delivering somebody else's skill into an organization — vendoring it at a pinned upstream commit, stamping provenance, updating it later without clobbering local edits — is not this tool's job. skill.yaml is passthrough, so provenance markers written by a delivering skill ride along inertly.

What is compiled

Only these, and only from a layer's root (invariant 1 — subfolders like research/ never enter context):

| Source | Model | Claude artifact | Gemini artifact | | -------------------------------------- | ---------- | ----------------------------------- | ------------------------------ | | root *.md (minus generated names) | contextDoc | CLAUDE.md (layered) | GEMINI.md (layered) | | skills/<n>/skill.yaml + body.md | Skill | .claude/skills/<n>/SKILL.md | .gemini/commands/<n>.toml | | skills/<n>/files/** | SkillFile | .claude/skills/<n>/** | — (capability warning) | | subagents/*.yaml | Subagent | .claude/agents/<n>.md | .gemini/settings.json agents | | mcp/*.yaml | McpServer | .mcp.json | .gemini/settings.json mcp | | hooks/*.yaml | Hook | .claude/settings.json | — (capability warning) |

Gemini has no lifecycle hooks, no disallowed-tools, and nowhere to put skill assets; those become capability warnings, never silent drops (invariant 5).

A context artifact over 600 lines of composed prose opens with a generated ## Contents index of every # and ## heading, each top-level entry naming the source file and layer it came from. There is no manifest key: length is a property of the document, not a preference, and an index regenerated with the document is the one kind that cannot go stale. CONTRACTS.md states it in full.

Determinism & committed artifacts

Identical source → byte-for-byte identical artifact: no timestamps, sorted keys, and a source-hash (sha256, 12 chars) plus the resolved source SHAs in the header. A repeat build is a no-op; --check is the CI "build is clean" gate. The header also names the engine that wrote it, read from the package's own package.json so that a version nobody remembered to edit cannot make it lie. A place with decisions_cache: true has one input more than its layers — its mirror db; see memory splits by volatility.

The artifact contains repo + solution + org and nothing personal, so it is identical for everyone and never ping-pongs between people.

Commit the artifacts next to their source and wire the harness in templates/:

  • gitattributes.sample.gitattributes marks artifacts linguist-generated (copy it in by hand; there is no init command, and CONTRACTS says why).
  • pre-commit.sample — soft guarantee: rebuild + git add in the same commit.
  • ci-snippet.yaml — hard guarantee: build --check fails on stale artifacts.

The six invariants

  1. source/artifact separation — artifacts are never read back as source; the loader skips generated names and reads only a layer's root *.md.
  2. fail-closed — an unreachable source aborts the build with a clear error, writing nothing.
  3. determinism — identical source → identical bytes, and identical TREES: pruning is per file and recursive, so a removed asset leaves nothing behind.
  4. multi-story — any dir with a manifest compiles; an org repo can be both a workplace and a source.
  5. capability warnings — anything an engine can't represent is surfaced, not dropped.
  6. auth delegation — the tool never stores, reads, parses, passes, or logs tokens; cloning is delegated to the system git and its credential helper.

Development

npm install
npm test            # vitest — git is injected, no network
npm run typecheck
npm run build
npm run dev -- build ./work --registry ./registry

Library API is exported from the package root (buildPlace, Resolver, composeLayers, adapters, …) for embedding in other tooling. The read side is exported too: activeDecisions, solutionNameOf, decisionsCacheDoc, DECISIONS_CACHE_MARKER. One breaking change in 0.5.0 — the decision query is an options object, activeDecisions(db, { scope, unencodedOnly }), where 0.4 took a bare scope string, because scope is no longer the only filter.