Run
Run agents from files or prompts
The run command executes agents from YAML configuration files or direct prompts.
The same modern engine also handles bare prompts with run-supported flags: praisonai "fix bug" --model gpt-4o is equivalent to praisonai run "fix bug" --model gpt-4o. See CLI Dispatcher for how routing is decided.
Usage
praisonai run [OPTIONS] [TARGET]Arguments
| Argument | Description |
|---|---|
TARGET | Agent file (YAML) or direct prompt text |
TARGET may be a YAML/agent file path or a direct prompt. When you type the verb explicitly (praisonai run TARGET), the router hands TARGET to the modern agent runner whenever it ends in .yaml / .yml (even if the file does not exist yet — the runner is what emits the clear file-not-found error), or it is an existing file on disk. Only a TARGET that is neither of those and is not a reserved async-jobs verb (submit / status / result / cancel / list / stream) falls through to the jobs argparse parser and errors out. For free-text prompts, prefer the bare-prompt shape (praisonai "TARGET"), which reaches the modern engine unconditionally — see CLI Dispatcher.
Bare Prompts and YAML
You can drop the run keyword entirely for one-shot prompts and YAML workflows:
praisonai "Find the weather in London" # equivalent to: praisonai run "Find the weather in London"
praisonai build a weather agent # unquoted multi-token prompt also works
praisonai "fix the auth bug" --model gpt-4o # run-supported flag → still modern engine
praisonai agents.yaml # equivalent to: praisonai run agents.yaml
praisonai agents.yaml --continue --output json # run flags apply to YAML too# Typo? Wrong directory? You still get a clear error from the runner.
praisonai run typo.yaml
# Error: [Errno 2] No such file or directory: 'typo.yaml'Both shapes inherit everything run gives you — --output modes, session continuity, credential gate, and permissions.
YAML workflows through the modern run engine
praisonai agents.yaml now flows through the same modern run envelope as praisonai run agents.yaml — the router forwards a .yaml/.yml target to the Typer run engine automatically.
That unlocks the full run surface for plain YAML invocations:
| Capability | Flags | |||||
|---|---|---|---|---|---|---|
| Session continuity | --continue, --session <id>, --fork | |||||
| Output modes | `–output silent\ | actions\ | verbose\ | json\ | stream\ | stream-json` |
| First-run credential gate | Ollama detection + setup wizard rescue | |||||
| Permissions & approval | --allow, --deny, --approval, --permission-default |
# YAML with structured output + a named session
praisonai agents.yaml --output json --session mysess
# Resume the last run of this workflow
praisonai agents.yaml --continue
# Branch off an existing session without mutating it
praisonai agents.yaml --session mysess --fork
# Permission-gated YAML workflow
praisonai agents.yaml --plan
praisonai agents.yaml --allow "bash:git *" --approval console--framework crewai / --framework autogen is a run option, so multi-framework YAML is carried into the modern envelope too.
Options
| Option | Short | Description | Default |
|---|---|---|---|
--output | -o | Output mode: text, plain, verbose, silent, actions, json, stream, stream-json | text |
--model | -m | LLM model to use | gpt-4o-mini |
--framework | -f | Framework: praisonai, crewai, autogen | praisonai |
--interactive | -i | Enable interactive mode | false |
--verbose | -v | Verbose output | false |
--stream | Stream output | true | |
--no-stream | Disable streaming | ||
--trace | Enable tracing | false | |
--memory | Enable memory | false | |
--tools | -t | Comma-separated tool names (e.g. web_search,github) or a tools.py file path (resolved via ToolResolver) | |
--toolset | Comma-separated named toolset groups to load | ||
--allow-local-tools | Load project-local .praisonai/tools/*.py for this run only (equivalent to PRAISONAI_ALLOW_LOCAL_TOOLS=true, but strictly per-invocation) | false | |
--pure / --no-plugins | Skip discovery/loading of external plugins for this run only (equivalent to PRAISONAI_NO_PLUGINS=1); persisted enable/disable state is unchanged. | false | |
--max-tokens | Maximum output tokens | 16000 | |
--continue | -c | Continue the most recent session for this project | false |
--session | -s | Resume a specific session ID | |
--fork | Fork from the specified session (requires --session) | false | |
--pick / --interactive-session | Interactively pick a recent session to resume before running (TTY only; ignored with --session / --continue or --json). Aborts with exit 1 if the user cancels or no sessions exist. See Pick a session to resume. | false | |
--no-save | Don’t auto-save the session after execution | false | |
--no-rules | Disable auto-injection of project instruction files (AGENTS.md, CLAUDE.md, etc.) — includes both the up-front project-root walk-up and the on-demand subtree attach fired as the agent touches files under a nested AGENTS.md. Also suppresses config-declared and --instructions sources. | false | |
--instructions | Extra instruction/context source (file path, glob, or http(s):// URL) to load alongside AGENTS.md / CLAUDE.md. Repeatable; merges on top of config-declared instructions. See Instruction Sources. | ||
--append-system-prompt | Append text (literal or @file) to the system prompt for this invocation only. Env fallback: PRAISONAI_APPEND_SYSTEM_PROMPT. Never persisted. See Append System Prompt. | None | |
--image <path_or_url> | Attach an image (local path or http(s):// URL) to a one-shot run so the agent can see it. Repeatable for multiple images. Supported formats: .png .jpg .jpeg .gif .webp .bmp .svg .tiff. Direct-prompt runs only. See Attach an image. | None | |
--no-context | Disable AGENTS.md/CLAUDE.md auto-loading into system prompt | false | |
--agent | -a | Use a named custom agent from .praisonai/agents/ | |
--subagents | Comma-separated named agents (.praisonai/agents/*.md) the running agent may delegate to. Omit to expose agents marked mode: subagent. | ||
--thinking | Reasoning effort for this invocation: off, minimal, low, medium, high | ||
--command | Execute a named custom command; TARGET becomes $ARGUMENTS | ||
--allow | Allow a permission pattern (e.g. 'bash:git *'); repeatable | ||
--deny | Deny a permission pattern; repeatable | ||
--permissions | Path to a YAML permission rules file | ||
--permission-default | Default action for unmatched patterns: allow, deny, or ask | ||
--approval | Approval backend mode: console, plan, accept-edits, bypass | ||
--plan | Read-only planning mode — the agent may explore/read/search but every mutating tool (write/edit/delete/shell/exec) is denied. Discoverable alias for --approval plan. Cannot be combined with --approval, --allow, --deny, or --permission-default (exits 1). | false | |
--restore | Restore workspace to a checkpoint (id, last, or latest) and exit | ||
--revert <ref> | Revert files + conversation together to a prior turn (last or N), showing the diff first, and exit | — | |
--no-checkpoint | Disable automatic pre-run checkpoint for this invocation | false | |
--attach | Event-stream label used only to fan out live events to praisonai attach <id> clients. It does not by itself select or persist a conversation. Paired with --no-save, the run streams events under <id> but stays on the anonymous, non-persisted path. Observers can also steer or interrupt the live turn via praisonai attach -m "..." <id> and praisonai attach --interrupt <id>. See Attach. | ||
--worktree | Run on an isolated git worktree/branch (branch-per-task); no-op outside a git repo | false | |
--keep | With --worktree, keep the worktree/branch after the run for review instead of tearing it down | false | |
--append-system-prompt | Append text (or @file) to the system prompt for this invocation only. Env fallback: PRAISONAI_APPEND_SYSTEM_PROMPT. Runs in-process (bypasses the warm runtime). See Append System Prompt. |
Append to the system prompt
--append-system-prompt adds a one-off suffix to the assembled system prompt for a single run. It is never persisted and disappears when the process exits.
# Literal text
praisonai run "Refactor this module" --append-system-prompt "Always answer in French"
# Read the suffix from a file (@file form)
praisonai run "Refactor this module" --append-system-prompt @policy.mdOmit the flag and set the environment variable instead — handy for CI:
export PRAISONAI_APPEND_SYSTEM_PROMPT="Reply in strict JSON, no prose."
praisonai run "Summarise the changelog"See Append System Prompt for the full behaviour.
Attach an image (--image)
--image attaches a screenshot, mockup, or diagram to a single praisonai run so a vision-capable agent can see it — no code, no YAML.
- Attach one image
bash praisonai run "Describe this screenshot" --image bug.png - Attach multiple images
Repeat the flag — never comma-separate a single value.
bash praisonai run "Which of these two designs has better contrast?" \ --image mock-a.png --image mock-b.png - Attach a remote URL
Any
http://orhttps://URL is accepted verbatim.bash praisonai run "Summarize this architecture diagram" \ --image https://raw.githubusercontent.com/example/repo/main/arch.png
How it works: the run is routed through the vision path and picks a vision-capable model — defaulting to gpt-4o. Override it with --model, but the model you pick must support vision.
praisonai run "Extract every visible number" --image invoice.jpg --model gpt-4oSupported formats: .png .jpg .jpeg .gif .webp .bmp .svg .tiff. An unsupported extension or a missing local file fails before any model call.
Attach images from the Python SDK or a YAML workflow — the full multimodal story.
Delegating to Named Agents
Let a running agent delegate sub-tasks to your own named agents in .praisonai/agents/*.md. Pass --subagents to opt agents in for a single run, or mark agents with mode: subagent to expose them on every run.
praisonai run --agent lead --subagents researcher,reviewer "Draft and review a brief on X"See Named Agent Delegation for the full guide.
Isolated Runs (Git Worktree)
Add --worktree and each run gets its own git branch and checkout — concurrent runs never clobber each other’s edits.
sequenceDiagram
participant User
participant Run as praisonai run --worktree
participant Worktree as Isolated Worktree
participant Agent
User->>Run: praisonai run --worktree "prompt"
Run->>Worktree: create fresh branch + checkout
Worktree-->>Agent: isolated working directory
Agent->>Worktree: edit files
Worktree->>Run: commit changes to branch on exit
Run-->>User: "Review/merge with: git merge <branch>"
classDef user fill:#8B0000,stroke:#7C90A0,color:#fff
classDef tree fill:#189AB4,stroke:#7C90A0,color:#fff
classDef result fill:#10B981,stroke:#7C90A0,color:#fff
class User,Agent user
class Run,Worktree tree# Prompt run on a fresh branch — output committed to the branch on exit
praisonai run --worktree "Refactor the auth module and add tests"
# YAML file run — same isolation, YAML loaded from the original checkout
praisonai run --worktree agents.yaml
# Keep the worktree checkout for in-place review after the run
praisonai run --worktree --keep "Draft a migration plan"
# Two concurrent runs of the same target get independent worktrees/branches
praisonai run --worktree "task A" &
praisonai run --worktree "task A" &What happens on exit depends on what the run produced:
Changes produced (default)
The run commits everything (git add -A + git commit --no-verify) to the isolated branch, then removes the worktree checkout while keeping the branch. You’ll see:
Committed changes to branch '<branch>'. Review/merge with: git merge <branch>If the commit can’t be made (e.g. no git identity configured), the worktree is kept in place with a warning so output is never lost.
No changes produced
The worktree checkout and its branch are torn down entirely — nothing is left behind.
No changes on '<branch>'.–keep set
The worktree checkout stays in place for in-place review, regardless of whether changes were made.
Worktree kept at <path> (branch '<branch>'). Review/merge then remove with: git worktree remove.This CLI flag wraps the same core primitive documented for the Python API. Pick the layer that fits:
Provision worktrees directly from Python for concurrent agents.
Per-Kanban-Task WorktreesGive each kanban task its own worktree with workspace_kind="worktree".
Piped Input
praisonai run composes in Unix pipelines. Piped stdin is merged with your prompt argument (prompt first, then piped body).
cat error.log | praisonai run "Diagnose the root cause"
git diff | praisonai run "Review these changes for bugs"Piped input is skipped when:
TARGETis an existing.yaml/.ymlfile (case-insensitive).--agentor--commandis used.--restoreis set (the command exits before ingestion).
See Piped Input for the full behaviour.
Output Modes
--output controls how results and events are written to stdout.
text (default)
Rich-formatted human-readable output in the terminal.
praisonai run "What is the capital of France?"actions
Runs in-process with the core actions preset — a structured event stream written by the core preset. Standalone-safe (pip install praisonai-code).
praisonai run --output actions "What is the capital of France?"json
Emits one single-line {"result": "<text or null>", "status": "<outcome>"} envelope on stdout for every outcome — ok on success, truncated on a step-limit cutoff, failed on an empty-result failure, and content_filtered / refused / length_truncated on a provider block.
praisonai run --output json "What is the capital of France?"
# {"result": "Paris", "status": "ok"}A --output json | script pipe never sees empty stdout — the envelope is written before the exit-2 reporters run (fixes #5665 via PR #5685). Standalone-safe.
stream
Prints the final answer text on stdout. On exit 2 (blocked / truncated) it also prints the partial answer, unless --quiet is set. Standalone-safe.
praisonai run --output stream "What is the capital of France?"
# Parisstream-json
Emits one JSON object per line (NDJSON) as the run progresses — one event per agent action. Ideal for CI pipelines, scripts, and observability tooling.
praisonai run --output stream-json "Find the weather in London"Transient rate limits surface as a run.retry event before the wait, so a rate-limited run shows a live countdown instead of a silent hang:
{"event":"run.retry","data":{"schema_version":1,"attempt":2,"max_attempts":5,"delay":4.0,"reason":"rate_limit"}}On exit 2 raw partial text is deliberately not printed (it would corrupt the NDJSON protocol); the terminal run.result event carries ok: false instead. See Stream Events for the full event protocol reference.
silent
No stdout output — useful when you only need the exit code.
praisonai run --output silent "Run tests" && echo "passed"verbose
Includes diagnostic details alongside normal output. Useful for debugging.
praisonai run --output verbose "What is the capital of France?"Partial-answer stdout on exit 2
On a blocked / truncated run (exit 2), the mode-specific stdout payload is written before the exit reporter runs, so a pipe or redirect never loses it.
| Mode | Exit-2 stdout | Notes | |||
|---|---|---|---|---|---|
--output json | `{“result”: “<partial text or null>”, “status”: “truncated\ | content_filtered\ | refused\ | length_truncated”}` | Single-line envelope; always printed |
--output stream | The partial answer text | Suppressed when --quiet is set | |||
--output stream-json | Terminal run.result event with ok: false (no raw text) | Raw text would corrupt the NDJSON protocol | |||
--output actions | Structured events already emitted by the core preset | No extra CLI write |
The --output stream quiet-mode suppression keeps --quiet honest in the exit-2 path. The --output json envelope writes on every outcome (ok, failed, truncated, content_filtered, refused, length_truncated), so a scripting consumer can always parse .status out of stdout (via PR #5685).
Exit Codes
praisonai run returns a non-zero exit code whenever the agent fails to produce a result, so CI pipelines and scripts can branch on the exit code instead of scraping stderr.
| Exit | When | Emitted where |
|---|---|---|
0 | Agent returned a non-empty result and did not hit the step/iteration limit | All modes |
1 | Agent returned None or empty (swallowed LLM/auth error, guardrail block, tool failure) | All run modes (direct prompt, YAML file, --agent, --attach, --profile, --profile-deep) |
1 | --pick used, but the user cancelled or no sessions exist | Direct prompt / YAML |
1 | No API key configured in CI mode | Direct prompt |
1 | Worktree flag combined with --attach, --agent, --command, or --profile | Direct prompt |
1 | --image combined with --agent / --profile / a YAML file / --output actions, or a comma inside a single --image value | Direct-prompt run |
1 | Warm-runtime major-version mismatch on --attach | --attach <id> |
2 | Run hit its max_iter / max_steps budget and returned a wrap-up summary (agent.last_stop_reason == "max_steps") | Actions path (_run_prompt), --agent (custom-agent) path, --profile / --profile-deep (profiling report is still printed first) |
2 | Provider content filter blocked the response (agent.last_stop_reason == "content_filtered") | All three run paths (_run_prompt, --agent, --profile / --profile-deep) |
2 | Model refused to answer / safety refusal (agent.last_stop_reason == "refused") | All three run paths |
2 | Response hit the model’s output length limit (agent.last_stop_reason == "length_truncated") | All three run paths |
2 | Single-word direct prompt collides with a reserved command / typo guard | Direct prompt |
Unified envelope (--output json)
Every outcome prints a single-line {"result": "<text or null>", "status": "<outcome>"} envelope on stdout — success included:
{"result": "Paris", "status": "ok"}The subsections below document each non-ok outcome; they all share this envelope shape.
Failure payload (--output json / --output stream-json)
When the agent produces no result, the CLI emits a machine-readable failure object and exits 1:
{"status": "failed", "result": null}The paired error event carries the same status and an error code:
{"code": "run_failed", "status": "failed", "message": "Run failed: the agent did not produce a result."}The human-facing stderr line includes an actionable remediation:
Run failed: the agent did not produce a result.
Re-run with --verbose to see the underlying error, or check credentials with: praisonai setupTruncated payload (--output json / --output stream-json)
When a run hits its max_iter / max_steps budget, the CLI keeps the wrap-up summary but marks the run truncated and exits 2:
{"status": "truncated", "result": "…wrap-up summary text preserved verbatim…"}In interactive (non-JSON) mode the CLI also prints a one-line stderr notice so a human knows the answer is partial:
Run hit the step/iteration limit; the answer above is a summary of partial progress, not a completed task. Raise the budget with ExecutionConfig(max_steps=...) on the agent (or `execution: {max_steps: ...}` in the YAML) and re-run.JSON / stream-JSON consumers do not receive this stderr warning (the CLI checks output.is_json_mode) — the status: "truncated" field is the machine signal.
See Step Budget for how max_steps truncation works, and Agent Run Outcomes → CLI Mapping for the SDK-to-CLI status mapping.
Provider-block payload (--output json / --output stream-json)
When the model’s finish_reason / refusal signals a content-filter block, safety refusal, or length cutoff, the CLI preserves any partial text but marks the run with the specific reason and exits 2:
{"status": "content_filtered", "result": "<partial text or null>"}
{"status": "refused", "result": "<partial text or null>"}
{"status": "length_truncated", "result": "<partial text or null>"}In interactive (non-JSON) mode the CLI prints the matching human-readable warning to stderr:
Run blocked: the provider's content filter blocked the response.
Run refused: the model declined to answer (safety refusal).
Run truncated: the response hit the model's output length limit.JSON / stream-JSON consumers do not receive the stderr warning — the status field carries the machine signal. A provider block wins over a generic empty-result failure, so an actionable reason is never masked as a plain run_failed. See Provider block outcomes for the SDK-level classifier and precedence.
CI usage
Test the run in-process first — the CLI’s exit-code contract mirrors this success test.
from praisonaiagents import Agent
agent = Agent(instructions="Answer succinctly")
result = agent.start("Summarise yesterday's PRs")
if not (result and (not isinstance(result, str) or result.strip())):
raise SystemExit("Agent run failed")The CLI does the same success test for you and returns exit 1 on failure:
# Fails the CI job when the agent swallows an error
if ! praisonai run "Summarise yesterday's PRs"; then
echo "Agent run failed"
exit 1
fiBranch on the .status field of the unified envelope — every outcome carries one:
result=$(praisonai run --output json "Refactor the auth module and add tests")
status=$(echo "$result" | jq -r '.status')
case "$status" in
ok) echo "done" ;;
truncated) echo "hit the step limit; raise ExecutionConfig(max_steps=…) and re-run"; exit 2 ;;
content_filtered) echo "provider blocked the response; try rephrasing"; exit 2 ;;
refused) echo "model declined; try a different model"; exit 2 ;;
length_truncated) echo "response cut off; re-run with a higher --max-tokens"; exit 2 ;;
failed) echo "$result" | jq -r '.result // "run failed"'; exit 1 ;;
esacOr branch on the exit code directly:
praisonai run "…big task…"
case $? in
0) echo "done" ;;
1) echo "run failed" ;;
2) echo "incomplete run (truncated / content_filtered / refused / length_truncated) — check --output json status" ;;
esacSee Step Budget for the truncation contract, and Agent Run Outcomes → CLI Mapping for how the SDK-level statuses map to these exit signals.
Standalone vs wrapper
On a standalone pip install praisonai-code install, default run "…" and the human-readable text modes (--output plain/verbose/silent) route through the in-process Agent (PR #2853) — the same path used by the structured modes (--output actions|json|stream|stream-json). As of PR #3818, chat and code also open their resident interactive TUI standalone — pip install praisonai-code alone delivers run, chat, and code:
pip install praisonai-code
praisonai-code chat
praisonai-code code "Refactor this function"Every text mode — default run "…" and --output plain|verbose|silent|actions|json|stream|stream-json — plus interactive chat/code runs with no wrapper. The structured-mode in-process dispatch and the per-mode stdout contract (the {result, status} envelope for json, partial-answer-before-exit for stream) were cemented by PR #5685. The wrapper (pip install praisonai) still adds the gateway, channel bots, and the richer legacy interactive dispatch. See the PraisonAI Code CLI standalone-limits table for the full command matrix.
Examples
Run a built-in preset (no YAML required)
# Read-only planner — never modifies files
praisonai run --agent plan "explore the codebase"
# Code reviewer that asks before shell commands
praisonai run --agent review "review the recent diff"
# Full toolset
praisonai run --agent build "add a /health endpoint"Run with a custom agent
praisonai run --agent researcher "Find info on X"Run with delegatable subagents
Let the running agent hand named sub-tasks to your other .praisonai/agents/*.md agents, each under its own model/tools/permissions.
# Expose named agents with `mode: subagent` in frontmatter
praisonai run --agent lead "Research 3 vector DBs, then draft an eval script"
# Or opt any named agents in explicitly
praisonai run --agent lead --subagents researcher,coder "Research 3 vector DBs, then draft an eval script"Wiring is a no-op when no delegatable agents exist — default runs are unchanged. See Named Agent Delegation.
Run with a custom agent and CLI permission override
# Agent definition has mode: review (bash:*: ask)
# --allow overrides to let git commands run without prompting
praisonai run --agent reviewer "review the diff" --allow 'bash:git *'Run with reasoning effort
praisonai run --thinking medium "Plan a release checklist for v4.7"
# Works with custom agents too
praisonai run --agent researcher --thinking high "Deep dive on vector DB options"--thinking applies across direct-prompt, actions-mode, and custom-agent paths. See Thinking.
Run with a custom command
praisonai run --command summarise "Long text here"Session flags (--continue, --session, --fork, --no-save) work with --agent.
Run from YAML file
praisonai run agents.yamlTo gate a workflow file with the same --allow / --deny / --approval rules you use on a prompt run, see YAML Workflows Are Permission-Gated.
Run with a prompt
praisonai run "What is the capital of France?"Run in read-only planning mode
# Read-only run — the agent explores but writes nothing
praisonai run --plan "Audit the auth module and propose a refactor"--plan denies every mutating tool (write/edit/delete/shell/exec). See Permission Modes and Permissions.
Run in a monorepo
Touching a nested package auto-loads that package’s AGENTS.md — no config, same as chat.
# From the repo root, touching a nested package auto-loads packages/foo/AGENTS.md
praisonai run "refactor packages/foo/bar.py to use the new logger"
# Opt out for a one-off run
praisonai run --no-rules "just print the file, don't apply project rules"
# Or globally in CI
export PRAISON_NO_RULES=trueRun with specific model
praisonai run "Explain quantum computing" --model gpt-4oRun in interactive mode
praisonai run agents.yamlRun with memory enabled
praisonai run "Remember my name is John" --memoryRun with verbose output
praisonai run agents.yaml --verboseRun with custom tools
# By name — the agent picks up built-in tools, no file needed
praisonai run "Summarise today's AI news" --tools "web_search,github"
# Or from a file
praisonai run agents.yaml --tools tools.pyIn run actions mode, --tools and --toolset are now wired end-to-end (previously silently dropped). The same is now true for run --agent <name> — --tools/--toolset merge into the agent’s frontmatter tools: list instead of being dropped (#3047).
Run with project-local tools
Local .praisonai/tools/*.py files require an opt-in because loading them executes Python:
# Discoverable per-invocation flag (recommended)
praisonai run --allow-local-tools "use the greet tool"
# Env-var equivalent (persists for the shell)
PRAISONAI_ALLOW_LOCAL_TOOLS=true praisonai run "use the greet tool"
# --agent runs also auto-discover .praisonai/tools/*.py now
praisonai run --agent researcher --allow-local-tools "use the greet tool to greet Ada"If tool files exist but neither is set, run prints a one-line hint naming the location(s) and enable step — no more silent skip. See Project-Local Tools.
Resume a previous session
Continue where you left off in your current project:
praisonai run "now add tests" --continueResume a specific session by ID:
praisonai run "what were we working on?" --session abc12345Fork from a session to try a different approach:
praisonai run "try a different approach" --fork --session abc12345Pick a session to resume
No id handy? Use --pick (alias --interactive-session) to choose a recent session from a picker, then run a new prompt against it:
# Pick a recent session interactively, then run a new prompt against it
praisonai run --pick "now add tests"
# Alias
praisonai run --interactive-session "now add tests"--pick opens the same picker as session resume. It is a no-op when --session or --continue already names a session, and silently ignored under --json or a non-TTY so scripts and CI keep today’s behaviour. On a TTY with no --session / --continue, cancelling or an empty store aborts the run with exit code 1 rather than falling through to a fresh run.
Run without saving the session:
praisonai run "one-off question" --no-saveRun without project instruction files
By default, praisonai run auto-loads AGENTS.md, CLAUDE.md, PRAISON.md, etc. from the project root and attaches nested-subtree instruction files as the agent touches them. Use --no-rules (or PRAISON_NO_RULES=true) to opt out of both:
praisonai run "Quick one-off task" --no-rulesUse --verbose to see which instruction files were loaded:
praisonai run "What does this codebase do?" --verbose
# > Loaded project instructions: AGENTS.md, CLAUDE.mdIsolated worktree runs
Pass --worktree to run the agent on a fresh git branch and worktree so its output never touches your working tree. Add --keep to retain the worktree and branch for review.
praisonai run "Refactor the auth module" --worktree
praisonai run agents.yaml --worktree --keepSee Run in an Isolated Worktree for the full teardown behaviour matrix and rejected-combination rules.
Live session attach
Tag a warm-runtime run with a session id so other terminals can stream its events with praisonai attach.
# Terminal A
praisonai daemon start --background
praisonai run "Research topic X" --attach my-session
# Terminal B
praisonai attach my-sessionIsolated Runs (--worktree)
Add --worktree to run the agent on a fresh git branch and worktree so its edits never touch your working tree.
praisonai run "refactor auth to use bcrypt" --worktree
# > Isolated run on branch 'praisonai/refactor-auth-<uid>' (./.praisonai/worktrees/refactor-auth-<uid>)
# ... agent runs ...
# > Committed changes to branch 'praisonai/refactor-auth-<uid>'.
# > Review/merge with: git merge praisonai/refactor-auth-<uid>--worktree provisions a fresh git worktree on a new branch, chdirs into it, runs the agent, then prints the branch name and a git status --short summary. Every other flag (--tools, --model, --session, --thinking, --memory) applies unchanged.
How teardown protects your output
sequenceDiagram
participant User
participant CLI as praisonai run
participant Git
participant Agent
participant Workspace as Original cwd
User->>CLI: praisonai run --worktree "prompt"
CLI->>Git: git worktree add (fresh branch)
Git-->>CLI: worktree ready
CLI->>Workspace: chdir(worktree)
CLI->>Agent: run(...)
Agent-->>CLI: done (files changed?)
alt changes present
CLI->>Git: git add -A && git commit --no-verify
CLI->>Git: git worktree remove (checkout only)
CLI-->>User: "Committed changes to branch 'praisonai/...'. Review/merge with: git merge praisonai/..."
else no changes
CLI->>Git: git worktree remove + delete branch
CLI-->>User: "No changes on 'praisonai/...'."
end
classDef user fill:#6366F1,stroke:#7C90A0,color:#fff
classDef cli fill:#8B0000,stroke:#7C90A0,color:#fff
classDef git fill:#189AB4,stroke:#7C90A0,color:#fff
classDef agent fill:#10B981,stroke:#7C90A0,color:#fff
class User user
class CLI cli
class Git,Workspace git
class Agent agentChanges are detected with git status --porcelain, so brand-new untracked files count. When the agent produced output, the CLI auto-commits everything to the branch (praisonai run: <target>, --no-verify) and retains the branch — only the worktree checkout is pruned. A run with no changes is torn down completely.
If the auto-commit fails (for example no user.email/user.name is configured, or a hook rejects it), the worktree checkout is kept in place with a Could not commit isolated changes; worktree kept at <path> (branch '<branch>') for manual review. warning — your output is never lost.
What happens on teardown
The run detects changes with git status --porcelain, so brand-new (untracked) files are never lost.
| Situation | Worktree checkout | Branch | Reported |
|---|---|---|---|
| No changes | Removed | Removed | No changes on '<branch>'. |
| Any change (tracked or untracked) | Pruned | Retained with auto-commit | Committed changes to branch '<branch>'. Review/merge with: git merge <branch> |
--keep | Retained | Retained | Worktree kept at <path> (branch '<branch>'). Review/merge then remove with: git worktree remove. |
| Commit fails (e.g. no git identity) | Retained for manual review | Retained | Warning: Could not commit isolated changes; worktree kept at <path> ... |
| Cwd is not a git repo | n/a (no-op) | n/a | Warning: Not a git repository; running without worktree isolation. |
When to use it
Recipes
# Run an agent on a fresh branch, then merge if you like the result
praisonai run agents.yaml --worktree
# → prints: Committed changes to branch 'praisonai/agents.yaml-a1b2c3d4'.
# Review/merge with: git merge praisonai/agents.yaml-a1b2c3d4
git merge praisonai/agents.yaml-a1b2c3d4# Keep the worktree open for hands-on review
praisonai run agents.yaml --worktree --keep
# → prints: Worktree kept at /path (branch 'praisonai/...').
# Review/merge then remove with: git worktree remove.# Run several agents in parallel on the same repo — each gets its own branch
praisonai run "Add tests to lib/api" --worktree &
praisonai run "Refactor lib/db" --worktree &
waitA short uuid4 token is appended to each run’s branch name (e.g. praisonai/agents.yaml-a1b2c3d4), so concurrent runs of the same target never collide.
Compatibility
The CLI rejects incompatible combinations at parse time.
| Combined with | Behaviour |
|---|---|
--attach <id> | ❌ Error: --worktree cannot be combined with --attach — isolated runs stay in-process, but --attach targets the warm runtime (a separate process). |
--keep without --worktree | ❌ Error: --keep requires --worktree — --keep is meaningless without isolation. |
--agent <name> | ❌ Error: --worktree is only supported for direct prompt and YAML file runs — the custom-agent flow has its own execution path. |
--command <name> | ❌ Same error — the custom-command flow has its own execution path. |
--profile / --profile-deep | ❌ Same error — the profiling flow has its own execution path. |
--session <id> / --continue | ✅ Works — session state is stored outside the worktree. |
--tools, --toolset, --model, --thinking, --memory | ✅ Works — orthogonal to isolation. |
Warm runtime (praisonai daemon start) | ⚠️ Bypassed — isolated runs always execute in-process, even when a warm runtime is available. |
Best practices
Auto-commit means your output is never lost
On completion, any tracked or untracked change is committed to the branch and the branch is retained. Even if the auto-commit fails (missing user.email/user.name, hook rejection), the worktree checkout is kept in place — you never have to fight the CLI for your agent’s output.
Use –keep for inspection, not for saving output
Auto-commit already preserves your work on a branch. --keep is for cases where you want to review files in place before merging — compare screenshots, or run a local dev server against the checkout.
Non-git directories still work
The flag is a transparent no-op outside git. Wire it into every project script without conditionals: praisonai run --worktree "..." runs in the current cwd if there’s no repo, printing Not a git repository; running without worktree isolation.
Cleaning up abandoned worktrees
--keep never auto-removes. When you’re done: git worktree remove <path> && git branch -D praisonai/…. To see leftovers from all runs: git worktree list.
See Workspace Isolation for the underlying GitWorktreeAdapter and the Python API.
Project context
By default, praisonai run walks up from the current directory to your git root and prepends any AGENTS.md / CLAUDE.md / agents.md / .agents/AGENTS.md it finds to the agent’s system prompt, layered on top of ~/.praisonai/AGENTS.md. In a monorepo it also attaches a nested packages/foo/AGENTS.md the first time the agent touches a file under packages/foo/ (parity with chat, wired in PR #3552). Pass --no-context (or set PRAISON_NO_CONTEXT=true) to disable the up-front walk-up, or --no-rules (or PRAISON_NO_RULES=true) to disable both. See Context Files for details.
Declared instruction sources
To declare extra instruction sources — files, globs, or URLs — that load on every run, use the top-level instructions: config key or the repeatable --instructions flag. Config sources merge first, then --instructions flags append on top; the result is prepended to the agent’s backstory as # Project Instructions. See Instruction Sources.
# One-off: layer an extra rules file on top of AGENTS.md for this run only
praisonai run --instructions docs/rules.md "explain this codebase"
# Repeatable: multiple sources merge in order
praisonai run \
--instructions ~/company/ai-rules.md \
--instructions docs/standards/*.md \
"review the diff"The --agent and --command paths honour declared instructions too:
praisonai run --agent lead --instructions docs/rules.md "task"
praisonai run --command review --instructions docs/rules.md "task"--no-rules (or PRAISON_NO_RULES=true) suppresses these declared sources along with the auto-discovered walk-up.
Environment variables
These env vars gate the same context behaviour as the flags, for CI and scripted runs.
| Variable | Effect | Precedence |
|---|---|---|
PRAISON_NO_RULES=true (1/true/yes, case-insensitive) | Same as passing --no-rules; skips both the up-front load and the subtree hook | CLI flag wins if both set |
PRAISON_NO_CONTEXT=true | Skips the up-front AGENTS.md/CLAUDE.md walk-up injection | CLI flag wins if both set |
PRAISON_CONTEXT_BUDGET=<int> | Char budget for the subtree hook. 0 (or missing / non-integer) disables the cap. | Applies to both chat and run |
First-run Credential Check
praisonai run verifies credentials are configured before doing any work. With no cloud key, it first checks for a reachable local endpoint and adopts it automatically — the prompt below only appears when nothing is reachable.
If no cloud key and no local endpoint is found, you’ll see:
Interactive (TTY):
No API key configured.
Would you like to run the setup wizard now? [Y/n]:Local model reachable (no cloud key needed):
No cloud key found; using local model ollama/llama3.2. Run `praisonai setup` to add a hosted provider.CI / non-interactive (no key and no local endpoint):
Error: No API key configured. Run: praisonai setup
(a running local endpoint such as Ollama would be used automatically)When no cloud key is set, PraisonAI probes for a local OpenAI-compatible endpoint (Ollama or any /v1-speaking server) and adopts it automatically before falling back — see Keyless local-first fallback. Non-TTY/CI still exits 1 when nothing is detected, so pipelines fail safe.
Exit code is 1 in CI mode. Set any supported env var to bypass the check entirely:
export OPENAI_API_KEY=sk-...
praisonai run "hello"“Supported env var” now means any of the 14 credential keys in the Credential → default-model map, so MISTRAL_API_KEY (or any catalogue key) also silences the credential gate:
export MISTRAL_API_KEY=...
praisonai run "hello"
# Uses mistral/mistral-large-latest — no --model, no wizard.Local model fallback
When no cloud key is configured, praisonai run "..." probes for a reachable local endpoint (Ollama or any OpenAI-compatible server) and adopts its model + base URL for the run — no --model needed. A cloud key always wins; the local probe only runs as a fallback.
ollama serve
praisonai run "Summarise this file"
# stderr: No cloud key found; using local model ollama/llama3.2. Run `praisonai setup` to add a hosted provider.See Auth, First-run Onboarding, and Keyless Local-First Run for the full behaviour matrix and CI examples.
Session Continuity
Pick up where you left off — praisonai run remembers per-project conversations and tracks cumulative token usage and cost.
Continue / session runs attach to the warm daemon
When a warm daemon is running, --continue and --session <id> runs now attach to it and reuse a warm, per-session agent instead of paying cold-start each turn. The first turn rehydrates history from the project session store; subsequent turns run against the retained warm agent. On failure the warm agent is dropped and the next turn rehydrates cleanly from the store, so recovery semantics match the in-process path. Fork (--fork) still runs the first turn in-process to mint the new id, then attaches warm on later turns against that id. See Daemon for the full warm-runtime lifecycle.
Usage footer
When running with an active session (--session <id> or --continue), a compact footer appears after each answer:
1,240 in / 3,980 out · $0.0140The footer is suppressed in --json / --output json mode, but usage is still persisted into the session record. Totals accumulate across runs and survive resume. See Cost & Token Tracking for the full breakdown.
- Continue the last run
Continue the most recent session for your current project:
bash praisonai run --continue "now add tests"--continuesearches both the project store and the global default store, so it resolves the genuinely most-recent root session — including ones created bychat, gateway, TUI, API, or a bareAgent(memory={"session_id": "..."}). If no previous session exists, a warning is shown and a new session starts. - Resume a specific session
Resume a specific session by ID (find IDs with
praisonai session list):bash praisonai run --session abc123 "what were we working on?"Errors out if the session ID does not exist in the current project.
- Try a different approach without losing history
Fork from an existing session to try alternatives:
bash praisonai run --fork --session abc123 "try Postgres instead of SQLite"Creates a new session ID copied from the source. Both sessions evolve independently.
- What gets restored on –continue / –session
When you use
--continueor--session <id>, every prior user, assistant, assistant-tool-call, and tool-result message in that session is replayed into the agent’s chat history before your new prompt runs. The agent answers with full awareness of what was discussed and what tools it called — no manual context-passing required. Tool-call persistence in the default JSON store landed in PraisonAI PR #3099.Diagram sequenceDiagram participant User participant CLI participant PS as Project session store participant GS as Global default store participant Agent User->>CLI: praisonai run --continue "..." CLI->>PS: list_sessions(limit) CLI->>GS: list_sessions(limit) Note over CLI: merge + dedup by session_id, freshest wins, prefer root sessions CLI->>PS: get_chat_history(session_id) PS-->>CLI: prior turns CLI->>Agent: pre-populate chat_history, then run new prompt Agent-->>User: response (aware of prior turns) classDef cli fill:#8B0000,stroke:#7C90A0,color:#fff classDef store fill:#189AB4,stroke:#7C90A0,color:#fff classDef result fill:#10B981,stroke:#7C90A0,color:#fff class User,CLI cli class PS,GS store class Agent resultRestored automatically: user, assistant, assistant-tool-call, and tool-result
chat_historymessages;auto_savecontinues for the resumed session.Not restored: intermediate scratchpad beyond the persisted tool turns; file artefacts from earlier runs remain on disk but are not re-emitted.
Restored From Chat history (user + assistant + tool-call + tool-result messages) Project store: ~/.praisonai/sessions/projects/<project_id>/<session_id>.json— or global store:~/.praisonai/sessions/<session_id>.json(used when--continuepicks up a session created bychat, gateway, TUI, API, or a bareAgent(memory={"session_id": "..."}))Auto-save bookmark (only new messages appended) Same file the session lives in Session ID --session <id>flag, or the last root session across both stores for--continue
Choosing between the flags
Prompt mode
# First run
praisonai run "Build a FastAPI todo app"
# Continue tomorrow
praisonai run --continue "now add tests"
# Continue with specific session
praisonai run --session abc123 "deploy it to Fly.io"
# Resume while emitting structured tool actions (JSON stream)
praisonai run --session abc123 --output actions "deploy it to Fly.io"YAML mode
# First run
praisonai run agents.yaml
# Continue with YAML file
praisonai run agents.yaml --continue
# Continue with specific session
praisonai run agents.yaml --session abc123
# Fork a YAML run to try a variation
praisonai run agents.yaml --fork --session abc123Session usage footer
After every prompt run inside an active session, a single-line footer shows cumulative token and cost totals:
$ praisonai run "Summarise yesterday's PRs"
... (agent output) ...
1,240 in / 3,980 out · $0.0140The format is:
{input_tokens:,} in / {output_tokens:,} out · ${cost:.4f}- Totals are cumulative since session start — not just the last prompt.
- Best-effort: if usage cannot be read or priced, the footer is silently skipped.
- Suppressed in JSON mode (
--output json,--output stream-json). There is no--no-usageflag. - Usage is persisted to
~/.praisonai/sessions/<id>.jsonso resuming with--continueor--session <id>rehydrates the totals and keeps accumulating.
JSON mode — the footer is suppressed; the stdout envelope carries result and status, and usage is attached when the run records it:
$ praisonai run --output json "Summarise yesterday's PRs"
{"result":"...","status":"ok","session_id":"abc12345","usage":{"input_tokens":1240,"output_tokens":3980,"total_tokens":5220,"cost":0.0140,"requests":1}}The minimal envelope is {"result": "...", "status": "ok"}; session_id and usage are added on paths that record them.
Troubleshooting session continuity
`–continue` runs but the agent has no memory of the previous turn
Fixed in PR #1963 — sessions were discovered but history was not loaded. Upgrade praisonai and re-run.
Fixed by commit 7016bfa (issue #2700). The actions-output branch of run re-imported build_cli_memory_config / apply_cli_session_continuity from praisonai_code.cli.utils.project, shadowing the correct praisonai_code.cli.state.project_sessions versions. The stale variant lacked the auto_save keyword, so wiring session continuity in actions mode raised TypeError: apply_cli_session_continuity() got an unexpected keyword argument 'auto_save'. Upgrade praisonai-code and re-run.
Session continuity works in prompt mode but not with agents.yaml
Also fixed by PR #1963 — YAML and file-mode runs use the same project session store as prompt-mode runs.
`praisonai-code run –output json '…' | script` prints nothing on standalone
Pre-PR #5685 (merged 2026-10-07), --output json/stream/stream-json on a pip install praisonai-code-only install fell through to the wrapper’s handle_direct_prompt and failed with praisonai.cli.legacy.direct_prompt requires the praisonai wrapper. Upgrade to a build with #5685 — the three structured modes now dispatch in-process and --output json writes the {result, status} envelope on every outcome.
Agent File Format
Create an agents.yaml file:
framework: praisonai
topic: Research Assistant
roles:
researcher:
backstory: Expert research analyst
goal: Find accurate information
role: Researcher
tasks:
research_task:
description: Research the given topic
expected_output: Comprehensive research summarySession Management
Sessions are scoped to the current project (git root, or current directory if not a git repository). Each run auto-saves to a generated session-<uuid8> unless --no-save is set.
Checkpoint & Rewind
praisonai run auto-checkpoints your workspace before YAML-file runs so a bad turn can be rewound with one command.
sequenceDiagram
participant User
participant CLI as praisonai run
participant CP as Checkpoint Engine
participant Agents
User->>CLI: praisonai run agents.yaml
CLI->>CP: auto-checkpoint (run:<id>)
CP-->>CLI: ✅ saved
CLI->>Agents: execute
Agents-->>User: results
User->>CLI: praisonai run --restore last
CLI->>CP: restore last checkpoint
CP-->>User: workspace rewound
classDef user fill:#6366F1,stroke:#7C90A0,color:#fff
classDef cli fill:#8B0000,stroke:#7C90A0,color:#fff
classDef cp fill:#189AB4,stroke:#7C90A0,color:#fff
classDef agents fill:#10B981,stroke:#7C90A0,color:#fff
class User user
class CLI cli
class CP cp
class Agents agentspraisonai run agents.yaml # auto-checkpoint, then run
praisonai run --restore last # rewind workspace, exit
praisonai run agents.yaml --no-checkpoint # skip the auto-checkpointAuto-checkpoint behaviour:
- Runs before any YAML-file execution (
.yaml/.ymltargets only). - Label:
run:<run_id>(or"auto checkpoint before run"as a fallback). - Workspace: the directory of the target YAML file, not the current directory.
- Plain-prompt runs are skipped to avoid empty-checkpoint noise.
- Best-effort — failures are swallowed and never block the run. Use
--verboseto see"Auto-checkpoint skipped: …"on failure. - Gated by
checkpoints.auto(defaulttrue) in your project config; override per-run with--no-checkpoint. - Reads
checkpoints.storage_dirfrom your project config so the auto-checkpoint/restore path shares the same store aspraisonai code --checkpointssessions. See Checkpoints for the config block.
New checkpoint flags:
| Flag | Description | Default | ||
|---|---|---|---|---|
| `–restore <id\ | last\ | latest>` | Restore the workspace to a checkpoint and exit — nothing runs. | — |
--no-checkpoint | Disable the automatic pre-run checkpoint for this invocation. | false |
Coherent rewind (--revert)
Roll files and the named session’s conversation back to a prior turn, then exit. Distinct from --restore, which is file-only.
praisonai run --revert # last turn, files-only preview
praisonai run --revert last --session my-feat # last turn, rewinds my-feat conversation too
praisonai run --revert 3 --session my-feat # last 3 turns| Argument | Effect |
|---|---|
--revert (no value) | Revert the last 1 turn. |
--revert last / --revert latest | Revert the last 1 turn. |
--revert N | Revert the last N turns (N ≥ 1). |
When --session <id> is provided the flag also rewinds that session’s conversation to the same boundary. Rewind counts real user messages (not a fixed 2·N assumption) so tool/system messages don’t orphan a partial turn.
YAML Workflows Are Permission-Gated
praisonai run workflow.yaml honours the same approval and permission flags as a single-agent praisonai run "<prompt>". The flags are threaded onto the workflow engine, so a YAML run can no longer bypass a deny rule that a prompt run would enforce.
praisonai run workflow.yaml \
--deny "bash:rm *" \
--allow "bash:pytest *" \
--approval console \
--approval-timeout 60Precedence ladder
| Priority | Condition | Result |
|---|---|---|
| 1 | --approval <mode> set explicitly | That backend wins (console, plan, accept-edits, bypass) |
| 2 | Any --allow / --deny / --permissions / --permission-default present, no --approval | Implicit console backend so deny / ask patterns are enforced |
| 3 | Project config rules (.praisonai/permissions.yaml, etc.) | Merged into the same rule set as the prompt path |
| 4 | No approval flags and no permission rules | Legacy default — no gate, prior behaviour preserved exactly |
What propagates to a YAML run
Explicit –approval wins
praisonai run workflow.yaml --approval console (or plan, accept-edits, bypass) sets the backend for the workflow run — identical to the prompt path.
Permission rules imply –approval console
A --deny, --allow, --permissions, or --permission-default on its own activates a console backend. Without this, a permission rule on a YAML workflow used to be silently bypassed.
# No --approval, but the deny rule still activates a console backend
praisonai run workflow.yaml --deny "bash:rm *"–approve-all-tools and –approval-timeout propagate
Both propagate to YAML runs, matching the single-agent run "<prompt>" semantics.
praisonai run workflow.yaml --approve-all-tools --approval-timeout 120Backward-compatible default (no flags → no gate)
A plain praisonai run workflow.yaml --no-save with no permission or approval flags threads no approval override at all, so the legacy engine preserves prior behaviour exactly. Existing pipelines see no change.
Composes with session flags
--continue, --session <id>, --fork, and --no-save already flow into the same YAML engine; the approval/permission flags now compose with them. A --no-save workflow run with only --allow sets approval without persisting a session.
Rule-source precedence (CLI + project config)
Permission rules come from CLI flags and project config, merged the same way the prompt path merges them — so a YAML run picks up .praisonai/permissions.yaml (or equivalent) identically. See Permissions.
For the full approval-gating story and backend semantics, see Approval › YAML workflow approval gating.
See Also
- Isolated Runs (
--worktree) – Run on a fresh git branch per run - Session – Session management commands
- Project-Scoped Sessions – How project sessions work
- Checkpoints – Auto-checkpoint and rewind feature
- Checkpoint CLI – Checkpoint subcommands
- Agents – Agent management
- Custom Agents, Commands & Tools – Define agents and how their tools compose on
--agent - Project-Local Tools – Auto-discovered
.praisonai/tools/*.pyonrunandrun --agent - Workspace Isolation –
--worktreeper-run git isolation and theGitWorktreeAdapterPython API - Workflow – Workflow execution
- Interactive TUI – Interactive terminal interface
- Attach – Stream live events from a warm-runtime session
- Workspace Isolation – Per-agent git worktree adapter (the SDK primitive
--worktreeexposes)