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.

Diagram

Rules & Instructions

📋 User Request

⚙️ Rules & Instructions

✅ Result

The user works in a repo with instruction files; the agent discovers them and injects the rules into the system prompt on every run.

Diagram

Rules & Instructions

📝 Prompt

🔍 Discover Rule Files

⚙️ Inject Rules

🤖 Agent

✅ Rule-aware Reply

How It Works

Diagram
📢 Logger🧠 LLM📂 Project Folder📚 RulesManager🤖 Agent👨‍💻 Developer📢 Logger🧠 LLM📂 Project Folder📚 RulesManager🤖 Agent👨‍💻 DeveloperSubfolder rules between cwd and workspaceload only when cwd is inside the workspaceCreate AGENTS.md“Use type hints”Agent(instructions=”…”, rules=True)agent.start(“sort a list”)First run → lazy _init_rules_managerDiscovery gate → scan workspaceAGENTS.md, CLAUDE.md, …rules context (build_system_prompt)WARNING “Applied rules: … (pass rules=False to turn off)”system prompt + rules + taskdef sort_list(items: list[int]) -> list[int]: …

always rules ship in the cached base prompt above; glob rules follow a second, per-turn path shown next.

Glob rules — per-turn

Diagram
LLMRulesManagerChatMixinAgentUserLLMRulesManagerChatMixinAgentUseragent.start(“open foo.py …”)_build_system_prompt()base prompt (cached, always+manual)has_glob_rules()?Truescan chat_history → {“foo.py”}get_glob_rules_for_paths([“foo.py”], exclude=always)[python.md]base prompt + “type-hinted code
Diagram

Agent

RulesManager

Rule Sources

📄 Root Files
CLAUDE.md, AGENTS.md, GEMINI.md

📁 .praisonai/rules/
Workspace Rules

🌐 ~/.praisonai/rules/
Global Rules

🔍 Auto-Discovery

📝 Parse Frontmatter

🎯 Filter by Activation

💉 Inject into System Prompt

🤖 LLM

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: manual rules 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=False to suppress discovery (and the line) for a sandboxed run.

Supported Instruction Files

PraisonAI automatically loads these files from your project root and git root:

FileDescriptionPriority
PRAISON.mdPraisonAI native instructionsHigh (500)
PRAISON.local.mdLocal overrides (gitignored)Higher (600)
CLAUDE.mdClaude Code memory fileHigh (500)
CLAUDE.local.mdLocal overrides (gitignored)Higher (600)
AGENTS.mdOpenAI Codex CLI instructionsHigh (500)
GEMINI.mdGemini CLI memory fileHigh (500)
.cursorrulesCursor IDE rules (legacy)High (500)
.windsurfrulesWindsurf IDE rules (legacy)High (500)
.claude/rules/*.mdClaude Code modular rulesMedium (50)
.windsurf/rules/*.mdWindsurf modular rulesMedium (50)
.cursor/rules/*.mdcCursor modular rulesMedium (50)
.praisonai/rules/*.mdWorkspace rulesMedium (0)
~/.praisonai/rules/*.mdGlobal modular rulesLow (-1000)
~/.praisonai/AGENTS.mdGlobal single-file instructionsLowest (loaded by load_context_files())

Quick Start

  1. 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.")
  2. With Configuration

    Use RulesConfig when 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 passWhat happens
Nothing (omit rules=)Auto-discover and apply if files exist (prints one Applied rules: … warning)
rules=NoneSame as omitting — auto-discover and apply
rules=TrueSame as above — auto-discover and apply
rules=RulesConfig(...)Full control (custom budget / files / workspace)
rules=FalseOff — no discovery, no injection, sandboxed run

CLI Usage

Project instruction files are auto-injected into praisonai run, chat, and code — no Python required.

bash
# 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:

Simple Rule (PRAISON.md)
# Project Guidelines

- Always use type hints for functions
- Follow PEP 8 style guide
- Write docstrings for public APIs
- Prefer async when possible
With Frontmatter
---
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
Glob-Based Rule
---
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
Manual Rule
---
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 authentication

Activation Modes

ModeDescriptionUse Case
alwaysAlways applied (default) — ships in the cached base promptGeneral guidelines
globInjected only for files the agent has actually touched this turn; matches paths that appear in the running conversationLanguage-specific rules
manualOnly via @mentionSecurity checklists, special procedures
ai_decisionAI decides when to applyContext-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.

python
from praisonaiagents import Agent

agent = Agent(
    name="Dev",
    instructions="Refactor the codebase",
)
agent.start("Open foo.py and add type hints")   # → python.md rule activates

The rule lives on disk as usual:

markdown
---
activation: glob
globs: ["**/*.py"]
---
Use type hints on every function and method.

Pick an activation mode based on when the rule should apply:

Diagram

Every run, always

Only for specific file types

Only when I explicitly mention it

Need a project rule?

When should it apply?

activation: always

activation: glob
+ globs: pattern

activation: manual

Path-Scoped Rules in Action

Author a glob rule and it fires the moment the agent touches a matching file during a run.

python
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.
Diagram
LLMRulesManagerChatMixinAgentUserLLMRulesManagerChatMixinAgentUseragent.start(“open foo.py …”)_build_system_prompt()base prompt (cached, always+manual)has_glob_rules()?Truescan chat_history → {“foo.py”}get_glob_rules_for_paths([“foo.py”], exclude=always)[python.md]base prompt + “type-hinted code

A rule with globs: ["**/*.py"] fires only when a filename token actually appears in the conversation:

User / tool text mentionsRule (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.

python
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.")
OptionTypeDefaultDescription
char_budgetOptional[int]NoneCharacter budget for injected rules context. None = RulesManager default.
filesOptional[List[str]]NoneExtra instruction file paths / globs registered with high priority beyond auto-discovery.
workspace_pathOptional[str]NoneWorkspace path override. Defaults to os.getcwd().

Choose an option

Diagram

Yes

No

Yes

No — need custom limits,
extra files, or a different
workspace

🎯 Do you need rules?

Reproducible run
or sandboxed?

rules=False
💤 Skip discovery

Default budget
enough?

rules=True or omit
✅ Zero config

rules=RulesConfig…
⚙️ Full control

@Import Syntax (like Claude Code)

Include other files in your rules using the @path/to/file syntax:

Basic Import
# CLAUDE.md
See @README for project overview
See @docs/architecture.md for system design

# Additional Instructions
- Follow the patterns in @src/examples/
Home Directory Import
# CLAUDE.md
# Import personal preferences from home directory
@~/.praisonai/my-preferences.md

# Project-specific rules
- Use TypeScript for all new code
Relative Import
# .praisonai/rules/python.md
# Import shared guidelines
@./shared/code-style.md

# Python-specific additions
- Use type hints
- Follow PEP 8

Git 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 backend

Rules 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.

Diagram

Yes: directory or file

No

Reached filesystem root

No

Yes

Success

Fail

📂 workspace_path

🔍 .git here?

✅ Use this directory as git root

⬆️ Walk up one level

🧭 GIT_DIR or
GIT_WORK_TREE set?

💤 No git root
Zero-cost result

⚙️ git rev-parse
–show-toplevel

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.

Diagram

Yes

No

📂 Current directory

🔍 Inside workspace
or git root?

⬆️ Walk up to the workspace root
Load rule folders at each level

💤 Skip the walk
Root instruction files still load

✅ Rules merged into context

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/✅ YesAlready loaded
At the workspace rootproject/— Nothing to walkNot needed
Monorepo rootA subpackage (repo/packages/api)❌ NoPoint workspace_path at the repo root
Repo root, running pytestA temp folder (tmp_path)❌ NoRulesConfig(files=[...])
python
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.md

Security & 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

python
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")     # glob

The two glob helpers power the per-turn path-scoped injection shown in Path-Scoped Rules in Action.

MethodSignatureReturns
has_glob_rules() -> boolTrue 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

python
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:

python
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.

python
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

python
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.

Diagram
🤖 Agent📚 RulesManager💻 praisonai run👤 User🤖 Agent📚 RulesManager💻 praisonai run👤 Userpraisonai run “Fix the auth bug”build_rules_context()Discover AGENTS.md, CLAUDE.mdrules_context (≤32 KB)rules_context + “\nResponse respecting project rules

CLI flags

FlagEffect
_(default)_Auto-inject all discovered instruction files
--no-rulesSkip auto-injection for this run
--include-rules <names>Force-include named rules (comma-separated)
--include-rules autoExplicitly enable auto (same as default)
--verbosePrint loaded files: Loaded project instructions: AGENTS.md, CLAUDE.md

Configuration

Global opt-out via ~/.praisonai/config.toml:

toml
[rules]
auto = false        # Disable everywhere
max_chars = 16000   # Or shrink the budget

See CLI Config for the full TOML reference.

Cross-Tool Compatibility

PraisonAI rules are compatible with other AI coding assistants:

ToolFileCompatibility
Claude CodeCLAUDE.md✅ Full
Codex CLIAGENTS.md✅ Full
Gemini CLIGEMINI.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.

Related