API Reference
Complete reference for pastoralist CLI and Node.js API
Complete reference for pastoralist CLI and Node.js API
Pastoralist provides a CLI and a Node.js API.
:::tip[Configuration Files]
Most CLI options can be stored in config files. See Configuration for .pastoralistrc, pastoralist.config.js, and package.json settings.
:::
CLI commands and options have their own headings so each entry can be linked directly.
pastoralistRun Pastoralist on the current directory's package.json.
npx pastoralist--help and --versionPrint CLI help or the installed package version.
npx pastoralist --helpnpx pastoralist --version # -vpastoralist doctorRun a read-only setup and override health check. This command enables dry-run
summary mode and does not modify package.json.
npx pastoralist doctorpastoralist onboardPrint a first-run onboarding checklist with initial local usage, agent setup, and GitHub Action setup.
npx pastoralist onboardAliases: pastoralist onboarding, pastoralist --onboard.
pastoralist --path <path>Type:
stringDefault:"package.json"
Run Pastoralist on a specific package.json file.
npx pastoralist --path packages/app/package.json # -p packages/app/package.jsonpastoralist --depPaths [paths...]Type:
string[]Default: unset
Read dependency data from multiple package.json files using glob patterns.
npx pastoralist --depPaths "packages/*/package.json" # -d "packages/*/package.json"pastoralist --ignore [patterns...]Type:
string[]Default:[]
Exclude files matching glob patterns.
npx pastoralist --ignore "**/node_modules/**"pastoralist --root <root>Type:
stringDefault: derived from--pathor the current working directory
Set the root directory for all operations.
npx pastoralist --root ../my-project # -r ../my-projectpastoralist initInitialize configuration with the guided setup. The wizard can configure workspace paths, security scanning, and where the configuration should be saved.
npx pastoralist initAliases: pastoralist init config, pastoralist --init config.
When run, this will:
workspaces entries from package.jsondepPaths: "workspace" or custom package globspackage.json or a supported config filepastoralist --init agent-skillInstall the bundled Pastoralist agent skill into .agents/skills/pastoralist.
npx pastoralist init agent-skillAlias: pastoralist --init agent-skill.
pastoralist --interactiveReview security fixes interactively. Use this with --checkSecurity when you
want to approve fixes instead of applying everything with --forceSecurityRefactor.
npx pastoralist --checkSecurity --interactivepastoralist --debugEnable detailed debug output.
npx pastoralist --debugpastoralist --dry-runPreview changes without modifying package.json.
npx pastoralist --dry-runpastoralist --outputFormat jsonReturn machine-readable output for CI or custom tooling.
npx pastoralist --summary --outputFormat jsonJSON output is a single result object.
{ "success": true, "hasSecurityIssues": false, "hasUnusedOverrides": true, "updated": false, "securityAlertCount": 0, "unusedOverrideCount": 1, "overrideCount": 2, "errors": [], "securityAlerts": [], "unusedOverrides": ["[email protected]"], "appliedOverrides": { "left-pad": "1.3.0", }, "metrics": { "packagesScanned": 1, "workspacePackagesScanned": 0, "appendixEntriesUpdated": 2, "vulnerabilitiesBlocked": 0, "overridesAdded": 0, "overridesRemoved": 0, "writeSuccess": false, "writeSkipped": true, },}pastoralist --quietQuiet mode for CI pipelines. Outputs minimal text and uses exit codes.
npx pastoralist --quiet --checkSecurity # -q --checkSecuritypastoralist --summaryDisplay metrics after run.
npx pastoralist --summarypastoralist --setup-hookAdd Pastoralist to your postinstall script automatically.
npx pastoralist --setup-hookpastoralist --remove-unusedRemove overrides that no package in your project depends on. When Pastoralist detects unused overrides during a run, it suggests this flag.
npx pastoralist --remove-unusedpastoralist --checkSecurityEnable security vulnerability scanning.
npx pastoralist --checkSecuritypastoralist --securityProvider <provider...>Choose one or more security providers. Supported values are osv, github,
npm, snyk, socket, and spektion.
npx pastoralist --checkSecurity --securityProvider osvpastoralist --securityProviderToken <token>Pass a provider token without writing it to config. Prefer environment variables for committed workflows.
npx pastoralist --checkSecurity --securityProvider github --securityProviderToken "$GITHUB_TOKEN"pastoralist --hasWorkspaceSecurityChecksInclude workspace package manifests in security scans when workspaces are configured.
npx pastoralist --checkSecurity --hasWorkspaceSecurityCheckspastoralist --forceSecurityRefactorApply security override fixes without prompting.
npx pastoralist --checkSecurity --forceSecurityRefactorpastoralist --promptForReasonsPrompt for ledger reasons when Pastoralist adds manual override records.
npx pastoralist --promptForReasonspastoralist --strictFail when a security provider, network request, or API call cannot complete.
npx pastoralist --checkSecurity --strictpastoralist --cache-dir <path>Type:
stringDefault:node_modules/.cache/pastoralist/
Store provider cache data in a custom directory.
npx pastoralist --checkSecurity --cache-dir .cache/pastoralistpastoralist --cache-ttl <seconds>Type:
numberDefault: provider default
Override the provider cache TTL.
npx pastoralist --checkSecurity --cache-ttl 3600pastoralist --no-cacheBypass cache reads and writes for a security run.
npx pastoralist --checkSecurity --no-cachepastoralist --refresh-cacheBypass cache reads and write fresh provider results.
npx pastoralist --checkSecurity --refresh-cacheUse the CLI directly when CI only needs to validate or report data.
npx pastoralist+npx pastoralist --dry-run --summary+npx pastoralist --quiet --checkSecurity+npx pastoralist --dry-run --outputFormat jsonUse the GitHub Action when the workflow should also expose outputs or create a maintenance PR.
- uses: yowainwright/pastoralist@v1 id: pastoralist with:+ mode: check+ check-security: true+ security-provider: osv - name: Block unused overrides+ if: steps.pastoralist.outputs.has-unused-overrides == 'true'+ run: exit 1The action exposes has-security-issues, has-unused-overrides, updated,
security-count, unused-count, override-count, and pr-url.
Use these shapes when you read JSON output, inspect the appendix, or build tooling around Pastoralist.
PastoralistResultPastoralistResult is the JSON object returned by --outputFormat json. It
reports whether the run succeeded, whether files changed, what security or
unused-override issues were found, and the run metrics.
npx pastoralist --dry-run --outputFormat json{ "success": true, "hasSecurityIssues": false, "hasUnusedOverrides": true, "updated": false, "securityAlertCount": 0, "unusedOverrideCount": 1, "overrideCount": 2, "errors": [], "securityAlerts": [], "unusedOverrides": ["[email protected]"], "appliedOverrides": { "left-pad": "1.3.0", }, "metrics": { "packagesScanned": 1, "workspacePackagesScanned": 0, "appendixEntriesUpdated": 2, "vulnerabilitiesBlocked": 0, "overridesAdded": 0, "overridesRemoved": 0, "severityCritical": 0, "severityHigh": 0, "severityMedium": 0, "severityLow": 0, "writeSuccess": false, "writeSkipped": true, },}Optional fields include securityAlerts, unusedOverrides,
appliedOverrides, removalVerification, bestCase, and metrics.
pastoralist.appendixpastoralist.appendix stores one entry per override version. Keys use
package-name@version; values can include root dependencies, dependents, patch
files, and ledger data.
{ "pastoralist": { "appendix": { "[email protected]": { "dependents": { "example-app": "left-pad@^1.0.0" }, "patches": ["patches/left-pad+1.3.0.patch"], "ledger": { "addedDate": "2026-08-22T00:00:00.000Z", "reason": "Keep the legacy formatter working." } } } }}AppendixItem.ledgerEvery current appendix entry has a ledger with addedDate. Manual records can
add reason; security runs can add provider, CVE, severity, vulnerable range,
patched version, confidence, source, and resolution fields.
{ "ledger": { "addedDate": "2026-08-22T00:00:00.000Z", "source": "security", "securityProvider": "osv", "cves": ["CVE-2026-1234"], "severity": "high", "vulnerableRange": "<1.3.0", "patchedVersion": "1.3.0", "confidence": "confirmed", "keep": { "reason": "Wait for upstream compatibility confirmation.", "reviewBy": "2026-09-30" } }}npm install pastoralistThe Node API runs the same override policy from JavaScript or TypeScript. The
CLI loads config, runs security checks, then calls update(). If you use the
API directly, call the pieces you need in that order.
update(options)Type:
(options: Options) => UpdateContextDefault:{ path: "package.json" }
Update package.json overrides and the appendix. Each appendix entry includes a
ledger with at least addedDate. Pass the parsed package manifest as
config; the function is synchronous and returns an UpdateContext.
import { resolveJSON, update } from "pastoralist"; const path = "./package.json"; const config = resolveJSON(path); if (config) { const result = update({ config, path,+ dryRun: true,+ outputFormat: "json",+ summary: true, depPaths: ["packages/*/package.json"], ignore: ["**/test/**"], }); + process.stdout.write(`${result.metrics?.appendixEntriesUpdated ?? 0} entries\n`); }SecurityChecker.checkSecurity(config, options)Type:
(config: PastoralistJSON, options?: SecurityCheckRuntimeOptions) => Promise<SecurityCheckResult>Default: provider and cache settings come from theSecurityCheckerconstructor.
Run vulnerability scanning directly and receive provider 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`); }optimizeBestCasePortfolio(options)Type:
(options: OptimizeBestCaseOptions) => Promise<BestCaseResult>Default: policy fromresolveBestCasePolicy()
Evaluate complete package-version states and return the lowest-risk state under a lexicographic policy. The evaluator must return alerts for the complete state, not for one package in isolation.
import { optimizeBestCasePortfolio, type BestCaseEvaluator, type BestCasePackageChoice, } from "pastoralist"; const choices: BestCasePackageChoice[] = [ { packageName: "example", currentVersion: "1.0.0", versions: ["1.0.0", "1.1.0"], }, ]; const evaluate: BestCaseEvaluator = async (state) => { const usesVulnerableVersion = state.example === "1.0.0"; const alerts = usesVulnerableVersion ? [ { packageName: "example", currentVersion: state.example, vulnerableVersions: "<1.1.0", patchedVersion: "1.1.0", severity: "high" as const, title: "Example vulnerability", cves: ["CVE-2026-1234"], fixAvailable: true, }, ] : []; return { alerts }; }; const result = await optimizeBestCasePortfolio({ choices, evaluate,+ config: {+ enabled: true,+ search: { mode: "auto", exactStateLimit: 256 },+ }, }); console.log(result.selectedState); console.log(result.search.provenOptimal);BestCaseEvaluation may also return incompatibilities, oldness, valid,
and error. Rejected callbacks are recorded as invalid states and do not abort
other evaluations.
SecurityChecker.checkSecurity(config, options) accepts bestCase and a
project-supplied bestCaseEvaluator. Package JSON can configure bestCase, but
the evaluator is an API option because functions cannot be stored in JSON.
LedgerReason is a non-empty string, ProjectReason, or BestCaseReason.
Reasons are stored per appendix dependency.
import type { LedgerReason } from "pastoralist"; const reason: LedgerReason = {+ type: "project",+ summary: "Pin this dependency while the upstream fix is reviewed.",+ pin: "3.2.1",+ patch: "patches/example+3.2.1.patch",+ constraints: ["Must retain the current runtime API"],+ references: ["https://example.com/upstream/issue/123"], };A BestCaseReason contains decisionId, policyHash, search, and impact.
CVEs stay in ledger.cves; they are not duplicated in the reason.
logger(config)Type:
(config: LoggerOptions) => LoggerDefault:{ isLogging: false }
Create a logger instance for custom debugging.
import { logger } from "pastoralist"; const log = logger({ file: "my-script.js",+ isLogging: true, }); +log.debug("starting action", "method-name", { data: "value" });+log.error("unexpected error", "method-name", { error: err }); import { resolveJSON, update } from "pastoralist"; const path = "./package.json"; const config = resolveJSON(path); if (config) {+ update({ config, path });+ console.log("Package overrides verified"); } import { resolveJSON, update } from "pastoralist"; import glob from "glob"; const packages = glob.sync("packages/*/package.json"); +for (const pkgPath of packages) {+ const pkg = resolveJSON(pkgPath);+ if (pkg) {+ update({ config: pkg, path: pkgPath });+ console.log(`Updated ${pkgPath}`);+ }+} import { resolveJSON, update } from "pastoralist"; import { execSync } from "child_process"; const path = "./package.json"; const config = resolveJSON(path); const before = execSync("git status --porcelain").toString(); if (config) {+ update({ config, path }); } const after = execSync("git status --porcelain").toString(); if (before !== after) {+ console.error("Package.json overrides need updating");+ process.exit(1); } import { logger, resolveJSON, update } from "pastoralist"; const log = logger({ file: "my-script.js",+ isLogging: process.env.DEBUG === "true", }); const path = "./package.json"; const config = resolveJSON(path); +log.debug("starting", "custom-action", { time: Date.now() }); if (config) {+ update({ config, path, debug: true }); } +log.debug("completed", "custom-action", { time: Date.now() });import { resolveJSON, update } from "pastoralist"; try { const path = "./package.json"; const config = resolveJSON(path); if (!config) throw new Error("Package.json not found"); update({ config, path });} catch (error) { if (error.message === "Package.json not found") { console.error("Package.json not found"); } else { console.error("Unexpected error:", error); }}DEBUG=trueEnable debug output (equivalent to --debug flag).
Security providers read tokens from environment variables when a token is not
passed with --securityProviderToken or SecurityChecker options.
github: GITHUB_TOKENsnyk: SNYK_TOKENsocket: SOCKET_SECURITY_API_KEYspektion: SPEKTION_API_KEY npx pastoralist+DEBUG=true npx pastoralistPastoralist includes full TypeScript support.
import { resolveJSON, update, type Options } from "pastoralist"; const path = "./package.json"; const config = resolveJSON(path); if (!config) { throw new Error("Package.json not found"); } const options: Options = { config, path,+ debug: true, }; update(options);npx pastoralistnpx pastoralist --help
npx pastoralist --version # -vnpx pastoralist doctornpx pastoralist onboardnpx pastoralist --path packages/app/package.json # -p packages/app/package.jsonnpx pastoralist --depPaths "packages/*/package.json" # -d "packages/*/package.json"npx pastoralist --ignore "**/node_modules/**"npx pastoralist --root ../my-project # -r ../my-projectnpx pastoralist initnpx pastoralist init agent-skillnpx pastoralist --checkSecurity --interactivenpx pastoralist --debugnpx pastoralist --dry-runnpx pastoralist --summary --outputFormat json{
"success": true,
"hasSecurityIssues": false,
"hasUnusedOverrides": true,
"updated": false,
"securityAlertCount": 0,
"unusedOverrideCount": 1,
"overrideCount": 2,
"errors": [],
"securityAlerts": [],
"unusedOverrides": ["[email protected]"],
"appliedOverrides": {
"left-pad": "1.3.0",
},
"metrics": {
"packagesScanned": 1,
"workspacePackagesScanned": 0,
"appendixEntriesUpdated": 2,
"vulnerabilitiesBlocked": 0,
"overridesAdded": 0,
"overridesRemoved": 0,
"writeSuccess": false,
"writeSkipped": true,
},
}npx pastoralist --quiet --checkSecurity # -q --checkSecuritynpx pastoralist --summarynpx pastoralist --setup-hooknpx pastoralist --remove-unusednpx pastoralist --checkSecuritynpx pastoralist --checkSecurity --securityProvider osvnpx pastoralist --checkSecurity --securityProvider github --securityProviderToken "$GITHUB_TOKEN"npx pastoralist --checkSecurity --hasWorkspaceSecurityChecksnpx pastoralist --checkSecurity --forceSecurityRefactornpx pastoralist --promptForReasonsnpx pastoralist --checkSecurity --strictnpx pastoralist --checkSecurity --cache-dir .cache/pastoralistnpx pastoralist --checkSecurity --cache-ttl 3600npx pastoralist --checkSecurity --no-cachenpx pastoralist --checkSecurity --refresh-cache npx pastoralist
+npx pastoralist --dry-run --summary
+npx pastoralist --quiet --checkSecurity
+npx pastoralist --dry-run --outputFormat json - uses: yowainwright/pastoralist@v1
id: pastoralist
with:
+ mode: check
+ check-security: true
+ security-provider: osv
- name: Block unused overrides
+ if: steps.pastoralist.outputs.has-unused-overrides == 'true'
+ run: exit 1npx pastoralist --dry-run --outputFormat json{
"success": true,
"hasSecurityIssues": false,
"hasUnusedOverrides": true,
"updated": false,
"securityAlertCount": 0,
"unusedOverrideCount": 1,
"overrideCount": 2,
"errors": [],
"securityAlerts": [],
"unusedOverrides": ["[email protected]"],
"appliedOverrides": {
"left-pad": "1.3.0",
},
"metrics": {
"packagesScanned": 1,
"workspacePackagesScanned": 0,
"appendixEntriesUpdated": 2,
"vulnerabilitiesBlocked": 0,
"overridesAdded": 0,
"overridesRemoved": 0,
"severityCritical": 0,
"severityHigh": 0,
"severityMedium": 0,
"severityLow": 0,
"writeSuccess": false,
"writeSkipped": true,
},
}{
"pastoralist": {
"appendix": {
"[email protected]": {
"dependents": {
"example-app": "left-pad@^1.0.0"
},
"patches": ["patches/left-pad+1.3.0.patch"],
"ledger": {
"addedDate": "2026-08-22T00:00:00.000Z",
"reason": "Keep the legacy formatter working."
}
}
}
}
}{
"ledger": {
"addedDate": "2026-08-22T00:00:00.000Z",
"source": "security",
"securityProvider": "osv",
"cves": ["CVE-2026-1234"],
"severity": "high",
"vulnerableRange": "<1.3.0",
"patchedVersion": "1.3.0",
"confidence": "confirmed",
"keep": {
"reason": "Wait for upstream compatibility confirmation.",
"reviewBy": "2026-09-30"
}
}
}npm install pastoralist import { resolveJSON, update } from "pastoralist";
const path = "./package.json";
const config = resolveJSON(path);
if (config) {
const result = update({
config,
path,
+ dryRun: true,
+ outputFormat: "json",
+ summary: true,
depPaths: ["packages/*/package.json"],
ignore: ["**/test/**"],
});
+ process.stdout.write(`${result.metrics?.appendixEntriesUpdated ?? 0} entries\n`);
} 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`);
} import {
optimizeBestCasePortfolio,
type BestCaseEvaluator,
type BestCasePackageChoice,
} from "pastoralist";
const choices: BestCasePackageChoice[] = [
{
packageName: "example",
currentVersion: "1.0.0",
versions: ["1.0.0", "1.1.0"],
},
];
const evaluate: BestCaseEvaluator = async (state) => {
const usesVulnerableVersion = state.example === "1.0.0";
const alerts = usesVulnerableVersion
? [
{
packageName: "example",
currentVersion: state.example,
vulnerableVersions: "<1.1.0",
patchedVersion: "1.1.0",
severity: "high" as const,
title: "Example vulnerability",
cves: ["CVE-2026-1234"],
fixAvailable: true,
},
]
: [];
return { alerts };
};
const result = await optimizeBestCasePortfolio({
choices,
evaluate,
+ config: {
+ enabled: true,
+ search: { mode: "auto", exactStateLimit: 256 },
+ },
});
console.log(result.selectedState);
console.log(result.search.provenOptimal); import type { LedgerReason } from "pastoralist";
const reason: LedgerReason = {
+ type: "project",
+ summary: "Pin this dependency while the upstream fix is reviewed.",
+ pin: "3.2.1",
+ patch: "patches/example+3.2.1.patch",
+ constraints: ["Must retain the current runtime API"],
+ references: ["https://example.com/upstream/issue/123"],
}; import { logger } from "pastoralist";
const log = logger({
file: "my-script.js",
+ isLogging: true,
});
+log.debug("starting action", "method-name", { data: "value" });
+log.error("unexpected error", "method-name", { error: err }); import { resolveJSON, update } from "pastoralist";
const path = "./package.json";
const config = resolveJSON(path);
if (config) {
+ update({ config, path });
+ console.log("Package overrides verified");
} import { resolveJSON, update } from "pastoralist";
import glob from "glob";
const packages = glob.sync("packages/*/package.json");
+for (const pkgPath of packages) {
+ const pkg = resolveJSON(pkgPath);
+ if (pkg) {
+ update({ config: pkg, path: pkgPath });
+ console.log(`Updated ${pkgPath}`);
+ }
+} import { resolveJSON, update } from "pastoralist";
import { execSync } from "child_process";
const path = "./package.json";
const config = resolveJSON(path);
const before = execSync("git status --porcelain").toString();
if (config) {
+ update({ config, path });
}
const after = execSync("git status --porcelain").toString();
if (before !== after) {
+ console.error("Package.json overrides need updating");
+ process.exit(1);
} import { logger, resolveJSON, update } from "pastoralist";
const log = logger({
file: "my-script.js",
+ isLogging: process.env.DEBUG === "true",
});
const path = "./package.json";
const config = resolveJSON(path);
+log.debug("starting", "custom-action", { time: Date.now() });
if (config) {
+ update({ config, path, debug: true });
}
+log.debug("completed", "custom-action", { time: Date.now() });import { resolveJSON, update } from "pastoralist";
try {
const path = "./package.json";
const config = resolveJSON(path);
if (!config) throw new Error("Package.json not found");
update({ config, path });
} catch (error) {
if (error.message === "Package.json not found") {
console.error("Package.json not found");
} else {
console.error("Unexpected error:", error);
}
} npx pastoralist
+DEBUG=true npx pastoralist import { resolveJSON, update, type Options } from "pastoralist";
const path = "./package.json";
const config = resolveJSON(path);
if (!config) {
throw new Error("Package.json not found");
}
const options: Options = {
config,
path,
+ debug: true,
};
update(options);