CLI
Install, the three commands, --cwd vs --path, exit codes, and the JSON contract.
View source on GitHub (opens in a new tab)playbookdiff is the terminal entry point for the deterministic comparator implemented in packages/core. It compiles a repository's effective Claude Code and Codex configuration, compares them, and prints the resulting CompatibilityReport.
Install
The CLI is published to npm as playbookdiff. It requires Node.js 24.11 or newer within the 24.x release line, and has no other runtime prerequisite.
npm install --global playbookdiff
playbookdiff check .
To run it once without installing it:
npx playbookdiff check .
While the tool is at 0.x, command output, the JSON contract, and exit-code detail may change between minor releases. Pin an exact version in automation that parses the output.
Running from a checkout
Contributors, and anyone who would rather read the source before running it, can build the CLI from the repository. This needs Node.js 24.11 or newer within the 24.x release line and pnpm 11.24.0.
git clone https://github.com/JacobisEpic/playbookdiff.git
cd playbookdiff
pnpm install --frozen-lockfile
pnpm --filter playbookdiff build
The package builds to a real executable at packages/cli/dist/bin.js:
node packages/cli/dist/bin.js check .
or through the root convenience script, which forwards arguments:
pnpm playbookdiff check .
This workspace does not install playbookdiff onto PATH, because it is not a dependency of another workspace package. To get the plain command from a checkout, install the built package globally from it:
npm install --global ./packages/cli
playbookdiff --help
The release artifact is built and installed in a clean temporary consumer environment by pnpm cli:verify-package in conventional CI, so the published package is exercised the way a consumer installs it. The maintained publication procedure is in the release guide.
Commands
playbookdiff check [repository]
playbookdiff explain <finding-id> [repository]
playbookdiff diff <baseline>..<candidate> [repository]
playbookdiff --help
playbookdiff --version
repository defaults to . for all three commands.
check
Compiles both harnesses' effective configuration for the given repository/cwd/target and prints the resulting CompatibilityReport.
playbookdiff check .
playbookdiff check ./my-repo --cwd apps/web
playbookdiff check . --path apps/web/src/page.tsx
playbookdiff check . --cwd apps/web --path apps/web/src/page.tsx --json
explain
Re-runs the same analysis and prints a detailed explanation of one finding, looked up by its stable ID (as printed by check). explain is stateless: it does not persist prior check runs, so it needs the same --cwd/--path you used to produce the ID, since a finding may only exist for a particular scope.
playbookdiff explain <finding-id> .
playbookdiff explain <finding-id> . --cwd apps/web --path apps/web/src/page.tsx --json
If the ID does not exist for the given repository/cwd/target, explain reports a lookup failure (exit code 2) rather than guessing a nearby match.
diff
Compares PlaybookDiff analysis at two Git revisions of the same repository and reports only the compatibility findings the candidate introduced or resolved relative to the baseline. Pre-existing divergence common to both revisions never causes diff to fail. Your active checkout, branch, HEAD, and index are never touched, and no remote is ever fetched.
playbookdiff diff main..HEAD
playbookdiff diff origin/main..HEAD .
playbookdiff diff main..feature --cwd apps/web --path apps/web/src/page.tsx
playbookdiff diff main..HEAD --json
See docs/git-diff.md for the full specification: exact range semantics, isolation guarantees, the introduced/resolved/unchanged matching rules, the regression policy, and the --json contract.
--cwd vs --path
These model two different things and must not be confused:
--cwdmodels the directory the coding agent was launched from. It is interpreted relative to the repository and defaults to the repository root (.).--pathmodels the repository path the coding agent is working on. It is also interpreted relative to the repository, and has no default (an unset--pathmeans "no specific target").
They matter independently because Codex's repository configuration discovery is bounded by the launch cwd, while Claude Code can discover additional configuration nested under the target path on demand. The same --path can produce a different report depending on --cwd:
playbookdiff check . --cwd . --path apps/web/src/page.tsx
playbookdiff check . --cwd apps/web --path apps/web/src/page.tsx
In the first invocation, Codex is bounded to the repository root and may not discover apps/web-scoped configuration; in the second, launching from apps/web puts that configuration in scope for Codex too. PlaybookDiff models this distinction explicitly rather than collapsing it into one "working directory" concept.
Exit codes
0 analysis completed; no actionable compatibility divergence found (or, for `diff`, no new actionable regression)
1 analysis completed; one or more actionable (medium/high severity) findings exist (or, for `diff`, were newly introduced)
2 PlaybookDiff could not perform the analysis (invalid input, path escape, lookup failure, invalid Git range, unresolvable revision, ...)
A deterministic unknown finding (informational, severity info) never causes exit code 1 by itself, and never causes exit code 2. --help and --version always exit 0.
diff's exit code answers a different question than check's: whether the candidate revision introduced a new actionable finding relative to the baseline, not whether actionable findings exist at all. See docs/git-diff.md for the full regression policy.
--json
check --json, explain --json, and diff --json print machine-readable JSON to stdout and never mix in ANSI styling or log lines; errors still go to stderr. An actionable check --json result still exits 1, and a diff --json result that introduces a new actionable regression still exits 1.
The JSON contracts are intentionally minimal wrappers around the existing CompatibilityReport/CompatibilityFinding types from @playbookdiff/core, rather than a second, CLI-specific report shape:
// check --json
{ context: { repository, cwd, targetPath? }, report: CompatibilityReport }
// explain --json
{ context: { repository, cwd, targetPath? }, finding: CompatibilityFinding }
// diff --json
{
context: { repository, cwd, targetPath? },
baseline: { revision, commit, diagnostics: { claude: Diagnostic[], codex: Diagnostic[] } },
candidate: { revision, commit, diagnostics: { claude: Diagnostic[], codex: Diagnostic[] } },
diff: {
introduced: CompatibilityFinding[],
resolved: CompatibilityFinding[],
unchanged: CompatibilityFinding[],
summary: { introduced, introducedActionable, introducedInformational, resolved, unchanged },
},
}
context.cwd/context.targetPath are the repo-relative values the adapters actually resolved (from the compiled config's target), so displayed context always reflects what was analyzed, not just what was typed. diff --json deliberately omits the full baseline/candidate CompatibilityReports in favor of the delta; see docs/git-diff.md for why.
Deterministic limitations
The CLI does not add semantic interpretation beyond what packages/core's comparator already proves. In particular:
- Different instruction prose at the same effective scope is reported as an informational
unknown, never as a claimed conflict or claimed equivalence. - A capability appearing in repository configuration is reported as configured, not as verified at runtime.
- PlaybookDiff detects structured differences; it does not infer that two harnesses will behave identically or differently.
See docs/comparison.md for the full deterministic comparison specification, and docs/github-action.md for running diff as a reusable GitHub Action.

