Skip to content

Latest commit

Β 

History

1,469 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Socket Badge npm version npm downloads CI OpenSSF Scorecard codecov

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.


Quick Start

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 pastoralist

Or install with Homebrew:

brew install yowainwright/tap/pastoralist

Note

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 doctor

For first-run guidance across local use, agents, and CI:

pastoralist onboard

Note

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-skill

When you are ready to add it to the project:

npm install pastoralist --save-dev
npx pastoralist init
npx pastoralist

Optionally keep the appendix current after installs:

 {
  "scripts": {
+    "postinstall": "pastoralist"
  }
 }

Pastoralist can add that hook for you:

npx pastoralist --setup-hook

What It Does

Track Overrides Across Package Managers

Pastoralist reads each package manager's native override field.

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

Record Why an Override Exists

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.",
+        },
+      },
    },
  },
 }

Keep Security Context With the Override

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",
+        },
+      },
    },
  },
 }

Link Local Patches

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"],
      },
    },
  },
 }

Remove Stale Overrides Safely

Preview unused overrides before explicitly removing them.

# first, test before removing
pastoralist --remove-unused --dry-run
# remove
pastoralist --remove-unused

Consolidate Workspace Overrides

Read workspace manifests and write one appendix in the root package.json.

 {
  "pastoralist": {
+    "depPaths": "workspace",
  },
 }

Run Pastoralist in CI

Choose preview, summary, quiet, or machine-readable output.

pastoralist --dry-run
pastoralist --summary
pastoralist --quiet --checkSecurity
pastoralist --outputFormat json

How It Works

Runtime Flow

Type: 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-run

CLI API

The 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.

pastoralist

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-run

--help and --version

Type: boolean options

Prints CLI help or the installed package version.

pastoralist --help
pastoralist --version
pastoralist -v

pastoralist doctor

Type: command

Runs a read-only health check. Internally this enables dryRun: true and summary: true.

pastoralist doctor
pastoralist doctor --outputFormat json

pastoralist onboard

Type: 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 --onboard

pastoralist init

Type: 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 config

pastoralist init agent-skill

Type: 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-run

--path, -p

Type: string option Default: "package.json"

Selects the package manifest Pastoralist should read and update.

pastoralist --path packages/app/package.json
pastoralist -p ./fixtures/package.json --dry-run

--root, -r

Type: 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.json

--depPaths, -d

Type: 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"

--ignore

Type: string[] option

Excludes package manifests from --depPaths matching.

pastoralist --depPaths "**/package.json" --ignore "**/node_modules/**"
pastoralist --depPaths "**/package.json" --ignore "**/fixtures/**" "**/dist/**"

--debug

Type: boolean option

Enables debug logging for CLI execution.

pastoralist --debug
pastoralist --checkSecurity --debug

--dry-run

Type: boolean option

Previews package, appendix, override-source, and security changes without writing files.

pastoralist --dry-run
pastoralist --checkSecurity --dry-run

--outputFormat

Type: "text" | "json" option Default: "text"

Selects terminal output or a single machine-readable JSON result.

pastoralist --outputFormat text
pastoralist --dry-run --outputFormat json

--summary

Type: boolean option

Prints the metrics table after a text-mode run.

pastoralist --summary
pastoralist --checkSecurity --summary

--quiet, -q

Type: 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 osv

--setup-hook

Type: 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-hook

--remove-unused

Type: 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-unused

--checkSecurity

Type: 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 --summary

--securityProvider

Type: "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 npm

--securityProviderToken

Type: 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"

Security Mode Flags

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 --promptForReasons

Cache Flags

Type: 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-cache

pnpm run setup:local-dev

Type: 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 none

Data API

PastoralistResult

Type: 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
  }
}

pastoralist.appendix

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."
        }
      }
    }
  }
}

AppendixItem.ledger

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"
    }
  }
}

Node.js API

update(options)

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`);
}

SecurityChecker.checkSecurity(config, options)

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`);
}

Configuration

Pastoralist reads config from package.json#pastoralist or an external config file. External config files use top-level Pastoralist settings.

Config Files

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,
};

$schema

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
}

depPaths

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"]
}

overrideSource

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.0

appendixSource

Type: 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"
  }
}

compactAppendix

Type: boolean

Stores routine appendix entries as { "addedDate": "..." } when no dependency, patch, security, or keep data needs to stay expanded.

{
  "pastoralist": {
    "compactAppendix": true
  }
}

overridePaths and resolutionPaths

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."
          }
        }
      }
    }
  }
}

checkSecurity

Type: boolean

Enables security scanning from config. security.enabled can override this inside the nested security config.

{
  "pastoralist": {
    "checkSecurity": true
  }
}

security

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
    }
  }
}

bestCase

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.

GitHub Action

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: false

The action can also run security checks, update files, or open scheduled maintenance PRs. See the GitHub Action docs.

Security and Release Assurance

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 signatures

Please reach out with any desired security requests and I will do my best to support you!

Thanks

Shout out to Bryant Cabrera and Mardin for the conversation, insight, and pairing around this topic.

Made by @yowainwright. MIT, 2022-2026.

About

A CLI for automatically shepherding package.json overrides πŸ‘©πŸ½β€πŸŒΎ

Topics

Resources

Code of conduct

Security policy

Stars

107 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages