Rules & Instructions
Auto-discover and apply persistent rules like Cursor, Windsurf, Claude, and Codex
PraisonAI auto-discovers per-project instruction files (AGENTS.md, CLAUDE.md, .praisonai/rules/*.md) and injects them into every agent run — CLI and direct Agent(...) from Python. Opt out with rules=False when you need a sandboxed / reproducible run.
The user works in a repo with instruction files; the agent discovers them and injects the rules into the system prompt on every run.
How It Works
always rules ship in the cached base prompt above; glob rules follow a second, per-turn path shown next.
Glob rules — per-turn
What you’ll see when rules are applied
When one or more rules are auto-discovered, a single WARNING-level log message prints the first time the agent loads its rules (exact prefix depends on your logging setup):
Applied rules: AGENTS.md, CLAUDE.md (pass rules=False to turn off)- Lists only rules actually injected (names sorted, comma-separated) —
activation: manualrules aren’t listed. - Logged once per distinct rule-name set per process — repeated
Agent(...)with the same active set stay quiet. - Silent when no rule files are discovered.
- Pass
rules=Falseto suppress discovery (and the line) for a sandboxed run.
Supported Instruction Files
PraisonAI automatically loads these files from your project root and git root:
| File | Description | Priority |
|---|---|---|
PRAISON.md | PraisonAI native instructions | High (500) |
PRAISON.local.md | Local overrides (gitignored) | Higher (600) |
CLAUDE.md | Claude Code memory file | High (500) |
CLAUDE.local.md | Local overrides (gitignored) | Higher (600) |
AGENTS.md | OpenAI Codex CLI instructions | High (500) |
GEMINI.md | Gemini CLI memory file | High (500) |
.cursorrules | Cursor IDE rules (legacy) | High (500) |
.windsurfrules | Windsurf IDE rules (legacy) | High (500) |
.claude/rules/*.md | Claude Code modular rules | Medium (50) |
.windsurf/rules/*.md | Windsurf modular rules | Medium (50) |
.cursor/rules/*.mdc | Cursor modular rules | Medium (50) |
.praisonai/rules/*.md | Workspace rules | Medium (0) |
~/.praisonai/rules/*.md | Global modular rules | Low (-1000) |
~/.praisonai/AGENTS.md | Global single-file instructions | Lowest (loaded by load_context_files()) |
Quick Start
- Simple
Drop an instruction file in your project — the agent picks it up automatically.
python from praisonaiagents import Agent agent = Agent( name="Assistant", instructions="You help users with Python.", rules=True, # same as omitting — auto-discover + apply ) agent.start("Sort a list of dicts by the 'priority' key.") - With Configuration
Use
RulesConfigwhen you need a character budget, extra rule files, or a different workspace root.python from praisonaiagents import Agent, RulesConfig agent = Agent( name="Assistant", instructions="You help users with Python.", rules=RulesConfig( char_budget=6000, files=["docs/CONVENTIONS.md"], ), ) agent.start("Refactor this module in line with our conventions.")
Precedence
rules= climbs the ladder from simplest to most explicit — Bool then RulesConfig — with rules=False as a standalone opt-out.
| You pass | What happens |
|---|---|
Nothing (omit rules=) | Auto-discover and apply if files exist (prints one Applied rules: … warning) |
rules=None | Same as omitting — auto-discover and apply |
rules=True | Same as above — auto-discover and apply |
rules=RulesConfig(...) | Full control (custom budget / files / workspace) |
rules=False | Off — no discovery, no injection, sandboxed run |
CLI Usage
Project instruction files are auto-injected into praisonai run, chat, and code — no Python required.
# 1. Create an instruction file in your project
echo "Always use type hints" > AGENTS.md
# 2. Run any CLI command — instructions load automatically
praisonai run "Help me with Python"Rule File Format
Rules support YAML frontmatter for advanced configuration:
# Project Guidelines
- Always use type hints for functions
- Follow PEP 8 style guide
- Write docstrings for public APIs
- Prefer async when possible---
description: Python coding guidelines
globs: ["**/*.py", "**/*.pyx"]
activation: always
priority: 10
---
# Python Guidelines
- Use type hints for all functions
- Follow PEP 8 style guide
- Write docstrings for public APIs
- Prefer composition over inheritance---
description: Testing guidelines
globs: ["**/*.test.*", "**/test_*.py", "**/*_test.py"]
activation: glob
priority: 20
---
# Testing Guidelines
- Use pytest for all tests
- Aim for 80% code coverage
- Mock external dependencies
- Use fixtures for common setup---
description: Security review checklist
activation: manual
priority: 100
---
# Security Checklist
- [ ] Validate all user inputs
- [ ] Use parameterized queries
- [ ] Implement rate limiting
- [ ] Check for SQL injection
- [ ] Verify authenticationActivation Modes
| Mode | Description | Use Case |
|---|---|---|
always | Always applied (default) — ships in the cached base prompt | General guidelines |
glob | Injected only for files the agent has actually touched this turn; matches paths that appear in the running conversation | Language-specific rules |
manual | Only via @mention | Security checklists, special procedures |
ai_decision | AI decides when to apply | Context-dependent rules |
How path-scoped rules activate
Rules with activation: glob fire the moment your agent reads or edits a matching file — no wiring needed.
from praisonaiagents import Agent
agent = Agent(
name="Dev",
instructions="Refactor the codebase",
)
agent.start("Open foo.py and add type hints") # → python.md rule activatesThe rule lives on disk as usual:
---
activation: glob
globs: ["**/*.py"]
---
Use type hints on every function and method.Pick an activation mode based on when the rule should apply:
Path-Scoped Rules in Action
Author a glob rule and it fires the moment the agent touches a matching file during a run.
from praisonaiagents import Agent
# File on disk: .praisonai/rules/python.md
# ---
# description: Python style
# globs: ["**/*.py"]
# activation: glob
# ---
# Use type hints on every function.
agent = Agent(
name="Coder",
instructions="Help with code.",
)
agent.start("Open foo.py and describe it")
# ↑ mentions foo.py → python.md rule is injected this turn.A rule with globs: ["**/*.py"] fires only when a filename token actually appears in the conversation:
| User / tool text mentions | Rule (globs: ["**/*.py"]) fires? |
|---|---|
"open foo.py" | ✅ yes |
"src/main.py" | ✅ yes |
"the .py file" | ❌ no (no filename token) |
"hello world" | ❌ no |
Agent reads foo.js | ❌ no (extension does not match) |
Configuration (RulesConfig)
Pass a RulesConfig when you need more than the default zero-config behaviour.
from praisonaiagents import Agent, RulesConfig
agent = Agent(
name="Assistant",
instructions="You are helpful.",
rules=RulesConfig(
char_budget=6000,
files=["docs/CONVENTIONS.md"],
workspace_path="/path/to/project",
),
)
agent.start("Summarise the project conventions.")| Option | Type | Default | Description |
|---|---|---|---|
char_budget | Optional[int] | None | Character budget for injected rules context. None = RulesManager default. |
files | Optional[List[str]] | None | Extra instruction file paths / globs registered with high priority beyond auto-discovery. |
workspace_path | Optional[str] | None | Workspace path override. Defaults to os.getcwd(). |
Choose an option
@Import Syntax (like Claude Code)
Include other files in your rules using the @path/to/file syntax:
# CLAUDE.md
See @README for project overview
See @docs/architecture.md for system design
# Additional Instructions
- Follow the patterns in @src/examples/# CLAUDE.md
# Import personal preferences from home directory
@~/.praisonai/my-preferences.md
# Project-specific rules
- Use TypeScript for all new code# .praisonai/rules/python.md
# Import shared guidelines
@./shared/code-style.md
# Python-specific additions
- Use type hints
- Follow PEP 8Git Root Discovery
PraisonAI automatically discovers rules from the git repository root, enabling monorepo support:
monorepo/ # Git root
├── .git/
├── CLAUDE.md # Discovered for all subdirs
├── .praisonai/rules/ # Discovered for all subdirs
├── packages/
│ ├── frontend/
│ │ ├── CLAUDE.md # Higher priority for frontend
│ │ └── .praisonai/rules/
│ └── backend/
│ └── CLAUDE.md # Higher priority for backendRules closer to the current working directory have higher priority than rules at the git root.
How git-root detection works
PraisonAI walks up from the workspace path looking for a .git entry (directory or file — worktrees and submodules use a .git file), without spawning a subprocess.
The wrapper-layer load_context_files() (used by configure_host / context_paths) uses the same git-root walk-up and adds layered merge semantics — collecting AGENTS.md, CLAUDE.md, agents.md, and .agents/AGENTS.md at every directory level from ~/.praisonai/AGENTS.md (lowest) up through the git root to cwd (highest). See Context Files → CLI auto-loading for how chat, run, code, and tui automatically inject this context into the agent system prompt.
Monorepos with per-subtree instruction files: see On-demand Subtree Attachment for how a sibling package’s AGENTS.md attaches the moment the agent opens a file inside it.
How subdirectory rules are discovered
PraisonAI loads the instruction files at your workspace (or git) root, then walks up from your current directory toward that root, picking up .praisonai/rules/, .claude/rules/, .cursor/rules/, and .windsurf/rules/ in each folder along the way.
That walk stays inside the workspace. If your current directory is outside it, the walk is skipped — rule folders in ~/.claude/rules, ~/.cursor/rules, or other unrelated parent folders are never loaded just because of where your shell happens to be.
| Where is your shell? | Where is workspace_path? | Subfolder walk runs? | How to load parent-folder rules |
|---|---|---|---|
Inside the workspace (e.g. project/src) | project/ | ✅ Yes | Already loaded |
| At the workspace root | project/ | — Nothing to walk | Not needed |
| Monorepo root | A subpackage (repo/packages/api) | ❌ No | Point workspace_path at the repo root |
Repo root, running pytest | A temp folder (tmp_path) | ❌ No | RulesConfig(files=[...]) |
from praisonaiagents import Agent, RulesConfig
# Shell outside the workspace? Register the rules you still want explicitly.
agent = Agent(
name="Dev",
instructions="Help with code.",
rules=RulesConfig(files=["docs/CONVENTIONS.md"]),
)
agent.start("Summarise the project conventions.")See also: How git-root detection works and On-demand Subtree Attachment, which attaches subtree instructions regardless of your current directory.
Storage Structure
project/
├── CLAUDE.md # Auto-loaded (Claude Code compatible)
├── CLAUDE.local.md # Local overrides (gitignored)
├── AGENTS.md # Auto-loaded (Codex CLI compatible)
├── GEMINI.md # Auto-loaded (Gemini CLI compatible)
├── PRAISON.md # Auto-loaded (PraisonAI native)
├── PRAISON.local.md # Local overrides (gitignored)
├── .cursorrules # Auto-loaded (Cursor legacy)
├── .windsurfrules # Auto-loaded (Windsurf legacy)
├── .claude/
│ └── rules/ # Claude Code modular rules
│ ├── code-style.md
│ └── testing.md
├── .windsurf/
│ └── rules/ # Windsurf modular rules
├── .cursor/
│ └── rules/ # Cursor modular rules
├── .praisonai/
│ ├── rules/ # Workspace rules
│ │ ├── python.md # globs: ["**/*.py"]
│ │ ├── testing.md # globs: ["**/*.test.*"]
│ │ └── security.md # activation: manual
│ ├── workflows/ # Reusable workflows
│ └── hooks.json # Pre/post operation hooks
└── ~/.praisonai/
└── rules/ # Global rules (all projects)
└── global.mdSecurity & Path Safety
The praisonai.rules.show, create, and delete MCP tools validate rule names to prevent path traversal:
Rejected inputs:
- Contains
/,\, or\x00(NUL byte) - Starts with
.(blocks hidden files and..) - Equals
.or.. - The resolved path escapes
~/.praisonai/rules/(symlink-safe)
Allowed pattern: Single filename with alphanumerics, underscores, hyphens, and dots (not leading), located strictly inside ~/.praisonai/rules/.
Error format: Error: invalid rule_name: '<value>'
Programmatic Rules Management
from praisonaiagents.memory import RulesManager
# Create manager for your workspace
rules = RulesManager(workspace_path="/path/to/project")
# Get all loaded rules
all_rules = rules.get_all_rules()
for rule in all_rules:
print(f"{rule.name}: {rule.description} (priority: {rule.priority})")
# Get active rules (always + ai_decision)
active = rules.get_active_rules()
# Get rules for a specific file (includes glob matches)
python_rules = rules.get_rules_for_file("src/main.py")
# Cheap gate — True if any activation: glob rule is loaded.
if rules.has_glob_rules():
# Return glob rules matching any of these paths,
# skipping names already emitted in the base prompt.
matching = rules.get_glob_rules_for_paths(
["foo.py", "src/main.py"],
exclude_names={"root", "python"},
)
for rule in matching: # sorted by priority, highest first
print(rule.name, rule.description)
# Get a specific rule by name (for @mention)
security_rule = rules.get_rule_by_name("security")
# Build context string for LLM
context = rules.build_rules_context(
file_path="src/main.py",
include_manual=["security"] # Include manual rules via @mention
)
# Register extra instruction files beyond auto-discovery.
# Accepts a concrete path OR a glob; each match wins over auto-discovered
# rules via a +200 priority bump. Returns the number of files added.
added = rules.add_rule_file("docs/CONVENTIONS.md") # concrete path
added = rules.add_rule_file(".praisonai/team-rules/*.md") # globThe two glob helpers power the per-turn path-scoped injection shown in Path-Scoped Rules in Action.
| Method | Signature | Returns |
|---|---|---|
has_glob_rules | () -> bool | True if any glob-activation rule is loaded. Cheap gate used to short-circuit per-turn work when no glob rules exist. |
get_glob_rules_for_paths | (file_paths: List[str], exclude_names: Optional[set] = None) -> List[Rule] | Glob rules matching any of the given paths, deduped by name, exclude_names skipped, sorted by priority descending. |
Creating Rules Programmatically
from praisonaiagents.memory import RulesManager
rules = RulesManager(workspace_path="/path/to/project")
# Create a new rule
rules.create_rule(
name="api_guidelines",
content="""# API Guidelines
- Use RESTful conventions
- Version all endpoints
- Return proper status codes
- Document with OpenAPI
""",
description="REST API development guidelines",
globs=["**/api/**/*.py", "**/routes/**/*.py"],
activation="glob",
priority=15,
scope="workspace" # or "global"
)
# Delete a rule
rules.delete_rule("old_rule")
# Reload rules from disk
rules.reload()
# Get statistics
stats = rules.get_stats()
print(stats)
# {
# 'total_rules': 8,
# 'always_rules': 3,
# 'glob_rules': 2,
# 'manual_rules': 1,
# 'ai_decision_rules': 2,
# 'root_rules': 3,
# 'gitroot_rules': 1,
# 'workspace_rules': 2,
# 'claude_rules': 1,
# 'windsurf_rules': 1,
# 'global_rules': 0,
# 'sources': {'root': 3, 'workspace': 2, 'claude': 1, ...}
# }AI Decision Activation
For ai_decision rules, PraisonAI can use an LLM to decide if a rule should be applied:
from praisonaiagents.memory import RulesManager
rules = RulesManager(workspace_path="/path/to/project")
# Get an ai_decision rule
rule = rules.get_rule_by_name("security")
# Evaluate if it should be applied to current context
def llm_func(prompt):
return agent.chat(prompt)
should_apply = rules.evaluate_ai_decision(
rule=rule,
context="User is asking about database queries",
llm_func=llm_func
)
if should_apply:
print("Security rule should be applied")Agent Integration
Rules are auto-applied on every .start() / .chat() / .run() call — no manual wiring needed.
from praisonaiagents import Agent, RulesConfig
# Default: auto-discover + apply (equivalent to rules=True or omitting the param)
agent = Agent(name="Coder", instructions="Help with code")
# Opt out for a reproducible sandbox run
sandbox = Agent(name="Sandbox", rules=False)
# Full control
strict = Agent(
name="Strict",
rules=RulesConfig(char_budget=4000, files=["docs/CONVENTIONS.md"]),
)Inspect what was loaded
from praisonaiagents import Agent
agent = Agent(name="Assistant", instructions="You are helpful.")
agent.start("hello") # triggers discovery on first run
if agent.rules_manager:
stats = agent.rules_manager.get_stats()
print(f"Loaded {stats['total_rules']} rules from {list(stats['sources'].keys())}")
# Preview the exact string injected into the system prompt
context = agent.get_rules_context(
file_path="src/main.py",
include_manual=["security"]
)
print(context)CLI Integration
Project instruction files are auto-injected into praisonai run, praisonai chat, and praisonai code runs — no Python required.
CLI flags
| Flag | Effect |
|---|---|
| _(default)_ | Auto-inject all discovered instruction files |
--no-rules | Skip auto-injection for this run |
--include-rules <names> | Force-include named rules (comma-separated) |
--include-rules auto | Explicitly enable auto (same as default) |
--verbose | Print loaded files: Loaded project instructions: AGENTS.md, CLAUDE.md |
Configuration
Global opt-out via ~/.praisonai/config.toml:
[rules]
auto = false # Disable everywhere
max_chars = 16000 # Or shrink the budgetSee CLI Config for the full TOML reference.
Cross-Tool Compatibility
PraisonAI rules are compatible with other AI coding assistants:
| Tool | File | Compatibility |
|---|---|---|
| Claude Code | CLAUDE.md | ✅ Full |
| Codex CLI | AGENTS.md | ✅ Full |
| Gemini CLI | GEMINI.md | ✅ Full |
| Cursor | .cursorrules, .cursor/rules/*.mdc | ✅ Partial |
| Windsurf | .windsurfrules, .windsurf/rules/*.md | ✅ Partial |
Best Practices
Running outside a git repo? No action needed
PraisonAI walks up looking for .git, then stops. Containers, serverless functions, and other non-git deploys pay zero cost for git-root detection. If your runtime relies on GIT_DIR / GIT_WORK_TREE to locate the repo, PraisonAI honors them.
Use PRAISON.md for project-specific rules
Create a PRAISON.md file in your project root with guidelines specific to your project. This file is automatically loaded with high priority.
Use glob patterns for language-specific rules
Put language-specific rules in .praisonai/rules/ with appropriate glob patterns. For example, python.md with globs: ["**/*.py"].
Use manual activation for checklists
Security checklists, deployment procedures, and other special rules should use activation: manual and be invoked via @mention when needed.
Set appropriate priorities
Use higher priority (50+) for critical rules that should override others. Default priority is 0 for workspace rules, -1000 for global rules.
Keep rules concise
Rules are injected into the system prompt, consuming context window. Keep rules focused and concise to leave room for conversation.