“Context compiler” for STAN — scans a repository and produces a deterministic dependency graph so an LLM can select the right files to read.
STAN is moving from a filtering model (“archive everything, then hide files”) to a selection model (“build a Map, then choose the Territory”).
This package builds the Map:
This package does not:
.stan/ state,Those concerns remain in @karmaniverous/stan-core (engine) and @karmaniverous/stan-cli (CLI adapter).
pnpm add @karmaniverous/stan-context
# or npm i @karmaniverous/stan-context
Node: >= 20
TypeScript: required for TS/JS analysis and must be provided explicitly by the host (see example below).
If TypeScript is missing or cannot be loaded from the injected inputs, generateDependencyGraph throws (this is not surfaced via errors).
Error propagation:
typescriptPath is provided but loading fails, the thrown error message includes the underlying loader failure, and the original error is preserved as error.cause (Node >= 20).typescript is invalid, there is no underlying loader error to propagate.import ts from 'typescript';
import { generateDependencyGraph } from '@karmaniverous/stan-context';
const res = await generateDependencyGraph({
cwd: process.cwd(),
typescript: ts,
config: {
includes: [],
excludes: ['dist/**'],
},
previousGraph: undefined,
});
console.log(res.stats);
// { modules, edges, dirty }
typescript / typescriptPath (required; host-provided)
typescript: an already-loaded TypeScript module instance, ortypescriptPath: an absolute path to a TypeScript entry module (for example require.resolve('typescript') from the host environment).typescript takes precedence.typescriptPath must be an absolute filesystem path (ESM host example: createRequire(import.meta.url).resolve('typescript')).typescriptPath fails, the thrown error preserves the original loader error as error.cause (Node >= 20).typescriptPath semantics (important for hosts):
typescriptPath is treated as an entry module file path that exports the TypeScript compiler API (it is not treated as a package root).require.resolve('typescript'), which typically resolves to a CommonJS file like .../typescript/lib/typescript.js..../tsserverlibrary.js) is also suitable as long as it exports the same TypeScript API surface.mod.default ?? mod when validating the injected TypeScript module; for typescriptPath, the file is loaded via require(), so the path MUST point to a CommonJS entry module.typescriptPath. If your host only has an ESM entrypoint, inject typescript (module injection) instead.typescriptPath loading to support both CJS and ESM by attempting import(pathToFileURL(...)) on ERR_REQUIRE_ESM, then normalizing mod.default ?? mod.previousGraph
nodeDescriptionLimit (default: 160)
GraphNode.description for TS/JS nodes based on module doc comments.....0 to omit descriptions.nodeDescriptionTags (default: ['@module', '@packageDocumentation'])
@-prefixed and match ^@\\w+$.hashSizeEnforcement (default: 'warn')
metadata.hash is present for a file node, metadata.size should also be present”.'warn' | 'error' | 'ignore'.maxErrors (default: 50)
errors entries (deterministic truncation).0 to omit errors.src/index.ts (repo-relative source)node_modules/.pnpm/pkg@1.2.3/node_modules/pkg/index.d.ts (physical external)C:/Users/me/dev/lib/index.d.ts (outside-root absolute, POSIX-normalized)node:fs (builtin)./missing-file (missing/unresolved specifier)index.ts) for named/default importsgraph.nodes keys are sorted for stable serialization.graph.edges is a complete map: every node key exists (empty [] means “analyzed; no outgoing edges”).This package also exports a helper for computing dependency selection closure membership and aggregate sizing from a graph plus dependency-state entries:
import {
summarizeDependencySelection,
type DependencyStateEntry,
} from '@karmaniverous/stan-context';
const include: DependencyStateEntry[] = [['src/index.ts', 2, ['runtime']]];
const summary = summarizeDependencySelection({ graph: res.graph, include });
console.log(summary.totalBytes, summary.largest, summary.warnings);
Contract (summary):
nodeId[nodeId, depth][nodeId, depth, edgeKinds][nodeId, depth, kindMask] (compact; runtime=1, type=2, dynamic=4)0 includes only the seed nodeN includes nodes reachable within N outgoing-edge traversalsedgeKinds filters which edges are followed (runtime | type | dynamic)SX using the same semanticsS \ XdefaultEdgeKinds: ['runtime', 'type', 'dynamic']dropNodeKinds: drops builtin and missing nodes by default (with warnings)0 (with warnings)totalBytes is the sum of metadata.size (bytes) for selected nodes where present0 (with warnings)estimatedTokens ≈ totalBytes / 4selectedNodeIds is sorted lexicographicallylargest is sorted by bytes descending, tie-break by nodeId ascendingwarnings is sorted lexicographicallyThis package ships an optional ESLint plugin subpath export:
import stanContext from '@karmaniverous/stan-context/eslint';
export default [
{
plugins: { 'stan-context': stanContext },
rules: {
...stanContext.configs.recommended.rules,
},
},
];
The default config enables stan-context/require-module-description at warn, and ignores test/test-like files by default (common *.test.*, *.spec.*, and test/tests/__tests__ directory patterns across TS/JS-like extensions).
To enforce the rule everywhere (including tests), override it explicitly:
import stanContext from '@karmaniverous/stan-context/eslint';
export default [
{
plugins: { 'stan-context': stanContext },
rules: {
...stanContext.configs.recommended.rules,
'stan-context/require-module-description': [
'warn',
{ ignorePatterns: [] },
],
},
},
];
Hosts that implement STAN “context mode” often need a compact, assistant-facing
dependency.meta.json that:
This package exports a helper to encode the standard DependencyGraph into a
compact meta form suitable for writing to .stan/context/dependency.meta.json:
import { encodeDependencyMeta, generateDependencyGraph } from '@karmaniverous/stan-context';
const res = await generateDependencyGraph({ cwd, typescript: ts });
const meta = encodeDependencyMeta({ graph: res.graph });
// write meta as minified JSON in the host
Compact meta notes (v2):
meta.v === 2meta.nnode.k is a numeric kind index (source/external/builtin/missing)node.e:
[targetId, kindMask] (explicit-only)[targetId, kindMask, resMask] (explicit/implicit/both)Hash note:
encodeDependencyMeta omits
content hashes to reduce context size.dependency.map.json) containing
canonical nodeId → source locator + size + full sha256.
This helper is pure (no FS) and does not manage .stan/ state; the host is
responsible for persistence, staging, and archiving workflows.BSD-3-Clause
Built for you with ❤️ on Bali! Find more great tools & templates on my GitHub Profile.