noxa
Passively captures business rules from your Claude Code conversations and turns them into typed validation code.
Stop re-teaching your AI the same rules every session.
noxa hooks into Claude Code and automatically captures business rules from your conversations — then turns them into validation code, injects them into CLAUDE.md, and exposes them via an MCP server so Claude never forgets a constraint again.
You: "user.email cannot be null, I've said this a hundred times"
noxa: ✓ captured — validate_user_email_required() generated
The problem
Every time you start a new Claude Code session, your AI assistant has forgotten everything you taught it last time.
Session 1: You: "invoice amounts cannot be negative" → Claude fixes it
Session 2: Claude generates negative invoice amounts again
Session 3: You repeat yourself. Again.
Domain rules, validation constraints, invariants — they live in your chat history and nowhere else. New team member? New session? Start from zero.
noxa makes your corrections permanent.
How it works
Claude Code session
│
▼ Notification hook (passive, zero latency)
noxa intercept
│
├─ 21 regex patterns → entity + constraint extracted
├─ Confidence scoring → corrections get a boost
└─ .noxa/rules.toml ←─ atomic write, git-versioned
│
┌──────┴──────────────────────┐
▼ ▼
CLAUDE.md validation.rs / .ts / .py
(next session (typed functions,
starts informed) ready to call)
│
noxa mcp ←─ Claude queries rules in real time
Install
cargo install noxa
Requires Rust 1.70+ and Claude Code.
5-minute setup
Step 1 — Initialize
cd my-project && noxa init
Step 2 — Configure Claude Code
Add both the hook and the MCP server to .claude/settings.json:
{
"hooks": {
"Notification": [
{
"matcher": ".*",
"hooks": [{ "type": "command", "command": "noxa intercept" }]
}
]
},
"mcpServers": {
"noxa": { "command": "noxa", "args": ["mcp"] }
}
}
Run
noxa hook-configandnoxa mcp-configto print these snippets individually.
Step 3 — Work with Claude Code as usual. noxa listens silently.
Step 4 — After your session
noxa list # see what was captured
noxa review # [c] confirm [d] delete [s] skip
noxa inject # → update CLAUDE.md (next session starts informed)
noxa generate # → .noxa/validation.rs
noxa generate --typescript # → .noxa/validation.ts
noxa generate --python # → .noxa/validation.py
What gets captured
noxa recognizes constraint patterns in English and French — no configuration needed.
| What you say to Claude | What noxa captures |
|---|---|
| "user.email cannot be null" | user.email → Required |
| "only one subscription per user" | subscription → Unique per user |
| "trial period cannot exceed 14 days" | trial.duration → Range [0, 14] |
| "plan_id cannot be changed after billing" | subscription.plan_id → Immutable |
| "username cannot exceed 32 characters" | user.username → Length max 32 |
| "email must be a valid email" | user.email → Format (regex) |
| "delete is forbidden" | delete → Forbidden |
| "un seul rapport par portail" | rapport → Unique per portail |
| "le solde ne doit jamais être négatif" | solde → Forbidden |
| "il ne peut y avoir qu'un seul admin" | admin → Unique |
21 patterns total. Ambiguous phrases? Run noxa enrich — Claude Haiku improves the extraction automatically.
Generated code
Same rule, three languages. Pick what your stack uses.
Rust:
/// Auto-generated by noxa
pub fn validate_user_email_required(value: &str) -> Result<(), &'static str> {
if value.trim().is_empty() { return Err("value is required and cannot be empty"); }
Ok(())
}
TypeScript:
/** Auto-generated by noxa */
export function validateUserEmailRequired(value: unknown): void {
if (value === null || value === undefined || String(value).trim() === '') {
throw new Error('value is required and cannot be empty');
}
}
Python:
def validate_user_email_required(value: str) -> None:
"""user.email must be required"""
if not str(value).strip():
raise ValueError("value is required and cannot be empty")
MCP server — Claude queries rules before coding
Add noxa mcp-config output to .claude/settings.json and Claude can call your rule store directly during a session:
Claude: [calls noxa_list_rules]
→ subscription.status must be unique per user
→ user.email must be required
→ trial.duration must be range [0, 14]
Claude: [generates code that already respects all three constraints]
No more corrections. No more re-teaching.
Available MCP tools: noxa_status · noxa_list_rules · noxa_explain_rule · noxa_confirm_rule · noxa_delete_rule
Audit — find unenforced rules
noxa audit
STATUS FUNCTION RULE
────────────────────────────────────────────────────────────────────────────────
enforced validate_user_email_required user.email must be required
ts: validateUserEmailRequired
src/validators.ts
missing validate_subscription_status_… subscription.status unique per user
ts: validateSubscriptionStatusUniqueness
1/2 rules enforced — run `noxa generate` to create the missing function.
Scans .rs .ts .tsx .js .jsx .py — matches both Rust and TypeScript function names. Excludes target/ and node_modules/ automatically.
Share rules across projects
# Export confirmed rules
noxa export --confirmed > shared-rules.toml
# Import into another project (deduplication built-in)
cd other-project && noxa import ../shared-rules.toml
# ✓ imported 8/10 rules (2 already present)
All commands
| Command | What it does |
|---|---|
noxa init |
Initialize .noxa/ in the current project |
noxa status |
Dashboard: detected / confirmed / enforced |
noxa list |
List all rules with ID, confidence, summary |
noxa list --confirmed |
Confirmed rules only |
noxa list --json |
JSON output for scripting |
noxa review |
Interactive: confirm / delete / skip pending rules |
noxa confirm <id> |
Confirm a rule by ID prefix |
noxa delete <id> |
Delete a rule |
noxa explain <id> |
Full rule details |
noxa enrich |
Use Claude Haiku to improve low-confidence extractions |
noxa inject |
Inject confirmed rules into CLAUDE.md |
noxa generate |
Generate .noxa/validation.rs |
noxa generate --typescript |
Generate .noxa/validation.ts |
noxa generate --python |
Generate .noxa/validation.py |
noxa audit |
Find which confirmed rules are used in the codebase |
noxa export |
Export rules to a portable TOML file |
noxa import <file> |
Import rules from an exported file |
noxa mcp |
Start the MCP server (stdio transport) |
noxa doctor |
Diagnose setup issues |
noxa hook-config |
Print the Claude Code hook snippet |
noxa mcp-config |
Print the MCP server configuration snippet |
Global flag: --root <path> to target a project other than the current directory.
Rule store
Rules live in .noxa/rules.toml — commit it. It's project state.
[[rules]]
id = "a3f2b1c0-4d2e-4f7a-b1c0-9d2e4f7a3b1c"
raw = "only one active subscription per user"
entity = "subscription"
property = "status"
confidence = 0.91
detected_at = "2026-07-08T14:23:00Z"
confirmed = true
[rules.constraint]
type = "unique"
per = "user"
Deduplication is built in — if noxa detects the same rule twice, it updates the confidence and moves on.
CLAUDE.md injection
After noxa inject, every new session starts with full context:
<!-- noxa:rules:start -->
## Business Rules (auto-captured by noxa)
- **subscription.status must be unique per user**: only one active subscription per user
- **user.email must be required**: user.email cannot be null
- **trial.duration must be range [0, 14]**: trial period cannot exceed 14 days
<!-- noxa:rules:end -->
Idempotent — safe to run multiple times.
Why noxa
- Zero friction. Passive hook. You don't change how you work.
- Git-native. Rules are a TOML file. Diff them, review them, version them.
- Multi-language. Rust, TypeScript, Python — generate once, use anywhere.
- MCP-ready. Claude queries constraints proactively instead of waiting for corrections.
- AI-assisted recovery.
noxa enrichuses Claude Haiku to improve fuzzy extractions. - Open source. MIT. No telemetry. No cloud. Your rules stay in your repo.
Roadmap
-
noxa export / importacross teams with domain tagging - Spanish, German, Portuguese detection patterns
- VS Code extension for inline rule visualization
Contributing
If noxa misses a rule pattern from your real usage, open an issue with the exact phrase. Detection coverage is built directly from real-world examples.
License
MIT — see LICENSE.
Part of rustkit-ai — open source Rust tools for the AI development era.