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

@adlc/core

v1.12.0

Published

Shared core for ADLC tools: LLM providers, git helpers, CLI conventions, ledgers, tickets, mutations.

Readme

@adlc/core

Shared library for all ADLC tools. Tools import from it and never keep a private copy of what it exports. When a second package needs a helper, promote it here: add a test in packages/core/test/, a declaration in index.d.ts, and no new runtime dependency. A defect in a core primitive is fixed here, with a regression test. The full rule is CONVENTIONS.md hard rule 2.

Import surface (from a tool at packages/<name>/):

import { complete, fan, fanProviders, extractJson, detectProvider, resolveModel, PROVIDER_NAMES } from '../../core/index.mjs';
import { git, gitDiff, changedFiles, isDirty, isGitRepo, coChange, pairKey, churn } from '../../core/index.mjs';
import { parseArgs, pass, gateFail, opError, printJson, readStdin, promptOnly } from '../../core/index.mjs';
import { ADLC_DIR, appendEntry, readEntries, ledgerPath, sha256, hashFiles } from '../../core/index.mjs';
import { TICKETS_PATH, loadTickets, validateTicket, topoSort, computeFloat, globMatch, inScope, scopesOverlap } from '../../core/index.mjs';
import { mutate } from '../../core/index.mjs'; // mutate.generateMutants / applyMutant / changedLinesFromDiff / OPERATORS

llm

  • detectProvider(env?, forceProvider?) → { name, apiKey, models } | null. Order: anthropic, openai, gemini, agy. Force with ADLC_PROVIDER (env) or the forceProvider arg (per-invocation, e.g. a CLI --provider flag — takes precedence over ADLC_PROVIDER).
  • PROVIDER_NAMES → ['anthropic', 'openai', 'gemini', 'agy'], for CLI validation of --provider/--providers.
  • resolveModel(provider, { tier, model }, env?) → model id. Tiers: cheap | mid | frontier. Override via ADLC_MODEL_CHEAP/MID/FRONTIER.
  • complete({ tier, model, system, prompt, maxTokens, provider?, onUsage?, cacheable? }, env?) → Promise<string>. Throws if no provider (tools must catch and offer --prompt-only). provider is a per-invocation override, additive only — omit it and single-provider auto-detect remains the default (cost/latency per ADR-0007). onUsage?: (usage) => void is an optional side-channel fired with { inputTokens, outputTokens, cachedTokens, provider, model, tier } when the provider reports usage (agy never does — there's nothing real to report). cacheable?: boolean (default false) marks the system/prompt as a prompt-cache candidate on providers that support explicit cache breakpoints (currently anthropic only) — only set it when the SAME {system, prompt} will be sent again; a cache write costs more than a plain input token and only pays off on a later read.
  • fan(opts, n, env?) → Promise<[{ ok, value | error }]> — n independent stateless completions of the SAME (auto-detected or opts.provider-forced) provider. opts.cacheable defaults to true here (unlike complete()) — fan() exists specifically to resend one prompt N times, exactly the shape a cache write pays for itself on. Pass cacheable: false to opt out. opts.onUsage, if given, fires once per resample.
  • fanProviders(opts, providerNames, env?) → Promise<[{ ok, value | error, provider }]> — one completion per DISTINCT named provider (e.g. ['anthropic', 'openai', 'gemini']), instead of N resamples of one provider. A named provider missing its API key surfaces as { ok: false } for that entry only. Does NOT default cacheable to true — each provider gets exactly one call, so there's no repeat to amortize a cache write against.
  • extractJson(text) → parsed JSON value from messy model output. Throws if none.

git

  • git(args[], opts?) → stdout string (execFileSync; throws on non-zero).
  • isGitRepo(cwd?), gitDiff(base?, cwd?), changedFiles(base?, cwd?), isDirty(cwd?).
  • coChange(limit?, cwd?) → { pairCounts: {'a b': n}, fileCounts } (logical coupling; commits touching >50 files skipped). Pair keys via pairKey(a, b) (sorted).
  • churn(limit?, cwd?) → { file: commitCount }.

cli

  • parseArgs(config) — node:util parseArgs with allowPositionals: true default.
  • pass(msg?) exit 0 · gateFail(msg, details?) exit 2 · opError(msg) exit 1.
  • printJson(obj), readStdin(), promptOnly(promptOrArray) (print prompt(s), exit 0).

Exit codes are the contract: 0 = gate passes, 1 = operational error, 2 = gate fails.

ledger (persistence at .adlc/)

  • appendEntry(name, entry, dir?) → appends to .adlc/<name>.jsonl.
  • appendEntries(name, entriesOrFactory, dir?) → appends a durable batch under one owner-token lock; factories receive the byte-exact locked ledger tail for safe sequence and hash-chain allocation.
  • readEntries(name, dir?) → { entries, skipped } — malformed lines reported, never swallowed.
  • sha256(content), hashFiles(paths) → { path: hash | null }.

Well-known ledger names: manifest (gate-manifest entries), findings (prosecution findings: { ts, tool, file, line, category, severity, desc, verdict }).

tickets (canonical logical store)

Schema (see lib/tickets.mjs header): { id, title, body, scope[], rails[], edges[{to, contract}], duration, category, budget }.

  • loadTickets(path?) → { tickets, errors } (reads legacy JSON or the sibling sharded store and validates ids, duplicate ids, unknown edges).
  • validateTicket(t) → errors[].
  • topoSort(tickets) → { order, cycle | null }. Edges mean "completes before edge.to".
  • computeFloat(tickets) → { floats: {id: n}, criticalPath: [ids], makespan } (CPM; duration default 1) or { error } on cycle.
  • globMatch(pattern, path) (*, **), inScope(ticket, path), scopesOverlap(a, b) (conservative).

mutate

  • mutate.OPERATORS — invert-comparison, bool-flip, null-return, off-by-one, logic-swap.
  • mutate.generateMutants(content, { targetLines?, maxMutants? }) → [{ line, operator, original, mutated }] (skips comments/imports/console lines).
  • mutate.applyMutant(content, mutant) → mutated content (throws if line content drifted).
  • mutate.changedLinesFromDiff(diffText) → { file: Set<newSideLineNo> }.

text (shared payload-capping helpers)

  • tail(str, maxChars = 4000) → the last maxChars characters of str, unchanged if already within limit. Hoisted here (was duplicated in consensus-fix) so every caller capping a prompt payload shares one implementation.
  • fence(label, content, maxChars, opts?) → wraps content in <<UNTRUSTED:...>>/<<END:...>> markers declaring it inert data, capped to maxChars (maxChars is required, not optional; there is no uncapped call). Truncation keeps the tail by default; pass { bias: 'head' } to keep the opening (spec-shaped content). opts must be a plain object whose only key is bias, and bias must be 'head' or 'tail'; anything else throws. Used by packages/fleet to fence prior build/gate/prosecution logs into a fix charter.

test-kit (@adlc/core/test-kit)

Fixture helpers for node:test suites; typed by lib/test-kit.d.mts.

  • tmp(t, prefix?) → a fresh realpath'd directory under os.tmpdir(), removed by a hook registered on t.after() (retrying transient ENOTEMPTY/EBUSY, FIXTURE_RM_OPTIONS). Fails closed: a t without a callable .after (none, null, a prefix string, an options object, or the context a describe-level before() receives) throws a TypeError and creates nothing.
  • gitRepo(t, options?) → { dir, git, g }: a repo inside tmp(t) with a test identity, commit.gpgsign=false, gc.auto=0 and gc.autoDetach=false (no detached maintenance child writing into .git during removal). options is { prefix, branch, userEmail, userName } or a prefix string. Same context rule as tmp.
  • createScope() → a context ({ after, dispose }) for fixtures that outlive one test callback: create in before(), await scope.dispose() in after(). Registering on a disposed scope throws.
  • withScopedContext(fn) → runs fn(ctx) with a fresh scope and disposes it when fn settles; for code that calls test functions outside a node:test callback.
  • runBin(binPath, args?, options?) → spawnSync of a Node script with DEFAULT_SCRUBBED_ENV (signing keys, bypass flags) removed unless allowEnv names them.