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

@amsterdamdatalabs/enact-repo-controls

v0.3.7

Published

Repo-local git workflow conventions (enact-m5 git hooks, commit rules, coverage ratchet, push checks, CI scripts) installed into a repository via core.hooksPath.

Downloads

1,050

Readme

enact-repo-controls

Repo-local git workflow conventions — the enact-m5 git hooks, commit-message rules, coverage ratchet, push checks and CI hygiene scripts — packaged as one global binary and installed into any repository through git's own core.hooksPath.

Scope: workflow/git conventions only. Agent setup lives in enact-extensions; the agent guard lives in enact-hook. No lefthook, no husky, no npm lifecycle magic.

How it works

target repo (committed)                      global package (npm i -g @amsterdamdatalabs/enact-repo-controls)
.workflows/hooks/pre-commit   ─┐              enact-repo-controls hook <name>
.workflows/hooks/commit-msg    ├─ sh stub ──▶    ├─ validates enact-config.toml [controls]
.workflows/hooks/pre-push      │  exec           ├─ checks bash >= 4
.workflows/hooks/post-commit  ─┘                 └─ runs scripts/hooks/<name>
enact-config.toml [controls]   (config, shared file — see below)   (package-owned script, config via env)
.workflows/repo-controls.lock.json (hashes)
local .git/config: core.hooksPath = .workflows/hooks
  • Stubs are POSIX sh, contain no paths, and fail loudly (with the install command) when enact-repo-controls is not on PATH — never a silent skip.
  • Bundled scripts in scripts/hooks/ are package-owned implementations. They originated in enact-m5; commit 20ec790 removed the former byte-copy tree and manifest when mechanism moved into this package. Their current behaviour is exercised by tests/hooks-e2e.test.ts and focused unit tests.
  • Logs (gitignored): .enact/logs/enact-repo-controls-activity.log gets a CSV row for every hook invocation; .enact/logs/enact-repo-controls-deny.log gets only FAIL rows. The owner-qualified names let Enact tools share one repository-local log root.

Requirements

  • bun >= 1.4 on PATH — runtime of the CLI and of the bundled enact-m5 TypeScript scripts (coverage ratchet, push lock, azure-devops-push.ts all use Bun.* / bun:sqlite).
  • bash >= 4 first on PATH — the enact-m5 scripts use ${var,,} (commit-msg) and mapfile (repo-hygiene). macOS ships /bin/bash 3.2: install a newer bash (brew install bash) and make sure GUI git clients see it. doctor and every hook run check this and fail loudly on 3.x.
  • git, and npm only to install the package.
  • Agent Vault — only when [controls.release] is declared. doctor needs agent-vault and reads only the canonical Agent Vault file: Linux /etc/enact/agent-vault.env (root-owned) or macOS $HOME/.config/enact/agent-vault.env (owned by the invoking user). It must be a regular 0600 file containing exactly one non-empty plain AGENT_VAULT_ADDR=, AGENT_VAULT_TOKEN=, and AGENT_VAULT_VAULT= assignment; comments and blank lines are the only other permitted content. The file wins over inherited values, is parsed without being sourced, and is never loaded for repositories without [controls.release]. Never place those values in a repository .env or ~/.codex/.env.
  • uv — only if [controls.compliance].enabled is non-empty; see below.
  • ast-grep — optional; only 3 of the compliance registry's 93 checks use it, and the Python runner degrades those specific rows gracefully (not a hard failure) when it is absent. doctor warns, never fails, on a missing ast-grep.

Why bun (and not node)

The package is distributed with npm i -g @amsterdamdatalabs/enact-repo-controls, but the scripts it must run verbatim are enact-m5's bun scripts (bun .../coverage/command.ts, #!/usr/bin/env bun push wrapper, Bun.spawnSync, bun:sqlite). Bun is therefore a hard runtime dependency no matter what the CLI is written in; writing the CLI in bun keeps one runtime, lets the bin entry run TypeScript directly (no build step, no dist/ drift), and gives the 100% lines/functions coverage gate natively via bunfig.toml. npm is only the installer: npm i -g links bin/enact-repo-controls.ts (shebang #!/usr/bin/env bun) and installs the one runtime dependency (istanbul-lib-coverage, imported by the ratchet).

A second runtime — only if compliance is enabled

package/enact-compliance is a separate, bundled Python project (managed by uv, which provisions its own Python 3.14+ — no system Python required). This is not a contradiction of the "one runtime" point above: bash ≥ 4 is already a second hard external dependency today, for the bundled enact-m5 hook scripts — uv/Python simply joins bash in that same "externally invoked bundled tool" category. Nothing about the CLI's own runtime changes; enact-repo-controls itself still runs in-process under bun exactly as before.

uv is required only when [controls.compliance].enabled is non-empty (run compliance will fail loudly, naming uv, if it's absent and needed) — installing this package and running every other command (install, uninstall, doctor with compliance disabled, hook, and the other run tools) never touches it.

The UI gate design and the source-to-sink proof required for genuine appearance centralization are documented in docs/ui-centralization-compliance.md.

CLI

enact-repo-controls install   --repo <path> [--dry-run]
enact-repo-controls uninstall --repo <path> [--dry-run]
enact-repo-controls doctor    --repo <path> [--json]
enact-repo-controls hook <pre-commit|commit-msg|pre-push|post-commit> [git hook args]
enact-repo-controls promote --repo <path>
enact-repo-controls run <azdo-push|coverage|repo-hygiene|validate-pr-metadata> --repo <path> [-- args]
enact-repo-controls run compliance --repo <path> [-- <gate-id or domain.name> | --summary | --files LIST | --log-dir DIR]
enact-repo-controls --version | help

install requires an existing root enact-config.toml: that is the central management marker. It deletes and rebuilds the complete Enact-owned .workflows/ root, then appends its [controls] table only when that table is missing. It appends the single shared root ignore /.enact/logs/ and /.coverage-cache/ to .gitignore, writes the lock, and sets core.hooksPath=.workflows/hooks with git config --local.

  • Re-running is state-idempotent: the rebuilt root is byte-identical.
  • --dry-run prints every action and changes nothing (files or git config).
  • Extra, altered, missing, and retired managed files below .workflows/ are deleted or repaired on the next install. Pre-hard-cut log evidence is kept in place as quarantine; it is never read, written, copied, moved, or deleted.
  • Written files contain no absolute paths and no $HOME.

uninstall removes the authoritative .workflows/ projection and deactivates its hook path. Canonical logs, their root ignore, quarantined log evidence, and runtime caches remain. enact-config.toml itself is never deleted — only the [controls] span this package appended is ever spliced back out.

doctor is read-only and exits 1 on any failure: bash >= 4, bun, binary on PATH, the bundled compliance package present at its packaged path, repository root, [controls] config validity, (warning) coverage/coverage-summary.json exists whenever pre_push.coverage_ratchet is enabled, [controls.compliance] config validity, uv on PATH (only checked once [controls.compliance].enabled is non-empty; otherwise reported ok unconditionally, same off-by-default posture as the coverage-summary check), (warning only, same conditioning) ast-grep on PATH, lock, each stub (present, unedited, executable), core.hooksPath, .gitignore lines, and (warning) lock version skew. When [controls.release] is declared, it also derives the configured origin remote, loads the validated canonical Agent Vault file before reading Azure DevOps, and records separate fail-closed receipts for the HTTPS handle, required owner reviewer, minimum-review reset, and build policy.

run executes a bundled enact-m5 tool against the repository with the config exported: azdo-push (Azure DevOps push with push lock + behind-origin refusal), coverage <check-push|prewarm-push|status|unlock|...>, repo-hygiene (CI entrypoint), validate-pr-metadata (Azure PR title/description policy).

run compliance runs the bundled enact-compliance Python engine (via uv) against the repository. --root <repo> is always injected first from --repo; once [controls.compliance].enabled is non-empty, --only <the enabled list, comma-joined> is injected right after it. Both are injected before any args given after --, so a user-supplied --root or --only there overrides the injected one — last flag wins, matching Python argparse semantics; this is intentional, not a bug to guard against. Everything else (a bare gate id or domain.name target, --summary, --files LIST, --log-dir DIR) passes straight through.

Configuration — enact-config.toml [controls]

enact-config.toml (repository root) is a shared file: three separate tools converge their config into it, one top-level table each — [hooks]/[rules]/ [repo] for enact-hook, [skills] for enact-extensions, [controls] for this package. A bare root-level version = 1 is a tool-agnostic marker every tool accepts and ignores. This package reads and writes only its own [controls] table; every other byte of the file (including other tools' comments) is opaque and is never inspected, reordered, or dropped.

enact-config.toml's mere presence marks a repository as enact-hook-managed — running install may effectively create that marker even if enact-hook itself is not installed there. enact-config.toml itself is never deleted by uninstall.

Install wires the hooks. It arms nothing.

install seeds exactly three attributes — version, branches and pre_push.remote — and every gate is off. A first install changes what no git push and no git commit does until somebody opts in. branches and pre_push.remote are seeded rather than omitted because they are not gates but the vocabulary gates are phrased in, and because pre-push reads their env vars with ${VAR:?}, which aborts on an empty value: "no branch policy" cannot be spelled as an empty list.

Seeding is per attribute, and only when absent. An attribute already in the file is never rewritten, reordered or reformatted, whatever its value — so re-running install (or a scheduled job that does) can add a missing key but can never arm, disarm or restyle one you set. Granularity is the top-level key inside [controls]: an inline table is one attribute, because editing inside one means rewriting bytes you own. Nothing is parsed and re-serialized; text is spliced into the table's span.

[controls] is strictly validated: unknown keys and wrong types are errors. An absent optional key is its documented default, never an error.

| Key | Seeded by install | Default when absent | enact-m5 | Effect | |---|---|---|---|---| | branches.protected | yes | required | ["main", "integration"] | pre-push refuses while on these branches | | branches.prefixes | yes | required | ["feat", "fix", "docs", "chore"] | pre-push / azdo-push branch pattern <prefix>/<kebab> | | pre_push.remote | yes | required | "origin" | remote azdo-push pushes to and reads status from | | pre_commit.biome | no | false | true | Biome format+lint on the staged index snapshot | | pre_commit.staged_coverage | no | false | true | enact-m5-only staged coverage measurement | | pre_commit.doc_freshness | no | false | opt in | a staged docs/ or plans/ .md carries a last_update stamped within 5h | | pre_push.coverage_ratchet | no | false | true | per outgoing commit, coverage/coverage-summary.json must exist and not regress | | pre_push.findings_ratchet | no | false | opt in | per outgoing commit, every declared static-analysis counter's findings must not exceed the committed floor | | pre_push.static_checks | no | [] | tsc, knip, svelte-check | { label, run } commands run on an export of HEAD, pass cached per tree OID | | release.source | no | "" | "integration" | source branch for the managed promotion PR; no branch pair is guessed | | release.target | no | "" | "main" | target branch for the managed promotion PR | | release.owner_reviewer_id | no | "" | owner identity | required owner identity in the blocking Azure DevOps reviewer policy | | release.merge_strategy | no | "squash" | "no-fast-forward" | one of squash, rebase, rebase-merge, or no-fast-forward |

install copies two package-owned standalone CI assets byte-for-byte:

  • .workflows/promote.ts
  • .workflows/scripts/enable-integration-auto-complete.mjs

Both installed assets are real executable files, never symlinks. Their SHA-256 hashes live in .workflows/repo-controls.lock.json, and doctor fails if either file, mode, or lock entry drifts from the canonical source under assets/. A repository opts into promotion by declaring [controls.release] and wiring its pipeline to run the installed promoter with the matching branch pair and merge strategy. release.owner_reviewer_id is required for doctor to prove the branch policy. The helper creates or reuses the active PR, requires the owner reviewer, and arms auto-complete without deleting the source branch or transitioning work items. Before it reads or mutates PRs, it proves the exact target branch has enabled blocking repository-wide policies for that owner, at least one approval with resetOnSourcePush = true, and a build definition; a missing or weaker policy fails promotion. Pre-commit calls the same read-only doctor report and never repairs or stages the managed surface: run enact-repo-controls install --repo . explicitly to repair it.

[controls.compliance] follows the same rule: install seeds version and enabled = [] only. registry and [controls.compliance.trees] have working defaults and are not written into every repository.

Fixed conventions (enact-m5, not configurable): commit subject ^(feat|fix|docs|refactor|perf|chore|release)(\([a-z0-9][a-z0-9-]*\))?: .+, at most 80 characters, lowercase description without trailing period; Merge , Revert " and X.Y.Z subjects exempt; no staged .scratchpad paths or new references; git diff --cached --check; git environment scrubbed before push checks.

Managed dot paths and protected controls

The pre-commit gate has two deliberately different outcomes:

  • A changed or untracked top-level dot path is commit-required. When it is omitted from the index, the hook names it and tells the author to stage it. Once staged, it can be committed normally. This covers centrally managed paths such as .agents/**, .claude/**, .codex/**, .cursor/**, .mcp.json, and future top-level dot paths. Nested paths such as tools/.claude/** are ordinary repository content. The designated ephemeral scratch storage is never committable.
  • A protected control requires human approval even when staged: every path under compliance/ or workflows/, enact-config.toml, and the repository's explicit [controls.integrity].watched patterns. The same controls cannot be left dirty while committing other work.

Git cannot make a pre-commit hook run for git commit --no-verify. That is the reviewed human-only escape hatch for protected controls: enact-hook denies agents' uses of that flag, while Tarun may review a protected diff and commit it manually with the flag. This package intentionally does not claim to enforce that actor boundary itself.

Note: doc_freshness is this package's own check, not a bundled enact-m5 script — the verbatim workflows/hooks/pre-commit is untouched and the check runs in runHook before it. It carries the rule that used to be compliance row DOC-003 (same 5h window, same YYYY-MM-DDTHH:MM:SSZ format, same exclusions), graded on the staged index instead of the whole docs/ tree: as a registry row it reddened every page five hours after anyone touched it, which no amount of editing kept green. Blobs are read with git show :<path>, so the work-tree copy is irrelevant.

Note: with coverage_ratchet = true every pushed commit and its parent must contain coverage/coverage-summary.json (Istanbul json-summary); commit a baseline first or set it to false.

Note: with findings_ratchet = true, [controls.findings] says where the floor lives (floor, default compliance/ratchets/static-analysis-findings.yaml) and [controls.findings.counters] names the repository's own static-analysis commands — a name -> command map the repository authors itself (e.g. lint = "just check-biome-lint"), so no tool name is ever written into this package; only the repository's own config names one. Each counter's command must print one repository-relative path per line to stdout for a run that completed (exit 0), one line per finding location — any other exit is that counter failing to run, never zero findings and never a pass. The gate only reads: it runs every declared counter fresh and fails naming the counter and what regressed if its findings moved past the committed floor. It never writes the floor — lowering it, to record an improvement, is a change a human makes in the same commit as the fix, the same discipline coverage-floor.json already keeps.

Configuration — enact-config.toml [controls.compliance]

install also appends a [controls.compliance] table to the same shared enact-config.toml, independently of [controls] (each table is its own append-once/user-owned-afterwards span; installing, editing, or uninstalling one never disturbs the other).

It is nested under controls rather than being a root-level [compliance], and that is load-bearing rather than cosmetic: enact-hook decodes this same shared file and rejects the whole document on an unrecognized root key, so a root-level [compliance] turned every enact-hook invocation in a repository carrying it into a decode error. Keeping this package's config inside the one root key it already owns needs no coordination with the other two tools — the same "no tool parses another tool's table" rule the shared file is built on. tests/compliance-config.test.ts guards this: the rendered config must only ever use root keys enact-hook accepts.

[controls.compliance] is strictly validated like [controls] — unknown keys and wrong types are errors — except [controls.compliance.trees], which is a genuinely open string map: any key is accepted, only its value must be a non-empty string.

| Key | Default | Effect | |---|---|---| | version | 1 | config schema version | | enabled | [] | gate ids (RST-001) or domain.name addresses (rust.acp-boundary) to grade; empty = installed, nothing runs | | registry | "default" | forward-compatible; today both values point at the same packaged registry YAML | | [controls.compliance.trees] | {} (no entries) | per-key path overrides; a key absent here keeps the packaged (source-shaped) default for that key |

Registry curation

The full compliance registry (145 rows) always ships and loads — enabled is what decides which of those rows is ever graded, not what's present. The packaged [controls.compliance.trees] defaults are shaped after the original source repository the checks were authored against, not any arbitrary target repo. A repository enabling any row needs its own [controls.compliance.trees] overrides for whatever that row's scope actually needs in that repository's layout — this package does not ship a pre-vetted "safe for any repo" subset; enabling a row without pointing its trees at the right paths will simply grade the wrong (or nonexistent) files.

Development

bun install
bunx tsc --noEmit
bun test --coverage     # 100% lines + functions gate (bunfig.toml)

Tests run real git in temporary repositories under .scratchpad/test-tmp/ (gitignored), exercising the installed stubs end to end. CI: azure-pipelines.yml (publishing present but disabled).