Skip to main content
Drop Markdown, YAML, or Python files into .praisonai/agents/, .praisonai/commands/, and .praisonai/tools/ to extend the CLI without writing packaging code. Existing .claude/agents/, .claude/commands/, .agents/agents/, and .agents/commands/ layouts from other agent tools are picked up too — no migration required. The user runs praisonai run --agent researcher; discovery loads custom agents, slash commands, and project-local tools from .praisonai/.

How It Works

Quick Start

Skip the boilerplate — praisonai init scaffolds a working .praisonai/ with a starter agent and command, then read on to customise.
1

Create an agent file

2

Run the agent

3

Create a command file

4

Run the command

How discovery works

Agents and commands are discovered from .praisonai/ and the conventional ecosystem roots .claude/ and .agents/. Tools stay .praisonai-only because loading them executes user code. Precedence (later wins on name collision):
  1. Nearer directory beats a more distant ancestor — a nested package’s definition overrides one inherited from a parent repo.
  2. Within the same directory level, .praisonai/ beats .claude/ and .agents/ — project-native definitions win over imported ones.
  3. User-global (~/.praisonai/) is overridden by any project-level match.
.claude/tools/*.py and .agents/tools/*.py are never auto-loaded. Loading a tool executes user code, so a drop-in checkout that ships a .claude/tools/ directory can’t run arbitrary Python. Auto-loaded tools remain .praisonai/tools/*.py only, still gated by PRAISONAI_ALLOW_LOCAL_TOOLS=true.

Reusing existing .claude/ or .agents/ layouts

Already have agents defined for another tool? PraisonAI picks them up as-is:
No migration, no copying. If you later add .praisonai/agents/reviewer.md, that project-native file wins.

Agent definitions

Files: .praisonai/agents/*.md|*.yaml, .claude/agents/*.md|*.yaml, .agents/agents/*.md|*.yaml

Tool composition on praisonai run --agent

When praisonai run --agent <name> runs, the agent’s tool list is built from these sources, in order, and dedup’d by callable identity: Example:
Before this was fixed (#3047), only the frontmatter list was honoured on the --agent path.

Scoping permissions

Three built-in agents (build, plan, review) are available without any file — see Agent Presets & Modes.
Add mode: to a definition for instant read-only or review scoping:
For finer control, use the permission: block:
See Agent Presets & Modes for the full modes reference, permission syntax, and precedence rules.

Supported mode: values

mode: subagent marks an agent as delegatable from other agents at run time (see Named Agent Delegation). It is a marker only — it does not apply any permission changes and is ignored when computing permissions. Every other mode: value still flows through the permission engine as before.

Command templates

Files: .praisonai/commands/*.md, .claude/commands/*.md, .agents/commands/*.md

Positional arguments

Reference $1, $2, …, $n to pull individual tokens out of the user’s argument string — $ARGUMENTS still holds the whole string.
Bare-minimum command file:
Invocation:
Positional tokens are escaped exactly like $ARGUMENTS — a user cannot smuggle $(...) shell substitution through $1..$n even when the command has allow_shell: true. The !`cmd` pattern still applies only to the template itself, never to injected user input.

Command frontmatter

Frontmatter fields tune how a command runs. Every field is optional — a command with only a description still works. tools wins when present; otherwise allowed-tools / allowed_tools supply the list.
The single-line form of tools is equivalent to the YAML list above:

Opt-in live shell substitution

!`cmd` is disabled by default. Enable with any one of:
  1. PRAISONAI_ALLOW_SHELL=true environment variable
  2. commands.allow_shell: true in .praisonai/config.yaml
  3. Per-command frontmatter allow_shell: true
Safety bounds: 30s timeout, 100KB max stdout, runs in the template’s working directory. Non-zero exit raises ShellSubstitutionError. !`cmd` inside $ARGUMENTS or @file contents is never executed — only markers in the original template run.
The same gate now controls the interactive !cmd shell escape in praisonai code. Enabling PRAISONAI_ALLOW_SHELL=true (or commands.allow_shell: true) turns on both !`cmd` template substitution and the interactive ! / !! prefixes. See Shell Escape for the interactive surface.

Project-local tools

Drop any .py file into .praisonai/tools/ and every praisonai run — including run --agent <name> — in that project auto-loads its tools — no --tools flag, no packaging, no imports in your agent code. Only .praisonai/tools/*.py is auto-loaded. .claude/tools/ and .agents/tools/ are intentionally ignored, because loading a tool executes Python — this keeps a drop-in ecosystem checkout from running arbitrary code.
That is the whole feature. The rest of this section is detail.
Loading a tool file executes its Python. Auto-load is gated by PRAISONAI_ALLOW_LOCAL_TOOLS=true — the same opt-in that guards every local tool in the CLI. You can also pass --allow-local-tools on the individual praisonai run invocation as a per-invocation equivalent.

How discovery works

What gets loaded

Precedence & opt-out

A richer example — private helpers stay private

Scaffold a single agent with praisonai agent create

Turn a one-line description into a permission-scoped agent file in one command — no editor round-trip.
The permission preset (read-only / review / full) maps to the same mode: shorthand documented in Scoping permissions above — no parallel grammar. When a provider credential is available, the system prompt is drafted via the LLM; otherwise the CLI writes an editable stub so file creation never blocks. Use this when you want one focused agent; use praisonai init when you also want starter commands and a tools scaffold next to it. See praisonai agent create for the full flag reference.

Scaffold with praisonai init

The scaffolded example.py is a commented @tool stub — uncomment or replace it in place. For a single-agent scaffold, see praisonai agent create.

Making an agent delegatable

Mark an agent with mode: subagent so a running primary agent can hand it sub-tasks by name.
Now a running primary agent (praisonai run --agent lead) can call spawn_subagent(agent_name="researcher", …) and this agent runs the sub-task under its own model/tools/permissions. See Named Agent Delegation.

Agent vs command vs skill vs rule

Slash commands

Custom commands auto-register in interactive mode as CommandKind.CUSTOM. Disable with SlashCommandHandler(discover_custom=False). Custom commands appear automatically in Telegram / Discord / autocomplete when your bot restarts, filtered by the same CommandAccessPolicy that gates execution. See Native / Autocomplete.
The same .praisonai/commands/{name}.md file now also runs in bot chats (Telegram / Slack / Discord), not just the code REPL/TUI. Enable it per channel with a commands block (opt-in via bots.commands) — one file, two surfaces, no duplication. Shell (!`cmd`) substitution stays off in bots unless both the deployment and the command’s frontmatter opt in. See File-based & Entry-point Commands in Bot Chats.

Inside praisonai code too

The same .praisonai/commands/*.md files work as /name inside praisonai code, the REPL, and the async TUI. A unified CommandRegistry aggregates built-ins, your custom commands, skills, MCP prompts, and pip-installed praisonai.commands packs into one namespace:
This runs the exact interpolated template that praisonai run --command mydeploy staging would — byte-for-byte parity between interactive and CLI. See Slash Commands → Unified Command Registry.

Python API

discover_project_tools() returns a list of tool callables (empty unless PRAISONAI_ALLOW_LOCAL_TOOLS=true). For metadata, CustomDefinitionsDiscovery().list_tools() returns CustomTool(name, path, callable, source) records where name is the namespaced <module>.<func>.

Best Practices

Commit .praisonai/agents/, .praisonai/commands/, and .praisonai/tools/ to git. Every teammate and CI run picks up the same tool set on the next praisonai run — no pip install and no --tools flag.
.praisonai/tools/_helpers.py is skipped entirely. Inside a loaded file, functions without @tool are only fallback-loaded when the file has no @tool at all — so decorating your public entry points keeps private helpers off the LLM’s tool list.
Use ~/.praisonai/ for personal shortcuts that should not override team agents.
Keep team-owned, project-native definitions in .praisonai/ — they win on name collision and are the single source PraisonAI writes to (e.g. praisonai init, praisonai agent create). Point PraisonAI at your existing .claude/agents/ or .agents/commands/ when you want to reuse assets that already ship in your repo for another agent tool, without duplicating them.
!`cmd` requires an explicit opt-in gate (allow_shell: true, config, or PRAISONAI_ALLOW_SHELL). $(...) is always escaped — use !`cmd` only when you need live output like git diff.
Positional args make the interpolation explicit and let you write help hints (argument-hint: <from> <to>) that survive editor tooltips. Use $ARGUMENTS only when the command should receive the raw request verbatim.
The hint shows up when the command is listed in praisonai command list / autocompleted in the interactive REPL and in bot slash-command menus. It’s the fastest way to teach yourself and your team what an argument looks like without opening the file.
When set, the command runs with only those tools available even if the caller’s default agent has more. Pair with a read-only model: for review/inspection commands.
Run praisonai init to scaffold .praisonai/agents/, .praisonai/commands/, and .praisonai/tools/ before hand-writing files.

Run CLI

—agent and —command flags

Agent CLI

List and inspect custom agents

Command CLI

List and preview commands

Slash Commands

Interactive custom commands

Init CLI

Scaffold .praisonai/ in one command

Tools

The @tool decorator and building custom tools

Project-Local Tools

Auto-discovered .praisonai/tools/*.py on run and run —agent

Agent Presets & Modes

Built-in presets and per-agent permission scoping

Named Agent Delegation

Delegate named sub-tasks to your agents with mode: subagent