Pastoralist is an audit trail for package manager overrides.
Overrides often start as real fixes: a CVE patch, a compatibility pin, a fork,
or a temporary transitive dependency workaround. Months later, the override is
still in package.json, but the reason is usually somewhere else.
| Without Pastoralist | With Pastoralist |
|---|---|
{
+ "overrides": {
+ // Q: Why is "barn-yarn" in overrides?
+ "barn-yard": "2.0.0",
+ }
} |
{
"overrides": {
- // Q: Why is "barn-yarn" in overrides?
"barn-yard": "2.0.0",
},
+ "pastoralist": {
+ "appendix": {
+ // A: Compatibility pin
+ "barn-yard@2.0.0": {
+ "ledger": {
+ "addedDate": "2026-08-22T00:00:00.000Z",
+ "reason": "Compatibility pin"
+ }
+ }
+ }
} |
Pastoralist handles your overrides and gives you a apendix ledger so you know know why: why the override exists, which packages still need it, which security provider found it, and when it can be removed.
Note
For the quick start section, we will be using npx as we're assuming pastoralist is not installed.
Ensure your agent doesn't establish this pattern long term because it will use more memory.
Install with your favorite JS package manager:
npm install --global pastoralistOr install with Homebrew:
brew install yowainwright/tap/pastoralistNote
For local projects where you just use pastoralist within scripts or CI,
npm install pastoralist --save-dev is enough
I'm also working to get pastoralist into brew officially soon, if possible! [August '26]
Start with a read-only check:
pastoralist doctorFor first-run guidance across local use, agents, and CI:
pastoralist onboardNote
The onboarding output includes quick scripts and copy/paste prompts for agents. See the Onboarding guide for the same checklist in the docs.
Set up the Pastoralist agent skill in a repo:
pastoralist --init agent-skillWhen you are ready to add it to the project:
npm install pastoralist --save-dev
npx pastoralist init
npx pastoralistOptionally keep the appendix current after installs:
{
"scripts": {
+ "postinstall": "pastoralist"
}
}Pastoralist can add that hook for you:
npx pastoralist --setup-hookPastoralist reads each package manager's native override field.
The appendix keeps the reason beside the packages that still need the override.
{
"pastoralist": {
"appendix": {
+ "barn-yard@2.0.0": {
+ "dependents": { "shepherd-cli": "barn-yard@^1" },
+ "ledger": {
+ "addedDate": "2026-08-22T00:00:00.000Z",
+ "reason": "Keep the gate API compatible.",
+ },
+ },
},
},
}Security records include the advisory, severity, provider, and patched version.
{
"pastoralist": {
"appendix": {
+ "escaped-sheep@1.0.1": {
+ "ledger": {
+ "addedDate": "2026-08-22T00:00:00.000Z",
+ "cves": ["CVE-escaped-sheep"],
+ "severity": "high",
+ "securityProvider": "osv",
+ "patchedVersion": "1.0.1",
+ },
+ },
},
},
}Pastoralist records the patch-package files that support an override.
{
"pastoralist": {
"appendix": {
"patchy-alpaca@1.4.0": {
+ "patches": ["patches/patchy-alpaca+1.4.0.patch"],
},
},
},
}Preview unused overrides before explicitly removing them.
# first, test before removing
pastoralist --remove-unused --dry-run
# remove
pastoralist --remove-unusedRead workspace manifests and write one appendix in the root package.json.
{
"pastoralist": {
+ "depPaths": "workspace",
},
}Choose preview, summary, quiet, or machine-readable output.
pastoralist --dry-run
pastoralist --summary
pastoralist --quiet --checkSecurity
pastoralist --outputFormat jsonType:
CLI options -> PastoralistResult
Pastoralist loads CLI options and project config, resolves native override data,
runs the optional security phase, rebuilds appendix entries, attaches
patch-package files, optionally removes verified unused overrides, then writes
the configured targets. --dry-run uses the same path without writing.
pastoralist --dry-run --summary
pastoralist --checkSecurity --securityProvider osv --dry-runThe direct commands below assume pastoralist is available from a project
script, global npm install, or Homebrew install. Use npx pastoralist ... for
one-off project setup.
Type:
command
Updates the target package manifest's override appendix. It reads npm
overrides, pnpm pnpm.overrides, Yarn resolutions, and Bun overrides.
pastoralist
pastoralist --dry-runType:
boolean options
Prints CLI help or the installed package version.
pastoralist --help
pastoralist --version
pastoralist -vType:
command
Runs a read-only health check. Internally this enables dryRun: true and
summary: true.
pastoralist doctor
pastoralist doctor --outputFormat jsonType:
command
Prints first-run guidance for local setup, agent setup, and GitHub Action setup.
pastoralist onboarding and pastoralist --onboard are aliases.
pastoralist onboard
pastoralist onboarding
pastoralist --onboardType:
command
Starts the config wizard. The wizard can save config to package.json or an
external config file, configure workspace paths, and set up security scanning.
pastoralist init
pastoralist init config
pastoralist --init configType:
command
Installs the bundled Pastoralist agent skill into
.agents/skills/pastoralist. Existing unmanaged skill files are preserved.
pastoralist init agent-skill
pastoralist --init agent-skill --dry-runType:
string optionDefault:"package.json"
Selects the package manifest Pastoralist should read and update.
pastoralist --path packages/app/package.json
pastoralist -p ./fixtures/package.json --dry-runType:
string option
Sets the root directory used to resolve relative paths, config files, lockfiles, patches, and workspace globs.
pastoralist --root ../my-project
pastoralist --root ../my-project --path package.jsonType:
string[] option
Scans additional package manifests for monorepo dependency context. Values are collected until the next flag.
pastoralist --depPaths "packages/*/package.json"
pastoralist -d "packages/*/package.json" "apps/*/package.json"Type:
string[] option
Excludes package manifests from --depPaths matching.
pastoralist --depPaths "**/package.json" --ignore "**/node_modules/**"
pastoralist --depPaths "**/package.json" --ignore "**/fixtures/**" "**/dist/**"Type:
boolean option
Enables debug logging for CLI execution.
pastoralist --debug
pastoralist --checkSecurity --debugType:
boolean option
Previews package, appendix, override-source, and security changes without writing files.
pastoralist --dry-run
pastoralist --checkSecurity --dry-runType:
"text" | "json" optionDefault:"text"
Selects terminal output or a single machine-readable JSON result.
pastoralist --outputFormat text
pastoralist --dry-run --outputFormat jsonType:
boolean option
Prints the metrics table after a text-mode run.
pastoralist --summary
pastoralist --checkSecurity --summaryType:
boolean option
Suppresses normal text output for CI. Security findings make the command exit
with code 1; clean security checks exit with code 0.
pastoralist --quiet --checkSecurity
pastoralist -q --checkSecurity --securityProvider osvType:
boolean option
Adds pastoralist to the target manifest's postinstall script. Existing
postinstall scripts are appended with && pastoralist.
pastoralist --setup-hook
pastoralist --root packages/app --setup-hookType:
boolean option
Removes verified unused override entries from the active override source and
appendix. Preview first with --dry-run.
pastoralist --remove-unused --dry-run
pastoralist --remove-unusedType:
boolean option
Runs vulnerability scanning before the appendix update. Fixable security findings can add override data and security ledger fields.
pastoralist --checkSecurity
pastoralist --checkSecurity --dry-run --summaryType:
"osv" | "github" | "snyk" | "npm" | "socket" | "spektion" | string[] option
Chooses one or more security providers. OSV is the default when security is enabled and no provider is set.
pastoralist --checkSecurity --securityProvider osv
pastoralist --checkSecurity --securityProvider osv npmType:
string option
Passes a provider token for a single run. Prefer provider environment variables
for CI: GITHUB_TOKEN, SNYK_TOKEN, SOCKET_SECURITY_API_KEY, or
SPEKTION_API_KEY.
pastoralist --checkSecurity --securityProvider github --securityProviderToken "$GITHUB_TOKEN"
pastoralist --checkSecurity --securityProvider socket --securityProviderToken "$SOCKET_SECURITY_API_KEY"Type:
boolean options
Controls how security findings are handled: --interactive prompts for fixes,
--forceSecurityRefactor applies available fixes without prompting,
--hasWorkspaceSecurityChecks includes workspace packages, --promptForReasons
asks for manual ledger reasons, and --strict fails on provider errors.
pastoralist --checkSecurity --interactive
pastoralist --checkSecurity --forceSecurityRefactor --strict
pastoralist --checkSecurity --hasWorkspaceSecurityChecks
pastoralist --promptForReasonsType:
string | number | boolean options
Controls provider cache behavior. --cache-dir changes the cache directory,
--cache-ttl sets TTL seconds, --no-cache bypasses reads and writes, and
--refresh-cache bypasses reads while writing fresh data.
pastoralist --checkSecurity --cache-dir .cache/pastoralist
pastoralist --checkSecurity --cache-ttl 3600
pastoralist --checkSecurity --no-cache
pastoralist --checkSecurity --refresh-cacheType:
package script
Sets up local agent config, bundled skills, Git hooks, and the postinstall hook.
Use --dry-run before writing setup files.
pnpm run setup:local-dev -- --dry-run
pnpm run setup:local-dev -- --agent codex --skills all --hooks git,postinstall
pnpm run setup:local-dev -- --agent skip --skills pastoralist --hooks noneType:
object
The JSON output shape returned by text-independent CLI runs. It reports write status, security status, unused overrides, applied string overrides, errors, and metrics.
pastoralist --dry-run --outputFormat json{
"success": true,
"hasSecurityIssues": false,
"hasUnusedOverrides": true,
"updated": false,
"securityAlertCount": 0,
"unusedOverrideCount": 1,
"overrideCount": 2,
"errors": [],
"securityAlerts": [],
"unusedOverrides": ["escaped-sheep@1.0.0"],
"appliedOverrides": {
"old-goat": "4.1.0"
},
"metrics": {
"packagesScanned": 1,
"workspacePackagesScanned": 0,
"appendixEntriesUpdated": 2,
"vulnerabilitiesBlocked": 0,
"overridesAdded": 0,
"overridesRemoved": 0,
"removedOverridePackages": [],
"severityCritical": 0,
"severityHigh": 0,
"severityMedium": 0,
"severityLow": 0,
"writeSuccess": false,
"writeSkipped": true
}
}Type:
Record<string, AppendixItem>
Stores the ledger entry for each override version. Keys use
package-name@version; values can include root dependencies, dependents,
patches, and ledger metadata.
{
"pastoralist": {
"appendix": {
"old-goat@4.1.0": {
"dependents": {
"shepherd-cli": "old-goat@^3.0.0"
},
"ledger": {
"addedDate": "2026-08-22T00:00:00.000Z",
"reason": "Keep the older shepherd-cli integration working."
}
}
}
}
}Type:
object
Records why an override exists and the security context behind it. Security runs can add CVEs, severity, provider, patched version, source, confidence, and resolution fields.
{
"ledger": {
"addedDate": "2026-08-22T00:00:00.000Z",
"source": "security",
"securityProvider": "osv",
"cves": ["CVE-2026-1234"],
"severity": "high",
"patchedVersion": "4.1.0",
"keep": {
"reason": "Wait for upstream compatibility confirmation.",
"reviewBy": "2026-09-30"
}
}
}Type:
(options: Options) => UpdateContext
Runs the core override and appendix update from JavaScript or TypeScript. Pass a
parsed package manifest as config and the manifest path.
import { resolveJSON, update } from "pastoralist";
const path = "./package.json";
const config = resolveJSON(path);
if (config) {
const result = update({
config,
path,
dryRun: true,
depPaths: ["packages/*/package.json"],
});
process.stdout.write(`${result.metrics?.appendixEntriesUpdated ?? 0} entries\n`);
}Type:
(config: PastoralistJSON, options?: SecurityCheckRuntimeOptions) => Promise<SecurityCheckResult>
Runs provider-backed vulnerability scanning directly and returns alerts, suggested overrides, update suggestions, package counts, and optional best-case metadata.
import { resolveJSON, SecurityChecker } from "pastoralist";
const config = resolveJSON("./package.json");
const checker = new SecurityChecker({ provider: "osv" });
if (config) {
const result = await checker.checkSecurity(config, {
root: process.cwd(),
packageJsonPath: "./package.json",
severityThreshold: "high",
});
process.stdout.write(`${result.alerts.length} alerts found\n`);
}Pastoralist reads config from package.json#pastoralist or an external config
file. External config files use top-level Pastoralist settings.
Type:
".pastoralistrc" | ".pastoralistrc.json" | "pastoralist.json" | "pastoralist.config.cjs" | "pastoralist.config.js" | "pastoralist.config.mjs"
Pastoralist searches for the first matching external config file in this order:
.pastoralistrc, .pastoralistrc.json, pastoralist.json,
pastoralist.config.cjs, pastoralist.config.js, then
pastoralist.config.mjs. External config is merged with
package.json#pastoralist; package.json wins on conflicts.
{
"pastoralist": {
"depPaths": "workspace",
"checkSecurity": true
}
}export default {
depPaths: ["packages/*/package.json", "apps/*/package.json"],
checkSecurity: true,
};Type:
string
The JSON Schema is exported as pastoralist/schema.json.
External JSON config files can reference ./node_modules/pastoralist/src/schema.json with $schema.
Configs that reference this schema reject unknown or mistyped fields; other configs retain compatible validation behavior.
{
"$schema": "./node_modules/pastoralist/src/schema.json",
"depPaths": "workspace",
"checkSecurity": true
}Type:
"workspace" | "workspaces" | string[]
Defines additional package manifests used for monorepo dependency context.
"workspace" and "workspaces" resolve from the root manifest's workspaces
field.
{
"workspaces": ["packages/*", "apps/*"],
"pastoralist": {
"depPaths": "workspace"
}
}{
"depPaths": ["packages/*/package.json", "apps/*/package.json"]
}Type:
string
Reads and writes native overrides from a separate JSON or YAML file instead of
the target package manifest. For pnpm 11 projects, Pastoralist can also resolve
pnpm-workspace.yaml automatically.
{
"pastoralist": {
"overrideSource": "config/overrides.json"
}
}packages:
- packages/*
overrides:
old-goat: 4.1.0Type:
string
Writes appendix data to a JSON config file instead of embedding it in
package.json. The target must be JSON or .pastoralistrc.
{
"pastoralist": {
"appendixSource": ".pastoralistrc.json"
}
}Type:
boolean
Stores routine appendix entries as { "addedDate": "..." } when no dependency,
patch, security, or keep data needs to stay expanded.
{
"pastoralist": {
"compactAppendix": true
}
}Type:
Record<string, Appendix>
Keeps manual appendix data for packages whose overrides or resolutions live in
workspace-specific paths. resolutionPaths is the Yarn-oriented fallback.
{
"pastoralist": {
"overridePaths": {
"packages/web/package.json": {
"react@19.0.0": {
"ledger": {
"addedDate": "2026-08-22T00:00:00.000Z",
"reason": "Pinned for the web app release."
}
}
}
}
}
}Type:
boolean
Enables security scanning from config. security.enabled can override this
inside the nested security config.
{
"pastoralist": {
"checkSecurity": true
}
}Type:
object
Configures security scanning. Supported fields are enabled, provider,
autoFix, interactive, securityProviderToken, severityThreshold,
excludePackages, hasWorkspaceSecurityChecks, strict, and preferLatest.
{
"pastoralist": {
"security": {
"enabled": true,
"provider": ["osv", "npm"],
"severityThreshold": "medium",
"excludePackages": ["@types/*"],
"hasWorkspaceSecurityChecks": true,
"strict": true
}
}
}Type:
BestCaseConfig
Opts into portfolio-level security fix selection. Pastoralist ranks complete version states by ordered objectives instead of picking each package fix in isolation.
{
"pastoralist": {
"checkSecurity": true,
"bestCase": {
"enabled": true,
"userOwnedOverrides": ["alpha"],
"riskAggregation": "both",
"objectives": ["known-exploited", "critical", "high", "change-count"],
"search": {
"mode": "auto",
"exactStateLimit": 256,
"beamWidth": 16,
"maxEvaluations": 1000
}
}
}
}See Configuration and Workspaces for the full setup surface.
Check override tracking on pull requests:
name: Override Check
on: [pull_request]
jobs:
pastoralist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
+ - uses: yowainwright/pastoralist@v1
+ with:
+ mode: check
+ check-security: falseThe action can also run security checks, update files, or open scheduled maintenance PRs. See the GitHub Action docs.
Pastoralist can write to package.json, so the package should be boring to
verify.
- Releases are published from GitHub Actions with npm provenance
- Published tarballs are packed before release and attached to GitHub Releases with artifact attestations
- ScriptC binaries are built, tested, and attested only for stable Homebrew releases
- Stable releases open a reviewed Homebrew tap update
- CI runs CodeQL, OpenSSF Scorecard, unit, integration, e2e, and dependency policy checks
You can verify registry signatures from your project:
npm audit signaturesPlease reach out with any desired security requests and I will do my best to support you!
Shout out to Bryant Cabrera and Mardin for the conversation, insight, and pairing around this topic.
Made by @yowainwright. MIT, 2022-2026.

{ // npm and Bun "overrides": { "barn-yard": "2.0.0" }, // pnpm "pnpm": { "overrides": { "old-goat": "4.1.0" } }, // Yarn "resolutions": { "escaped-sheep": "1.0.1" }, }