The flight envelope for agentic coding.
Turn any code change — a GitHub PR, a local branch, staged edits — into a structured, agent-consumable spec with intent drift detection. CLI and MCP server.
Agents make code changes without guardrails. There is no structured way to declare intent, detect scope drift, or surface risk signals before review. Teams discover scope creep, forbidden file touches, and risk escalation only after the fact — or not at all.
declare intent → make changes → pr-to-spec check → structured spec + drift signals
- Declare intent: Tell
pr-to-specwhat you're building, what scope is allowed, and your risk ceiling - Make changes: Work normally in your branch
- Check drift:
pr-to-spec check --jsonproduces a structured spec + drift signals - Agent consumes: Any agent reads the envelope and acts accordingly
Works as both a CLI tool and an MCP server for IDE integration.
| Who | Engineering teams using AI coding agents |
| What | CLI + MCP server for intent drift detection |
| Where | Node.js (npm install -g pr-to-spec) |
| When | During code review / PR analysis / pre-commit checks |
| Why | Prevents scope creep and surfaces hidden risks before merge |
| Component | Technology |
|---|---|
| Language | TypeScript / Node.js |
| Protocol | MCP (Model Context Protocol) via stdio transport |
| Integration | GitHub API (Octokit), local git |
| Output | Agent protocol envelope (JSON), YAML, Markdown |
| AI (optional) | Anthropic, OpenAI for enhanced summaries |
- Intent-first — declare what you're building before coding, not after
- Drift detection — automatic forbidden-file, scope-creep, risk-escalation, and size-overrun signals
- Agent protocol envelope — structured JSON output with version, status, exit codes, and typed signals
- Dual mode — CLI for pipelines + MCP server for IDE integration (Claude Code, Cursor, Windsurf)
- Deterministic core — heuristics, not LLMs. Reproducible and auditable by default
src/
servers/ MCP server (6 tools via stdio transport)
cli/ CLI entrypoints (analyze, scan, intent, check)
action/ GitHub Action entrypoint
core/
schema/ Zod schema for the canonical prompt-spec format
github/ Octokit-based PR data fetching
sources/ DiffSource abstraction (GitHub PR, local branch, staged, commits)
parsing/ Deterministic spec generation from diff metadata
risk/ Rule-based risk classification heuristics
intent/ Intent schema and YAML storage (.pr-to-spec/intent.yaml)
drift/ Drift detection against declared intent
protocol/ Agent protocol envelope (version, status, exit_code)
rendering/ YAML, Markdown, JSON, and PR comment renderers
ai/ Optional AI enhancement (Anthropic, OpenAI)
diff/ Spec version diffing
- Deterministic first: Core spec uses heuristics, not LLMs. Reproducible and auditable.
- No execution: Never runs code — metadata and diffs only.
- Agent-native: JSON envelope output, clean exit codes, field extraction. Built for piping.
- Local-first: Works on local branches and staged changes without GitHub.
- Minimal trust surface: Read-only by default. Zod-validated output.
Built-in heuristic rules flag changes to:
| Category | Severity | Triggers |
|---|---|---|
authentication |
high | Auth, login, session, OAuth, JWT files |
secrets |
high | .env, .key, .pem, credentials files |
database |
high | Migrations, .sql, schema files |
permissions |
high | RBAC, ACL, policy files |
payment |
high | Stripe, billing, subscription files |
dependencies |
medium | Lockfiles, package managers |
infrastructure |
medium | Docker, Terraform, k8s, deploy configs |
destructive-operations |
medium | DROP TABLE, DELETE FROM in patches |
security-config |
medium | CORS, CSP, security headers |
large-change |
low | 300+ line changes in a single file |
All --json output is wrapped in the agent protocol envelope:
{
"version": 1,
"command": "check",
"status": "drift_detected",
"exit_code": 3,
"signals": [
{
"type": "forbidden_touch",
"description": "1 forbidden file(s) modified",
"severity": "high",
"details": ["src/db/schema.ts"]
}
],
"spec": { ... },
"intent": { ... }
}| Code | Meaning |
|---|---|
0 |
Clean — no issues |
1 |
Error |
2 |
High-risk changes detected |
3 |
Drift from declared intent |
4 |
Gate policy failed |
| Signal | Trigger |
|---|---|
scope_creep |
Files changed outside expected_scope |
forbidden_touch |
Files matching forbidden_scope were modified |
risk_escalation |
Detected risk level exceeds max_risk |
size_overrun |
Total LOC changed exceeds size_budget |
type_mismatch |
Inferred change type doesn't match expected_type |
| Tool | Description |
|---|---|
analyze_pr |
Analyze a GitHub PR and generate a structured spec |
scan_local |
Scan local git changes (branch, staged, commits) |
check_drift |
Check changes against declared intent for drift |
set_intent |
Declare what a code change should accomplish |
show_intent |
Show the current intent declaration |
analyze_assumptions |
Surface implicit decisions with 2x2 matrix |
| Variable | Required | Description |
|---|---|---|
GITHUB_TOKEN |
For GitHub PRs | GitHub token with PR read access |
ANTHROPIC_API_KEY |
No | For AI-enhanced summaries |
OPENAI_API_KEY |
No | Alternative AI provider |
PR_TO_SPEC_DIR |
No | Config directory (default: .pr-to-spec) |
# Install
npm install -g pr-to-spec
# Analyze a GitHub PR
pr-to-spec --repo owner/repo --pr 42 --json
# Scan local branch changes
pr-to-spec scan --json
# Declare intent and check for drift
pr-to-spec intent set --goal "Add user auth" --scope "src/auth/**" --forbid "src/db/**" --max-risk medium
pr-to-spec check --jsonInstall as a Claude Code plugin:
claude plugin add jeremylongshore/pr-to-specOr add to your project's .mcp.json:
{
"mcpServers": {
"pr-spec-analyzer": {
"command": "node",
"args": ["path/to/dist/servers/pr-spec-analyzer.js"]
}
}
}Options:
--repo <owner/name> GitHub repository (required)
--pr <number> Pull request number (required)
--out <directory> Output directory (default: ./output)
--token <token> GitHub token (or set GITHUB_TOKEN env var)
--format <format> Output: yaml, markdown, json, both (default: both)
--stdout Print to stdout instead of files
--quiet Suppress logging
--field <path> Extract a single field (dot notation)
--json Shorthand for --format json --stdout --quiet
--comment Post spec summary as a PR comment
--ai-enhance Enhance spec with AI-generated insights
--debug Log API requests, git commands, and timing
-V, --version Show version
-h, --help Show help
Options:
--branch <ref> Base branch to diff against (default: main)
--diff <n> Diff last N commits
--staged Analyze staged changes only
--out <directory> Output directory (default: ./output)
--format <format> Output: yaml, markdown, json, both
--stdout Print to stdout
--quiet Suppress logging
--json Shorthand for --format json --stdout --quiet
--field <path> Extract a single field
Subcommands:
set Set the intent for this project
show Show the current intent
analyze Analyze assumptions and surface decisions
approve Approve the current intent (draft → approved)
lock Lock the current intent (approved → locked)
gate Evaluate intent gate policy
intent set Options:
--goal <text> What this change is trying to achieve (required)
--scope <glob...> Expected file globs (repeatable)
--forbid <glob...> Forbidden file globs (repeatable)
--max-risk <level> Maximum acceptable risk: low, medium, high
--type <type> Expected change type: feature, bugfix, refactor, etc.
--size-budget <n> Max total lines changed
--json Output as JSON
Options:
--branch <ref> Base branch to diff against
--diff <n> Diff last N commits
--staged Analyze staged changes only
--quiet Suppress logging
--json Output as JSON agent protocol envelope
Subcommands:
add Add a new contract
list List all contracts
remove Remove a contract by ID
contract add Options:
--type <type> Contract type (required)
--description <text> Contract description
--params <json> Contract parameters as JSON
--severity <level> blocking or warning (default: blocking)
--json Output as JSON
Contract types: no_new_dependencies, no_file_outside_scope,
max_files_changed, no_pattern_in_diff, require_pattern_in_diff,
no_new_exports
Subcommands:
query Query ancestors or descendants of a node
impact Show nodes impacted by changes
stats Show graph statistics
graph query Options:
--node <id> Node ID to query (required)
--direction <dir> up (ancestors) or down (descendants)
--json Output as JSON
Subcommands:
review Ingest a code review result
ci Ingest CI pipeline results
status Show current feedback and graph status
feedback review Options:
--reviewer <name> Reviewer name (required)
--status <status> approved, changes_requested, commented (required)
--comment <text...> Review comments (repeatable)
--target <id...> Target node IDs (required)
--json Output as JSON
- MCP Server (
servers/pr-spec-analyzer.ts) — 6-tool Model Context Protocol server exposinganalyze_pr,scan_local,check_drift,set_intent,show_intent, andanalyze_assumptionsvia stdio transport - Claude Code plugin metadata (
.claude-plugin/plugin.json,.mcp.json) — enables pr-to-spec as a standalone MCP plugin for Claude Code, Cursor, and Windsurf @modelcontextprotocol/sdkdependency for MCP protocol compliance--debugflag for CLI — logs API request URLs, git commands, and timing info to stderrPR_TO_SPEC_DIRenv var — configurable storage directory (default:.pr-to-spec)- Security test suite (
tests/security.test.ts) — webhook URL validation, custom_command rejection, prototype pollution guard - API error test suite (
tests/github-errors.test.ts) — 401, 403, 404, 422, rate limit, large PR truncation - README: documented all subcommands (
intent,check,contract,graph,feedback) - README: exit code 4 (
gate_failed) - README: troubleshooting section with common errors
- Removed
custom_commandcontract type — eliminated command injection vector from arbitrary shell execution. The type is preserved in the schema but always fails with a clear deprecation message. - Webhook SSRF prevention — validates webhook URLs: requires HTTPS, rejects localhost, private IPs (10.x, 172.16-31.x, 192.168.x), and link-local addresses (169.254.x).
- Reduced GitHub Action permissions —
contents: write→contents: read(the action only reads PRs and posts comments). - Prototype pollution guard —
--fieldextraction blocks__proto__,constructor, andprototypetraversal. - Secret masking — webhook URLs are masked in error output to prevent leaking sensitive endpoints.
- Version sync — CLI and Action version strings updated from stale
0.6.0to0.8.0 - API error messages — GitHub API errors now return user-friendly messages instead of raw Octokit exceptions (401, 403, 404, 422, rate limit)
- Git error messages —
scan/checkcommands now surface clear messages for "not a git repo" and "unknown revision" errors - Large PR warning — warns when GitHub API returns 300 files (the per-page maximum), indicating truncation
- 384 tests passing (all existing tests updated for security changes)
- Intent DAG: Graph-based intent tracking with spec fragments, decision taxonomy, and propagation engine
graphsubcommand — query ancestors/descendants, compute impact, view graph statscontractsubcommand — declarative contract verification (no_new_dependencies, no_file_outside_scope, max_files_changed, no_pattern_in_diff, require_pattern_in_diff, no_new_exports, custom_command)intent approve/intent lock— approval workflow (draft → approved → locked)intent gate— evaluate gate policy (min_confidence, require_no_stale, require_no_must_ask)- Graph materialization — gate and contract results become audit trail nodes
- Feedback ingestion — learn from reviews to improve future classifications
- 364 tests (84 new tests for Intent DAG)
- Contract evaluator: smarter
no_new_dependenciesdetection (lock files vs manifest files) - Protocol envelope:
gate_failedstatus with exit code 4 checksubcommand: integrated contract evaluation and gate checks
- Renamed:
pr-to-prompt→pr-to-spec - Positioning: "The flight envelope for agentic coding"
DiffSourceabstraction — works on any diff (local branches, staged changes, GitHub PRs)scansubcommand — analyze local git changes without GitHubintentsubcommand — declare expected scope, risk ceiling, change typechecksubcommand — scan + drift detection against declared intent- Drift signals:
scope_creep,forbidden_touch,risk_escalation,size_overrun,type_mismatch - Agent protocol envelope wraps all
--jsonoutput - Claude Code skill manifest (
.claude/skills/pr-to-spec/SKILL.md)
pr-to-spec --repo owner/repo --pr 42 --jsonstill works unchanged
- JSON output format with
--format jsonfor agent-friendly piping (#7) - Bash-friendly CLI flags:
--quiet,--field <path>,--jsonshorthand (#8) - Exit code semantics: 0=success, 1=error, 2=high-risk PR detected (#8)
decision_promptfield in spec for agent-driven review decisions (#12)- Webhook notifications via
webhook_urlinput in GitHub Action (#11) - Example integrations:
claude-code.md,bash-agent.sh,github-action-chain.yml(#10)
- README rewritten with agent-first positioning and piping examples (#9)
- Review parsing with
parseReviews()for aggregated review summaries - Monorepo detection with
detectMonorepo()for workspace-aware specs - Semantic diff analysis with
analyzeSemanticDiff()for change categorization - Spec diffing with
diffSpecs()for comparing spec versions - Review comments integration in PR data and rendered outputs
- GitHub Marketplace release as official Action
- Action bundle with
@vercel/nccfor single-file distribution
- AI enhancement with
--ai-enhanceflag (Anthropic/OpenAI support) - Compact spec output via
compactSpec()for smaller payloads - GitHub Action for automated PR spec generation
- Initial release
- Core PR parsing with
generateSpec() - Risk classification with severity levels
- YAML and Markdown rendering
- PR comment posting via
--commentflag - GitHub API client with Octokit