Make AI-assisted development safer, auditable, and release-ready without breaking normal CLI and git workflows
Project description
PlanGuard
Plan and control how AI coding agents make changes in your project.
PlanGuard is a lightweight, language-agnostic framework that makes AI-assisted development safer and auditable. It enforces a simple rule: plan first, then implement.
Why
AI agents are powerful but unconstrained. Without guardrails they can:
- Modify files you didn't intend them to touch
- Skip tests or verification steps
- Make "small" database changes that break production
- Leave no audit trail of what they did or why
PlanGuard fixes this with scope enforcement, safety checks, verification gates, and an immutable session log — all driven from the CLI, with no external services required.
Quick Start
# Install or upgrade
pipx install planguard # or: pip install planguard
# Set up in your project
cd /path/to/your-project
planguard init # detects stack, creates context files and AGENTS.md
# Plan → Check → Activate → Implement → Verify → Complete
planguard plan # interactive wizard to define scope, risks, verification
planguard check # validate structure, risk score, collisions
planguard activate my_plan # record baseline, allow implementation
# ... now prompt the agent — see "Activation and Implementation" below ...
planguard verify my_plan # run verification commands
planguard complete my_plan # mark done (only if verified snapshot matches)
That's the whole workflow. Everything below is detail you can read as you need it.
Install
pipx install planguard # any project (recommended)
pip install planguard # or as a Python dependency
poetry add --group dev planguard # or with Poetry
Works on Linux, macOS, and Windows. Requires Python 3.9+.
Upgrade
0.7.4 Highlights
PlanGuard 0.7.4 is a performance-focused follow-up release for status and list:
- default
planguard statusandplanguard listnow use cached verification state from plan status files and avoid expensive per-plan live freshness recomputation --refresh-verificationis available on both commands when you explicitly want currentpassedvsstaleevaluation against the repo- malformed plan handling remains robust, so invalid plans are still visible without crashing the command
- refresh mode reuses scoped git snapshots within a single command run where possible instead of recomputing the same scope repeatedly
Tradeoff: the default fast path shows the last recorded verification result (passed, failed, or —). It does not compute stale unless you opt into refresh mode.
0.7.2 Highlights
PlanGuard 0.7.2 is a robustness release focused on day-to-day workflow quality:
planguard statusandplanguard listnow tolerate malformed plans and flag them inline instead of crashing the whole command- baseline capture is scope-aware by default, with unrelated repo dirt tracked separately as context
planguard activate --baseline-mode repoandplanguard resume --refresh-baseline --baseline-mode repokeep the older repo-wide baseline behavior when you explicitly want it- structured verification checks and explicit interpreters are documented as first-class verification options
- common CLI usage suppresses known low-level dependency warning noise by default, while
planguard --verbose ...still leaves a path for debugging unexpected internal failures
Existing 0.5+ installs should upgrade in place:
pipx upgrade planguard
pip install -U planguard
poetry add --group dev planguard@latest
For existing repositories, prefer the scripted upgrade path:
planguard upgrade --no-wizard
planguard check
On legacy repositories that still keep plans under docs/, planguard upgrade --no-wizard now migrates them to the local default .planguard/plans/, refreshes the managed AGENTS workflow, and keeps runtime state under .planguard/state/.
Upgrade also normalizes common legacy plan shapes so that a follow-up planguard check is usable immediately:
- legacy
placeholderordeferredplans are converted intosuspendedreview plans instead of remaining executable-but-invalid - older migrated plans get safe mechanical backfills for fields such as sprint
focus_paths, backlogtests, and newer runtime status sections - malformed
plan.yamlfiles are not silently rewritten;planguard checkreports them as per-plan parse failures with file and line information and continues checking the remaining plans
During upgrade, the CLI prints a summary of migrated plans, which plans were normalized, which ones were suspended for review, and which ones still need manual intervention.
Manual Fallback
If you only need to refresh the managed AGENTS workflow without migrating plan storage, the manual fallback is:
planguard init --refresh-agents --no-wizard
planguard check
Adopt the Newer Features
Existing plans remain compatible. Once upgraded, you can use the newer capabilities where they help:
- use
planguard plan --template <name>for tailored plan generation - use structured
verify_commandsentries for deterministic or shell-free verification - add
renames:mappings to plans that intentionally move files after activation - use
planguard suspend <plan>/planguard resume <plan>when overlapping work needs to pause safely
Setting Up a Project
planguard init
The wizard scans your repo, detects languages, frameworks, test/build commands, and creates:
| File | Purpose |
|---|---|
AGENTS.md |
Workflow rules that AI agents read before working |
.planguard/project.yaml |
System description and detected stack |
.planguard/conventions.md |
Coding patterns and style constraints |
.planguard/boundaries.md |
Files/directories agents must never modify |
.planguard/policies.yaml |
Governance rules (database, security, custom) |
.gitignore |
Updated to keep local plans and runtime state out of commits |
.planguard/state/active_plans.yaml |
Local runtime registry of active plans |
Review these files, then commit them. Any agent that reads AGENTS.md will follow the workflow.
Storage Layout
Plan definitions and runtime state are intentionally separated:
| Path | Purpose |
|---|---|
.planguard/plans/<plan_name>/plan.yaml |
Canonical plan definition |
.planguard/state/plans/<plan_name>/status.yaml |
Runtime status, activation baseline, verification results, handoff notes |
.planguard/state/active_plans.yaml |
Local registry of plans and statuses |
.planguard/state/log.jsonl |
Append-only lifecycle log |
Legacy repositories that still keep plans under docs/ can be migrated with planguard upgrade --no-wizard.
Creating a Plan
Run the wizard yourself:
planguard plan
Or tell the agent to create one:
Read AGENTS.md and the .planguard context. Create a PlanGuard plan for
"add JWT authentication to the API". Do not change application code yet.
Or skip the wizard entirely:
planguard plan "jwt-auth" --objective "Add JWT auth" --scope "src/api, tests" --priority high --no-wizard
Each plan produces:
<plans_root>/<plan_name>/plan.yamlfor the local plan definition.planguard/state/plans/<plan_name>/status.yamlfor local runtime progress state
That split is intentional: keep both plan definitions and runtime churn local by default, while only the install scaffold stays commit-worthy.
By default, plan definitions are stored under .planguard/plans/ and ignored via .gitignore. If you need a different local path, create .planguard/config.yaml:
plans_root: .planguard/plans # or any relative path
Templates
Use --template to generate plans tailored to specific change types:
planguard plan --template docs-only "update-readme" --objective "..." --no-wizard
planguard plan --template schema-change "add-users-table" --objective "..." --no-wizard
Available templates: default, docs-only, refactor, schema-change, service-integration. Each adjusts the generated phases, risks, and test strategy. When omitted, default is used — identical to the current behaviour.
Review the plan before activating.
Running Checks
planguard check # all plans
planguard check my_plan # specific plan
Checks: structure validation, risk scoring (severity-weighted, threshold 6), dependency graph cycles, scope collisions between plans, scope drift after activation, policy and boundary enforcement.
Activation and Implementation
planguard activate my_plan
# or, when you intentionally want repo-wide baseline capture:
planguard activate my_plan --baseline-mode repo
This re-runs checks, records a git-backed baseline, and marks the plan as active. Only now may the agent write code — and only within the declared scope. If the agent needs to touch files outside scope, update the plan first.
By default, activation captures a scoped baseline from the plan's scope.included paths. If the repo already has unrelated dirty files, PlanGuard records them separately as out-of-scope context instead of mixing them into the scoped baseline. This keeps baseline_changed_files focused on the plan while still preserving visibility into the rest of the worktree.
Example first prompt after activation:
The plan is now active. Implement the approved work for <plan_name>.
Only modify files inside the declared scope, update or add tests as
needed, run the relevant checks, and summarize what changed.
After implementation:
planguard verify my_plan # runs verify_commands, records passing snapshot
planguard complete my_plan # succeeds only if snapshot still matches
Small Changes
Not everything needs a plan. Typos, single-line fixes, formatting, and config tweaks can proceed directly.
Exception: database and schema changes are never small. Even adding a single column can require a migration, lock a table, or break downstream consumers. See Database Safety below.
Database Safety
PlanGuard ships with default protections for database work:
- Migration policy — plans touching
migrations/**,alembic/**, or**/migrations/**are flagged as high-risk - Schema-change policy — diffs containing SQL DDL or ORM migration operations are flagged
- Migration boundary —
migrations/is off-limits without an active plan
These are enforced by planguard check when a plan exists. For changes that bypass planning entirely, use guard:
planguard guard
Guard scans the staged diff (or unstaged changes) for migration files, schema DDL, and ORM operations — no plan required. It exits with code 1 if anything is found, making it suitable as a pre-commit hook.
Security
PlanGuard is not a security scanner — use Bandit, Semgrep, or CodeQL for that. But it complements them in two ways:
-
Policy rules —
.planguard/policies.yamlincludes commented-out security rules you can enable (hardcoded secrets, SQL injection, eval/exec, shell injection, disabled auth). These are regex-based guardrails, not a substitute for AST-aware analysis. -
Verification commands — add security scanners to a plan's
verify_commands(e.g.,bandit -r src/ -ll) so they run duringplanguard verifyand become part of the auditable lifecycle.
Verification Commands
verify_commands in plan.yaml supports three formats:
verify_commands:
# Plain string — runs via default shell (backward compatible)
- "pytest tests/ -v"
# Shell command with explicit interpreter — avoids cross-platform shell issues
- command: "Get-ChildItem *.ps1 | ForEach-Object { & $_.FullName }"
interpreter: pwsh
timeout: 120
# Non-shell argv invocation with per-command environment variables
- argv: ["dotnet", "test", "tests/PlanGuard.Tests.csproj"]
shell: false
env:
DOTNET_CLI_HOME: .planguard/tmp/dotnet
NUGET_PACKAGES: .planguard/tmp/nuget
# Structured assertion — no shell, declarative
- check: file_exists
path: src/config.py
- check: text_contains
path: README.md
pattern: "## Installation"
- check: file_moved
from: src/old_module.py
to: src/new_module.py
- check: text_not_contains
path: src/app.py
pattern: "TODO: remove"
Available structured checks: file_exists, file_not_exists, file_moved, text_contains, text_not_contains.
Command entries also support:
env: merge extra environment variables into the subprocess environmentshell: false: bypass the platform shell forcommandstringsargv: provide an explicit argument vector for the most portable non-shell execution path
The interpreter field solves cross-platform issues: PlanGuard uses the right invocation style for common interpreters (cmd /C, pwsh -Command, python -c, and shell-style -c for Unix shells) instead of relying on the default system shell. Use bash, sh, pwsh, powershell, cmd, python3, or any executable on PATH.
When a verification command fails or times out, PlanGuard now preserves the tail of partial stdout and stderr so timeout debugging does not lose the most relevant context.
All three formats can be mixed in the same list. Existing plans with plain string commands continue to work unchanged. Invalid structured entries fail verification with concise user-facing messages instead of raw tracebacks.
Policies and Boundaries
.planguard/policies.yaml defines pattern-based rules enforced by planguard check. Rules can block or require_approval, and are scoped to specific paths. Content-pattern rules are evaluated against actual changed files after activation.
.planguard/boundaries.md lists files and directories agents must never modify. If a plan's scope overlaps a boundary, planguard check blocks it.
Plan Lifecycle
Every plan moves through: draft → active → completed → archived
| Status | Meaning |
|---|---|
draft |
Plan exists, not yet approved for implementation |
active |
Checks passed, implementation allowed |
suspended |
Paused — other plans can proceed on overlapping scope |
completed |
Work done, verified |
archived |
Removed from active consideration |
Suspend and Resume
When one plan needs to pause while a smaller change goes through:
planguard suspend my_plan --reason "waiting on API deploy"
# ... other work proceeds, even on overlapping scope ...
planguard resume my_plan # picks up where it left off
planguard resume my_plan --refresh-baseline # re-capture baseline if repo changed significantly
Suspended plans retain their activation data and are excluded from collision detection.
Multiple Plans
Each piece of work gets its own plan with a declared scope. planguard check detects when two active plans have overlapping paths — collisions must be resolved before both can be active. planguard status shows a table of all plans.
If one plan is malformed, planguard status and planguard list still show the other plans and flag the invalid one with a concise parse summary.
For performance, status and list use cached verification results by default. Use --refresh-verification when you want current passed vs stale freshness against the repo.
Session Log
Every lifecycle event is logged to .planguard/state/log.jsonl — an append-only audit trail of what happened, when, and against which git state.
planguard log # all events
planguard log my_plan # filter by plan
All Commands
planguard init # Set up PlanGuard in a project (wizard)
planguard plan # Create a plan (wizard)
planguard plan --template <name> # Create from template: docs-only, refactor, schema-change, service-integration
planguard check [name] # Run all checks, or check a specific plan
planguard activate <name> # Mark plan as ready to implement
planguard activate <name> --baseline-mode repo
planguard verify <name> # Run verification commands (strings, interpreter, or structured checks)
planguard complete <name> # Mark plan as done (auto-fills handoff metadata)
planguard suspend <name> # Pause plan, unblock overlapping work
planguard resume <name> # Resume a suspended plan
planguard resume <name> --refresh-baseline --baseline-mode repo
planguard archive <name> # Archive a plan
planguard guard # Scan staged diff for database/schema risks
planguard status # Fast table of all plans with cached verification state
planguard status --refresh-verification
planguard list [--all] # Fast list view with cached verification state
planguard list --all --refresh-verification
planguard log [name] # Show session log (optionally filtered by plan)
planguard graph <name> # Show dependency graph for a plan
Compatibility
PlanGuard works with any agent that reads AGENTS.md — Claude, Codex, Cursor, Copilot, and others. It incorporates practices from the OpenAI Codex cookbook and Claude Code best practices.
Disabling or Removing
Override the risk threshold — add risk_threshold: 12 to the plan: section of any plan.yaml (default is 6).
Remove PlanGuard — delete .planguard/, the PlanGuard section from AGENTS.md, and uninstall the CLI (pipx uninstall planguard).
Requirements
- Python 3.9 or newer
- Works on Linux, macOS, and Windows
- No system dependencies beyond Python
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file planguard-0.7.4.tar.gz.
File metadata
- Download URL: planguard-0.7.4.tar.gz
- Upload date:
- Size: 58.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.12.3 Linux/6.6.87.2-microsoft-standard-WSL2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de437c300bd590d3ffa475d28d666ca92ec0b9f5cf7d3ac83b450461c7c67b87
|
|
| MD5 |
5a93f05eac43d84c61787454926e03e5
|
|
| BLAKE2b-256 |
2152dcb6dd7723e9b0fc5c17233df0e5f847b50ea2d169def8e06a11887ce1e5
|
File details
Details for the file planguard-0.7.4-py3-none-any.whl.
File metadata
- Download URL: planguard-0.7.4-py3-none-any.whl
- Upload date:
- Size: 63.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.12.3 Linux/6.6.87.2-microsoft-standard-WSL2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2d357631ba6baae7698df8e0d150b6244d417b7206a9a9c09f7ea9bdaad1c01
|
|
| MD5 |
37917c81660531922a0cbec7a7e2b08d
|
|
| BLAKE2b-256 |
e913bd551a298976c0d74b64b730e2149ba5d3e7d3a03d96562f58d33793afcb
|