Context Files

Inject markdown context files into your agent's instructions automatically

Context files automatically inject markdown content into your agent’s instructions — drop an AGENTS.md in your project root and every run sees it, no config needed.

python
from praisonaiagents import Agent

agent = Agent(name="project-agent", instructions="Follow project AGENTS.md guidelines.")
agent.start("Implement the feature using our team conventions.")

The user adds AGENTS.md or CLAUDE.md; discovery merges layered markdown into the agent instructions.

Diagram

Context Injection Flow

📄 AGENTS.md
CLAUDE.md

🔍 Walk-up Discovery

🔗 Layer & Merge

🤖 Enhanced Agent

Quick Start

  1. Drop an AGENTS.md in your project root
    markdown
    # AGENTS.md
    ## Company Guidelines
    - Always be professional and helpful
    - Focus on customer satisfaction
    - Use clear, concise language
    
    ## Product Information
    Our main products include:
    - AI Assistant Platform
    - Workflow Automation Tools
  2. Run from any subdirectory — instructions load automatically
    bash
    # From the project root
    praisonai run "Help a customer"
    
    # Or from a nested package — the root AGENTS.md is still found
    cd packages/frontend
    praisonai run "Build a login form"

    Or use the Python API:

    python
    from praisonaiagents import Agent
    from praisonai.integration import configure_host
    
    agent = Agent(
        name="Customer Support",
        instructions="Help customers with their questions"
    )
    
    # No context_paths needed — auto-discovery picks up AGENTS.md
    configure_host(agents=[agent], style="dashboard")

How It Works

load_context_files() walks up from your current directory to the git root, collecting candidates at each level and concatenating them with the nearest file winning (appended last):

Diagram

Discovery Layers (low → high precedence)

🌐 ~/.praisonai/AGENTS.md
(user-global)

📁 git root /
AGENTS.md · CLAUDE.md · agents.md · .agents/AGENTS.md

📁 intermediate dirs
(each level)

📁 cwd /
AGENTS.md · CLAUDE.md · agents.md · .agents/AGENTS.md

🔗 Concatenate
global → root → cwd

🤖 Agent

The merged content is prepended to the agent’s instructions:

{original_instructions}

Context:
{layered_context_from_all_levels}

On-demand Subtree Attachment

On-demand attachment picks up per-subtree AGENTS.md / CLAUDE.md files the up-front walk-up missed, only when a file inside that subtree is actually touched.

Diagram

During the session

Yes

No

✍️ Touch packages/foo/main.py

Walk up

Dedup vs
already_loaded
+ seen files

Budget
remaining?

📎 Attach nearest
AGENTS.md / CLAUDE.md

⏭ Skip

Session start

📁 cwd walk-up

📄 already_loaded

  1. Zero-config in a monorepo

    When your host wires the attacher into the CLI auto-loader, on-demand attachment runs for you — drop a packages/foo/AGENTS.md and it appears the first time the agent edits a file inside packages/foo/. If your host has not wired it in yet, reach for the stateless helper or the session class below.

  2. Attach for a single path (stateless)
    python
    from praisonaiagents import Agent
    from praisonai.integration.context_files import (
        load_context_files,
        load_context_files_for_path,
    )
    
    up_front = load_context_files()  # existing walk-up from cwd to root
    
    agent = Agent(
        name="Monorepo Refactorer",
        instructions=up_front + "\n\n" + load_context_files_for_path(
            "packages/foo/main.py",
            already_loaded=up_front,
        ),
    )
    agent.start("Refactor packages/foo/main.py to use the new logger.")
  3. Attach across many touches (session-scoped)
    python
    from praisonaiagents import Agent
    from praisonai.integration.context_files import (
        load_context_files,
        PathContextAttacher,
    )
    
    up_front = load_context_files()
    attacher = PathContextAttacher(already_loaded=up_front, max_chars=8000)
    
    for path in ["packages/foo/main.py", "packages/foo/util.py", "packages/bar/api.py"]:
        extra = attacher.attach_for_path(path)
        if extra:
            # feed `extra` into the agent (e.g. via instructions or a system message)
            ...
  4. Wire it into an agent (auto-attach as tools run)

    Register the hook on an agent and the nearest AGENTS.md attaches automatically the first time the agent reads or edits a file in a new subtree — no manual attach_for_path() calls.

    python
    from praisonaiagents import Agent
    from praisonaiagents.hooks import HookEvent, HookRegistry
    from praisonai.integration.context_files import (
        load_context_files,
        build_subtree_context_hook,
        file_tool_matcher,
    )
    
    up_front = load_context_files()
    
    hooks = HookRegistry()
    hooks.register_function(
        HookEvent.AFTER_TOOL,
        build_subtree_context_hook(already_loaded=up_front),
        matcher=file_tool_matcher(),
    )
    
    agent = Agent(
        name="Monorepo Refactorer",
        instructions=up_front,
        hooks=hooks,
    )
    agent.start("Refactor packages/foo/main.py to use the new logger.")

    The hook fires after each file tool, reads the touched path from filepath, file_path, path, filename, or target_file, and returns the nearest subtree rules as additional_context the first time that subtree is touched. Because it reads any of those keys, the hook fires correctly for both the wrapper file tools and the ACP-shaped tools without configuration.

    Diagram
    sequenceDiagram
        participant User
        participant Agent
        participant Tool
        participant Hook as AFTER_TOOL hook<br/>(build_subtree_context_hook)
        participant Attacher as PathContextAttacher
    
        User->>Agent: start("edit packages/foo/main.py")
        Agent->>Tool: edit_file(file_path="packages/foo/main.py")
        Tool-->>Agent: result
        Agent->>Hook: AFTER_TOOL(tool_input)
        Hook->>Attacher: attach_for_path("packages/foo/main.py")
        Attacher-->>Hook: packages/foo/AGENTS.md
        Hook-->>Agent: additional_context
        Agent-->>User: reply (now aware of foo's rules)
    
        classDef user fill:#6366F1,stroke:#7C90A0,color:#fff
        classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
        classDef tool fill:#189AB4,stroke:#7C90A0,color:#fff
        classDef hook fill:#F59E0B,stroke:#7C90A0,color:#fff
        classDef attach fill:#10B981,stroke:#7C90A0,color:#fff
    
        class User user
        class Agent agent
        class Tool tool
        class Hook hook
        class Attacher attach

Behaviour guarantees

GuaranteePlain language
Subtree file attached only after a file under it is touchedNothing loads until the agent opens something in that folder.
Files already in the up-front load are not re-attachedNo duplicate rules from what loaded at startup.
A file emitted for one directory is not re-emitted for anotherThe same rules file never shows up twice.
Repeated touches of the same directory hit the cacheRe-opening a folder costs nothing — no disk re-read.
A sibling directory with no AGENTS.md returns an empty stringEmpty folders add nothing.
Total emitted text is bounded by max_charsThe attached context can never grow past the budget.
load_context_files_for_path(...) is a stateless one-shotUse it when you touch a single path.
Returns "" when nothing new is foundSilent when there is nothing to add.

When do I need on-demand attachment?

Diagram

Yes

No — Python API

Yes

No

One known path

Many paths, I call attach_for_path myself

An agent + tools — auto-attach as tools run

On a CLI
(chat / run / code / tui)?

✅ Nothing to wire
the CLI registers the hook for you
unless –no-rules / PRAISON_NO_RULES

Are all instruction files inside cwd or above it?

✅ Up-front load_context_files is enough
(no attacher)

How does the session touch files?

🎯 load_context_files_for_path(path)
(stateless helper)

🔁 PathContextAttacher(…)
(session-scoped cache + dedup)

🪝 build_subtree_context_hook()
+ file_tool_matcher()


CLI Auto-loading

As of PraisonAI PR #2358, praisonai chat, praisonai run, praisonai code, and praisonai tui automatically discover and inject AGENTS.md-style project context — no flag required. Each command wires two things: the up-front load_context_files() merge that prepends the walk-up context to the system prompt, and the on-demand build_subtree_context_hook attach that fires the first time the agent touches a file under a nested AGENTS.md. Parity for run (including run --agent … and run --profile …) landed in PR #3552 — before that, only chat registered the subtree hook.

Diagram
sequenceDiagram
    participant User
    participant CLI as praisonai CLI
    participant Loader as load_context_files
    participant Hook as build_subtree_context_hook
    participant Agent as Agent backstory

    User->>CLI: praisonai chat / run / code / tui
    CLI->>Loader: walk_up=True, cwd=workspace
    Loader-->>CLI: merged context (≤8000 chars)
    CLI->>Agent: prepend "# Project Context\n{context}"
    CLI->>Hook: register AFTER_TOOL hook (unless --no-rules)
    Note over Hook: fires on first touch under a nested AGENTS.md
    Agent-->>User: response with project awareness

    classDef user fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef cli fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef loader fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef hook fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef agent fill:#10B981,stroke:#7C90A0,color:#fff

    class User user
    class CLI cli
    class Loader loader
    class Hook hook
    class Agent agent

Zero-config in a monorepo: from the repo root, praisonai run "refactor packages/foo/bar.py" picks up packages/foo/AGENTS.md the moment it opens a file there — the same behaviour chat has always had.

Beyond auto-discovered AGENTS.md / CLAUDE.md, teams can declare extra sources — files, globs, or URLs — via the top-level instructions: config key or the praisonai run --instructions flag. Declared sources load up front alongside the walk-up and dedup against the same files. See Instruction Sources.

  • As of PraisonAI PR #4338, praisonai run (and the resident TUI’s chat / code in praisonai-code) register the subtree hook standalone — no praisonai_bot / praisonai needed. The CLI falls back to the resident praisonai_code.integration.context_files copy when the optional packages are absent. Previously the hook was a silent no-op without those packages.

Opt-out

  1. CLI flag

    Pass --no-context to any of the four commands to skip all project-context loading for that invocation:

    bash
    praisonai chat --no-context
    praisonai run --no-context "Quick one-off task"
    praisonai code --no-context
  2. Environment variable

    Set PRAISON_NO_CONTEXT=true in your shell or CI environment to disable auto-loading globally:

    bash
    export PRAISON_NO_CONTEXT=true
    praisonai run "task"   # context skipped
  3. InteractiveConfig field

    When using the interactive core programmatically:

    python
    from praisonai.cli.interactive.config import InteractiveConfig
    
    config = InteractiveConfig(no_context=True)
  4. configure_host() param

    In host integrations, pass no_context=True directly or via agent_kwargs for backward compatibility:

    python
    configure_host(no_context=True)
    # or
    configure_host(agent_kwargs={"no_context": True})

Opt-out matrix

Two independent switches gate the two mechanisms: --no-context / PRAISON_NO_CONTEXT skips the up-front load_context_files() merge, while --no-rules / PRAISON_NO_RULES skips the on-demand build_subtree_context_hook attach. Both switches work on a standalone pip install praisonai-code — the subtree hook is now registered standalone via the resident praisonai_code.integration.context_files copy (PR #4338), so these opt-outs apply even without praisonai_bot / praisonai.

SurfaceOpt-out for up-front load (load_context_files)Opt-out for subtree hook (build_subtree_context_hook)
CLI flag--no-context--no-rules
Env varPRAISON_NO_CONTEXT=truePRAISON_NO_RULES=true (accepts 1 / true / yes, case-insensitive)
InteractiveConfig fieldno_context=Trueno_rules=True
configure_host() paramno_context=True (or via agent_kwargs)no_rules=True (or via agent_kwargs)

Token budget and truncation

The default budget is 8000 characters. When discovered context exceeds the budget, it is truncated and \n... [project context truncated] is appended. Override via:

  • InteractiveConfig(context_token_budget=16000) for interactive sessions
  • configure_host(context_token_budget=16000) for host integrations

PRAISON_CONTEXT_BUDGET — integer character budget for the subtree hook (build_subtree_context_hook(max_chars=…)). Applies to both interactive (chat) and non-interactive (run) CLI. PRAISON_CONTEXT_BUDGET=0 — or a missing / non-integer value — disables the cap on the CLI subtree hook. Set a positive integer to bound the total subtree text emitted across the session.


Configuration Options

Default File Discovery

When no context_paths is given, these filenames are checked at every directory level:

PathDescription
CLAUDE.local.mdLocal overrides (highest precedence, git-ignored)
AGENTS.mdPrimary agent context file (Codex CLI compatible)
agents.mdAlternative casing
.agents/AGENTS.mdHidden directory structure
CLAUDE.mdClaude Code memory file
GEMINI.mdGemini CLI context file

Discovery Scope

LayerLocationPrecedence
User-global~/.praisonai/AGENTS.mdLowest (loaded first)
Git/project rootRoot-level candidatesLow
Intermediate dirsEach dir between root and cwdMedium
cwdCurrent working directory candidatesHighest (loaded last)
Touched-file subtree (on-demand)Nearest AGENTS.md/CLAUDE.md above the touched fileAppended after all up-front layers

walk_up Parameter

ValueBehavior
True _(default)_Walk up to git/project root, include ~/.praisonai/AGENTS.md
FalseRead only from cwd, skip global file

The walk_up parameter is ignored when you pass an explicit paths list.

Explicit Paths (Backward Compatible)

Pass context_paths to skip auto-discovery entirely — files are read from cwd only:

python
configure_host(
    context_paths=[
        "docs/guidelines.md",
        "config/style-guide.md", 
        "knowledge/product-info.md"
    ]
)

Choosing a Mode

Diagram

Yes

No

Yes

No

Which mode do I need?

Explicit file list?

Pass context_paths=[…]
cwd-only, no walk-up

Restrict to cwd only?

walk_up=False
cwd-only, no global

Default auto-discovery
walk_up=True
global → root → cwd

Context Loading Behavior

python
from praisonai.integration.context_files import load_context_files

# Auto-discovery (default): walks up, includes global file
context = load_context_files()

# Restrict to cwd only (no walk-up, no global)
context = load_context_files(walk_up=False)

# Explicit paths: cwd-only, no walk-up (backward compatible)
context = load_context_files(paths=["AGENTS.md", "STYLE.md"])

De-duplication is automatic: if cwd equals the git root, the same file is read only once (matched by inode, safe on case-insensitive volumes).

New public helpers

Two helpers wire the attacher into an agent’s hooks so subtree rules attach automatically as file tools run.

HelperSignaturePurpose
build_subtree_context_hook(already_loaded=None, *, max_chars=8000) -> CallableAFTER_TOOL hook that auto-attaches subtree instruction files as file tools run. Session-scoped.
file_tool_matcher() -> strRegex that limits the hook to file-touching tool names. Matches wrapper defaults (read_file, write_file, edit_file, list_files), ACP tools (acp_create_file, acp_edit_file, acp_delete_file), and interactive-core / Claude-Code-style names (read, edit, write, apply_patch, str_replace, multi_edit, search_replace). Pass as matcher= when registering.

Options for build_subtree_context_hook:

OptionTypeDefaultDescription
already_loadedOptional[str]NoneUp-front instruction text (used only to seed dedup so files already loaded at session start are not re-attached).
max_charsint8000Character budget across the whole session; 0 disables it.

File Structure Examples

Basic AGENTS.md

markdown
# Agent Context

## Role Definition
You are a customer support specialist with deep knowledge of our products.

## Communication Style
- Be friendly and professional
- Use active voice
- Keep responses under 150 words unless detailed explanation needed

## Knowledge Areas
- Product features and pricing
- Technical troubleshooting
- Account management procedures

Monorepo Layering

Place org-wide guidelines at the repo root and package-specific overrides closer to the working directory:

monorepo/
├── AGENTS.md              # Org-wide guidelines (lowest project precedence)
├── packages/
│   ├── frontend/
│   │   └── AGENTS.md      # Frontend overrides (higher precedence)
│   └── backend/
│       └── AGENTS.md      # Backend overrides (higher precedence)

Running from inside packages/frontend/ automatically merges both files — root guidelines first, frontend overrides last (winning):

bash
cd packages/frontend
praisonai run "Add a login form"
# Loads: monorepo/AGENTS.md → packages/frontend/AGENTS.md

No context_paths configuration needed.

User-Global Instructions

~/.praisonai/AGENTS.md applies across all your projects as the lowest-priority layer:

markdown
# ~/.praisonai/AGENTS.md
## Personal Preferences
- Always respond in British English
- Prefer async/await over callbacks
- Include type hints in all Python code

Every project you work in inherits these preferences unless overridden by a project-level file.


Common Patterns

Environment-Specific Context

python
import os
from praisonaiagents import Agent
from praisonai.integration import configure_host

agent = Agent(name="Assistant", instructions="You are helpful.")

env = os.getenv("ENVIRONMENT", "development")
context_files = ["AGENTS.md"]

if env == "production":
    context_files.append("production-guidelines.md")
else:
    context_files.append("dev-guidelines.md")

configure_host(agents=[agent], context_paths=context_files)

Monorepo with sibling subtrees

Start a session at the repo root, then let the agent open files in different packages — each package’s own AGENTS.md attaches the moment the agent touches a file there.

monorepo/
├── AGENTS.md                    # Org-wide guidelines
└── packages/
    ├── foo/
    │   ├── AGENTS.md            # Foo conventions
    │   └── main.py
    └── bar/
        ├── AGENTS.md            # Bar conventions
        └── api.py
Diagram

✍️ packages/foo/main.py

📄 packages/foo/AGENTS.md

📁 monorepo/AGENTS.md
(git root)

python
from praisonai.integration.context_files import (
    load_context_files,
    PathContextAttacher,
)

up_front = load_context_files()  # session starts at the repo root
attacher = PathContextAttacher(already_loaded=up_front)

attacher.attach_for_path("packages/foo/main.py")  # attaches packages/foo/AGENTS.md
attacher.attach_for_path("packages/bar/api.py")   # attaches packages/bar/AGENTS.md
attacher.attach_for_path("packages/foo/util.py")  # cached — no disk re-read, nothing new

The agent picks up each package’s conventions the moment it opens a file there.

Conditional Context Loading

python
def get_context_files(agent_type):
    base_files = ["AGENTS.md"]

    if agent_type == "support":
        base_files.append("support-protocols.md")
    elif agent_type == "sales":
        base_files.append("sales-playbook.md")

    return base_files

configure_host(context_paths=get_context_files("support"))

CI / Snapshot Testing

When you don’t want ancestor files to influence a test run, disable walk-up:

python
from praisonai.integration.context_files import load_context_files

# Only reads files in the current test directory
context = load_context_files(walk_up=False)

Best Practices

File Organization

Structure your context files for maintainability:

context/
├── base/
│   ├── guidelines.md     # Universal principles
│   └── style.md         # Communication style
├── roles/
│   ├── support.md       # Role-specific context
│   └── sales.md         # Role-specific context  
└── knowledge/
    ├── products.md      # Product information
    └── policies.md      # Company policies
Content Structure

Keep context files focused and well-organized:

markdown
# Context File Template

## Core Role
Brief description of the agent's primary function

## Guidelines  
- Key behavioral guidelines
- Communication principles
- Quality standards

## Knowledge Areas
Relevant domain knowledge the agent should reference

## Examples
Sample interactions or responses when helpful
When to use walk_up=False

Disable walk-up for isolated CI jobs, snapshot tests, or any run where you want to guarantee only the files in the current directory are loaded — no ancestor directories, no global user file.

python
context = load_context_files(walk_up=False)
Content Management

Version control your context files:

bash
# Track context changes
git add AGENTS.md STYLE.md
git commit -m "Update agent communication guidelines"

# Review context impact
git diff HEAD~1 context/
Use PathContextAttacher in long-running sessions

When the agent will touch many files across a monorepo, create one PathContextAttacher per session and reuse it. The per-directory cache means repeated touches of the same subtree cost nothing, and the char budget stops the attached context from growing unbounded across the run. Pass already_loaded=up_front (the string returned by load_context_files()) so files discovered up front are not re-attached.

python
from praisonai.integration.context_files import (
    load_context_files,
    PathContextAttacher,
)

up_front = load_context_files()
attacher = PathContextAttacher(already_loaded=up_front, max_chars=8000)
Prefer the hook for interactive/agentic sessions

For interactive sessions where the agent decides which files to touch, register build_subtree_context_hook() on HookEvent.AFTER_TOOL instead of calling attach_for_path() manually. The hook fires exactly when a file is touched, so the agent gets the nearest subtree rules just-in-time without you having to predict its path list up front. Pair with matcher=file_tool_matcher() so the hook skips non-file tools cheaply.

python
from praisonaiagents.hooks import HookEvent, HookRegistry
from praisonai.integration.context_files import (
    load_context_files,
    build_subtree_context_hook,
    file_tool_matcher,
)

up_front = load_context_files()
hooks = HookRegistry()
hooks.register_function(
    HookEvent.AFTER_TOOL,
    build_subtree_context_hook(already_loaded=up_front),
    matcher=file_tool_matcher(),
)
Testing Context Changes

Validate context injection in development:

python
from praisonai.integration.context_files import load_context_files

context = load_context_files()
print("Loaded context length:", len(context))
print("Preview:", context[:200])

Related