prosecheck
v0.8.2
Published
LLM-powered code linter with natural-language rules
Maintainers
Readme
Prosecheck
Natural-language rules for AI coding agents. Write rules in plain English, and prosecheck enforces them in two ways:
- Deep checks — independent agents evaluate each rule against your codebase and report pass/warn/fail. Thorough but expensive; ideal for CI or periodic local runs.
- Inline reminders — rules are injected directly into your agent's context during long coding sessions, keeping it on track without spawning separate agents. Cheap and continuous.
Both modes use the same rules. A rule can participate in one or both.
┌────────┬───────────────────────────────────────────────────────────────┐
│ STATUS │ RULE │
├────────┼───────────────────────────────────────────────────────────────┤
│ PASS │ Request ID must propagate from endpoints to background jobs │
│ FAIL │ Architecture docs are updated when modules change │
└────────┴───────────────────────────────────────────────────────────────┘
1 failed | 1 passedHow It Works
You write rules as headings in RULES.md files placed anywhere in your project. Each rule has a mode that controls how it's enforced:
| Mode | How it works | Cost |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| independent | A separate Claude agent evaluates the rule against your codebase after changes. Reports pass/warn/fail. | High (one agent per rule) |
| reminderTimed | The rule body is injected verbatim into your agent's context every N tool calls. No LLM evaluation. | Near zero |
| reminderPrompt | Every N tool calls, a lightweight agent checks whether the rule is relevant to recent work. If yes, the rule is injected. | Low (one Haiku call per check) |
Rules default to independent if no mode is specified. You can combine modes — a rule with modes: [independent, reminderTimed] gets both deep checks and inline reminders.
Features
- Natural-language rules — enforce conventions that traditional linters can't express: architectural boundaries, naming consistency, documentation standards, API design patterns
- Two enforcement modes — deep independent checks for thorough evaluation, inline reminders for continuous lightweight guidance during coding sessions
- Scoped rules — place
RULES.mdin any directory to scope its rules there.src/api/RULES.mdonly triggers whensrc/api/files change - Incremental — only rules affected by your changes are evaluated
- Multiple output formats — terminal (stylish), JSON, and SARIF
- Configurable dispatch — one agent per rule, parallel sub-agents, or sequential single-agent
- CI-ready — auto-detects CI environments, supports SARIF upload to GitHub Code Scanning
- ADR rules — extract rules from Architecture Decision Records automatically
Installing
Prerequisites
- Git (for change detection)
- Claude Code (the default execution backend)
- Node.js >= 20 (for the npm install route)
From npm
npm install -D prosecheckOr run directly:
npx prosecheck lintQuick Setup
Initialize prosecheck in your project:
prosecheck init --rulesThis creates:
.prosecheck/config.json— configuration fileRULES.md— a starter rules file with examples
Edit RULES.md to define your own rules:
This file defines conventions that should be followed. Each rule is checked by prosecheck. Rule names are headings; rule contents are everything until the next heading or the end of file.
# Request ID must propagate from endpoints to background jobs
The `requestId` from incoming API requests must be threaded through to all
background job dispatches (queue publishers, async workers). This ensures
end-to-end tracing across synchronous and asynchronous boundaries. Check
that job dispatch calls include the request ID from context, not a newly
generated one.
# Architecture docs are updated when modules change
When a module under `src/lib/` is added, removed, or has its public API
changed, the corresponding section in `docs/architecture/ARCHITECTURE.md`
must be updated in the same PR. New modules need a new section. Removed
modules should have their section deleted. Signature changes should be
reflected in the description.Rules are markdown headings. The body is the rule description — write as much or as little detail as you need.
Scope is determined by where you place the RULES.md file: rules apply to everything in the file's directory and below. Place it at the highest level you want the rules to apply to. A file at src/api/RULES.md only triggers when files under src/api/ change. A RULES.md at the project root applies to everything.
Run the linter:
prosecheck lintOutput:
┌────────┬───────────────────────────────────────────────────────────────┐
│ STATUS │ RULE │
├────────┼───────────────────────────────────────────────────────────────┤
│ PASS │ Request ID must propagate from endpoints to background jobs │
│ FAIL │ Architecture docs are updated when modules change │
└────────┴───────────────────────────────────────────────────────────────┘
1 failed | 1 passedOutput formats
prosecheck lint --format stylish # default, colored terminal output
prosecheck lint --format json # machine-readable JSON
prosecheck lint --format sarif # SARIF 2.1.0 for GitHub Code ScanningCI Setup
Option 1: Full check on every push
The simplest approach. Every push runs all triggered rules from scratch.
prosecheck init --github-actionsGenerates a GitHub Actions workflow that runs prosecheck lint --last-run-read 0 on every push. No incremental state, no extra requirements.
Requirements:
ANTHROPIC_API_KEYin your repository secrets (used by Claude Code)
Option 2: Incremental with merge queue
For larger projects where running all rules on every push is too expensive.
prosecheck init --github-actions-incrementalGenerates two workflows:
- PR push: runs
prosecheck lint --last-run-read 1(only checks rules not covered by a previous run) - Merge queue: runs
prosecheck lint --last-run-read 0(full check before merge)
Also sets lastRun.write=true for the interactive environment so local runs persist the hash.
Requirements:
ANTHROPIC_API_KEYin your repository secrets- GitHub merge queue enabled on your repository (ensures the full check runs before merge)
Option 3: Hash check (zero token cost)
For teams that run prosecheck locally but want CI to verify it actually happened.
prosecheck init --github-actions-hash-checkGenerates a workflow that checks whether .prosecheck/last-user-run matches the current commit — confirming someone ran prosecheck before pushing. No LLM calls in CI, no API key needed. Relies on developers running locally with --last-run-write 1 and committing the hash file.
Inline Reminders
Inline reminders keep your coding agent aligned with your rules during long sessions — without spawning expensive independent checks. Rules are injected into the agent's context via Claude Code hooks.
Setting up reminders
1. Add modes to your rules
In RULES.md, add frontmatter to opt rules into reminder modes:
# Always use structured logging
---
modes: [independent, reminderTimed]
reminderTimedFrequency: 5
---
All log calls must use the structured logger (`logger.info({...})`), never
`console.log`. This ensures logs are parseable by our observability pipeline.| Frontmatter field | Description |
| ------------------------- | --------------------------------------------------------------------- |
| modes | List of modes: independent, reminderTimed, reminderPrompt |
| reminderTimedFrequency | For reminderTimed: inject every N tool calls (default: 10) |
| reminderPromptFrequency | For reminderPrompt: evaluate every N tool calls (default: 10) |
| reminderPromptHistory | For reminderPrompt: number of recent transcript entries for context |
| reminderPromptTemplate | For reminderPrompt: custom evaluation prompt template |
A rule with no modes frontmatter defaults to independent only.
2. Install hooks
prosecheck install-hooksThis writes Claude Code hook entries into .claude/settings.local.json (gitignored, personal to your machine). Prosecheck registers command hooks on SessionStart and PostToolUse that manage reminder injection.
3. Check status
prosecheck statusShows session counter values and active reminder rules.
How it works at runtime
When Claude Code calls a tool, the PostToolUse hook fires:
Timed reminders — prosecheck increments a counter. When a
reminderTimedrule's frequency threshold is reached, its text is injected into the conversation as a hook message.Prompt reminders — when a
reminderPromptrule's frequency threshold is reached, prosecheck reads recent transcript entries, builds a self-contained prompt with the rule text + activity context, and spawns a lightweightclaude --print --model haikusubprocess. If the subprocess detects a violation, the response is injected as a[prosecheck reminder]message. If the rule passes, nothing is injected.
Configuration
Environments
Prosecheck auto-detects the environment from process.env.CI or defaults to interactive. Override with --env:
prosecheck lint --env ciEnvironment-specific settings are defined in config.json under environments:
{
"environments": {
"ci": { "warnAsError": true, "timeout": 600 },
"interactive": {}
}
}Local overrides
Create .prosecheck/config.local.json for personal settings that aren't committed to the repo (it's gitignored automatically):
{
"timeout": 60,
"lastRun": { "write": true }
}Run modes
Control how rules are dispatched to Claude via claudeCode.claudeToRuleShape:
| Mode | Description |
| -------------------- | ------------------------------------------------------------------------- |
| one-to-many-teams | (default) Rules packed into team invocations with parallel sub-agents |
| one-to-one | One Claude process per rule |
| one-to-many-single | All rules in one process, evaluated sequentially |
prosecheck lint --claude-to-rule-shape one-to-oneAdditional rule sources
Beyond RULES.md files, prosecheck can extract rules from Architecture Decision Records:
{
"ruleCalculators": [
{ "name": "rules-md" },
{ "name": "adr", "options": { "path": "docs/adr" } }
]
}ADRs that contain a ## Rules section automatically generate prosecheck rules. ADRs without that heading are ignored. Sub-rules (### Sub-rule) within the section become separate rules; otherwise the entire section is one rule named after the ADR title.
More info
Run any command with --help for available options:
prosecheck lint --help
prosecheck init --help
prosecheck config --help
prosecheck install-hooks --help
prosecheck status --helpView all configuration fields, their current values, and descriptions:
prosecheck config listProject Internals
Prosecheck is TypeScript (strict mode, ESM-only, Node >= 20). Built with tsup, tested with vitest.
Repository structure
src/
cli.ts # Commander-based CLI entry point
commands/ # lint, init, config, install-hooks, hook, status
lib/ # Core: config, engine, change detection, prompts, results
calculators/ # Rule discovery (rules-md, adr)
modes/ # Execution backends (claude-code, user-prompt)
formatters/ # Output formatting (stylish, json, sarif)
ui/ # Ink/React interactive terminal UI
docs/
architecture/ # Architecture documentation
roadmap/ # Roadmap and milestone tracking
adr/ # Architectural Decision Records
plans/ # Original design documentsDocumentation
- Architecture — system design, data flow, module descriptions
- Roadmap — current and completed milestones
- ADRs — architectural decision records
- Design plans — original design documents
