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.
Store project settings in package.json or an external config file.
See Configuration for formats and precedence.
Each command and option below has a direct link. The walkthroughs use fixed fixture versions.
pastoralistRun Pastoralist on the current directory's package.json.
npx pastoralistTrack the existing qs override. Scroll to see the generated record.
--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.
Loading output…
Preview workspace tracking without changing project files. Security scanning is disabled in this fixture.
pastoralist --path <path>Type:
stringDefault:"package.json"
Run Pastoralist on a specific package.json file.
Alias: -p.
Loading output…
Target the fixture's standalone project with its own qs override.
pastoralist --depPaths [paths...]Type:
string[]Default: unset
Read dependency data from multiple package.json files using glob patterns.
Alias: -d.
Loading output…
The root pins qs. Two workspace packages depend on it: @shepherd/api and @shepherd/worker.
pastoralist --ignore [patterns...]Type:
string[]Default:[]
Exclude files matching glob patterns.
Loading output…
Scan the workspace manifests while excluding worker.
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 to project files. Security providers may still write caches.
npx pastoralist --dry-runPreview appendix changes for the workspace fixture.
pastoralist --outputFormat jsonReturn machine-readable output for CI or custom tooling.
JSON output is a single result object.
Loading output…
Update tracking and print a machine-readable result. Security scanning is disabled in this fixture.
pastoralist --styleguideOpen an interactive radio menu for exploring the Pastoralist DX components
without changing project configuration. Use the arrow keys and Enter to choose
a demo. In the prompt demo, Space toggles choices, a selects all, n selects
none, and Esc cancels.
npx pastoralist --styleguidepastoralist --quietQuiet mode for CI pipelines. Outputs minimal text and uses exit codes.
--strict)npx pastoralist --quiet --checkSecurity # -q --checkSecuritypastoralist --summaryDisplay metrics after run.
npx pastoralist --summaryUpdate the appendix and print the CLI's metrics. This fixture disables security scanning.
pastoralist --setup-hookAdd Pastoralist to your postinstall script automatically.
npx pastoralist --setup-hookAdd Pastoralist after the existing postinstall command.
pastoralist --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-unusedHere, express has been removed and no dependency needs qs. Scroll to see cleanup.
pastoralist --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 pastoralistnpx pastoralist --dry-run --summarynpx pastoralist --quiet --checkSecuritynpx 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,
security-fixes-applied, 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.ledgerExpanded entries have ledger.addedDate. Manual records can add reason;
security fixes can add provider, CVE, severity, vulnerable range, patched
version, confidence, source, and resolution fields. With compactAppendix,
routine entries may contain only top-level addedDate.
{ "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) => UpdateContext
Pass a parsed package manifest as config and its manifest path.
update() returns UpdateContext synchronously.
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 }, },}); process.stdout.write(`${JSON.stringify(result.selectedState)}\n`);process.stdout.write(`${result.search.provenOptimal}\n`);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: new Error("Example failure") });import { resolveJSON, update } from "pastoralist"; const path = "./package.json";const config = resolveJSON(path); if (config) { update({ config, path });}Use the CLI when CI needs to fail on security findings or provider errors:
npx pastoralist --dry-run --quiet --checkSecurity --strictimport { 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) { const isNotFound = error instanceof Error && error.message === "Package.json not found"; if (isNotFound) { 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_KEYnpx pastoralistDEBUG=true npx pastoralistPastoralist includes TypeScript types for its Node API.
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);{
"name": "shepherd-cli",
"dependencies": {
"express": "4.18.2"
},
"overrides": {
"qs": "6.11.2"
},
"pastoralist": {
"appendix": {
"[email protected]": {
"dependents": {
"shepherd-cli": "qs (required by express)"
},
"ledger": {
"addedDate": "2026-10-16"
}
}
}
}
}npx pastoralist --help
npx pastoralist --version # -vnpx pastoralist doctor --outputFormat json{
"success": true,
"updated": false,
"overrideCount": 1,
"unusedOverrides": [],
"appliedOverrides": {
"qs": "6.11.2"
}
}npx pastoralist doctor --outputFormat jsonnpx pastoralist --path standalone/package.json{
"name": "shepherd-tool",
"private": true,
"devDependencies": {
"qs": "6.11.2"
},
"overrides": {
"qs": "6.11.2"
},
"pastoralist": {
"appendix": {
"[email protected]": {
"dependents": {
"shepherd-tool": "[email protected]"
},
"ledger": {
"addedDate": "2026-10-16T00:00:00.000Z"
}
}
},
"security": {
"enabled": false
}
}
}npx pastoralist --path standalone/package.jsonnpx pastoralist --depPaths "packages/*/package.json"{
"pastoralist": {
"appendix": {
"[email protected]": {
"dependents": {
"@shepherd/api": "qs@^6.10.0",
"@shepherd/worker": "qs@^6.11.0"
},
"ledger": {
"addedDate": "2026-10-16T00:00:00.000Z"
}
}
}
}
}npx pastoralist --depPaths "packages/*/package.json"npx pastoralist --depPaths "packages/*/package.json" --ignore "**/worker/**"{
"pastoralist": {
"appendix": {
"[email protected]": {
"dependents": {
"@shepherd/api": "qs@^6.10.0"
},
"ledger": {
"addedDate": "2026-10-16T00:00:00.000Z"
}
}
}
}
}npx pastoralist --depPaths "packages/*/package.json" --ignore "**/worker/**"npx pastoralist --root ../my-project # -r ../my-projectnpx pastoralist initnpx pastoralist init agent-skillnpx pastoralist --checkSecurity --interactivenpx pastoralist --debug{
"pastoralist": {
"appendix": {
"[email protected]": {
"dependents": {
"@shepherd/api": "qs@^6.10.0",
"@shepherd/worker": "qs@^6.11.0"
},
"ledger": {
"addedDate": "2026-10-16T00:00:00.000Z"
}
}
}
}
}npx pastoralist --outputFormat json{
"success": true,
"updated": true,
"overrideCount": 1,
"unusedOverrides": [],
"appliedOverrides": {
"qs": "6.11.2"
}
}npx pastoralist --outputFormat jsonnpx pastoralist --styleguidenpx pastoralist --quiet --checkSecurity # -q --checkSecurity+--------------------------+------------+
| 🧑🌾 Pastoralist Summary |
+--------------------------+------------+
| Packages scanned | 5 |
| Appendix entries updated | 1 |
| Vulnerabilities blocked | 0 |
| Overrides added | 0 |
| Overrides removed | 0 |
| By severity: | |
| Critical | 0 |
| High | 0 |
| Medium | 0 |
| Low | 0 |
| Write status | Success |
+--------------------------+------------+{
"name": "shepherd-cli",
"scripts": {
"postinstall": "node --version && pastoralist"
}
}{
"name": "shepherd-cli",
"overrides": {}
}npx 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-cachenpx 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 pastoralistimport { 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 },
},
});
process.stdout.write(`${JSON.stringify(result.selectedState)}\n`);
process.stdout.write(`${result.search.provenOptimal}\n`);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: new Error("Example failure") });import { resolveJSON, update } from "pastoralist";
const path = "./package.json";
const config = resolveJSON(path);
if (config) {
update({ config, path });
}npx pastoralist --dry-run --quiet --checkSecurity --strictimport { 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) {
const isNotFound = error instanceof Error && error.message === "Package.json not found";
if (isNotFound) {
console.error("Package.json not found");
} else {
console.error("Unexpected error:", error);
}
}npx pastoralist
DEBUG=true npx pastoralistimport { 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);