You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase 1a of #1519 (merged in #1521) shipped the Agent(cli_backend="claude-code") Python surface. Phase 1b completes the "3 ways to do everything" commitment in AGENTS.md by adding the YAML and CLI surfaces for the exact same CLI Backend Protocol, and adding a back-compat smoke test for the legacy --external-agent flow.
End goal: the following three snippets must produce equivalent results.
# 1. Python (already landed via #1521)frompraisonaiimportAgentagent=Agent(name="coder", cli_backend="claude-code")
agent.start("Refactor utils.py")
agents_generator.pyPraisonAgent(...) call at line 1250 forwards cli_backend=details.get('cli_backend') (one-line change)
agents_generator.pydetails.get('cli_backend') accepts either a string id ("claude-code") or a dict {id: "claude-code", overrides: {timeout_ms: 60000}} and resolves through praisonai.cli_backends.resolve_cli_backend(id, overrides=...)
YAML config passing cli_backend: claude-code at role level produces an agent with agent._cli_backend is not None
CLI
New argparse flag: praisonai --cli-backend {claude-code,...} with choices pulled dynamically from list_cli_backends() so new backends auto-appear
Help text: "Delegate agent turns to a CLI backend (see praisonai backends list)"
When --cli-backend is passed, the auto-generated agent is constructed with cli_backend=args.cli_backend
New subcommand praisonai backends list prints registered backend ids, one per line
Mutual exclusion: --cli-backend X --external-agent Y → argparse error "--cli-backend and --external-agent are mutually exclusive"
Back-compat (MUST NOT BREAK)
praisonai "Hello" --external-agent claude --external-agent-direct still reaches ExternalAgentsHandler.get_integration("claude") unchanged
A new smoke test asserts --external-agent claude path still resolves the integration (mocked subprocess)
Overview
Phase 1a of #1519 (merged in #1521) shipped the
Agent(cli_backend="claude-code")Python surface. Phase 1b completes the "3 ways to do everything" commitment in AGENTS.md by adding the YAML and CLI surfaces for the exact same CLI Backend Protocol, and adding a back-compat smoke test for the legacy--external-agentflow.End goal: the following three snippets must produce equivalent results.
Background
praisonaiagents.cli_backend.protocols+praisonai.cli_backends.{registry,claude}+praisonai.Agent(cli_backend=...)wrapperlist_cli_backends()→['claude-code']; new backends (codex-cli,gemini-cli) will register the same way--external-agent {claude,gemini,codex,cursor}flag still exists and must keep working (back-compat gate)Current State (verified on
main@6ee8fdff)src/praisonai/praisonai/agent.pyAgent(cli_backend="claude-code")workssrc/praisonai/praisonai/agents_generator.py:1250-1267(PraisonAgent(...)kwargs)cli_backendparsecli_backend=details.get('cli_backend')src/praisonai/praisonai/cli/main.py:1061--external-agentonly--cli-backendsrc/praisonai/praisonai/cli/features/external_agents.py--external-agent claudeAcceptance Criteria
API / YAML
agents_generator.pyPraisonAgent(...)call at line 1250 forwardscli_backend=details.get('cli_backend')(one-line change)agents_generator.pydetails.get('cli_backend')accepts either a string id ("claude-code") or a dict{id: "claude-code", overrides: {timeout_ms: 60000}}and resolves throughpraisonai.cli_backends.resolve_cli_backend(id, overrides=...)cli_backend: claude-codeat role level produces an agent withagent._cli_backend is not NoneCLI
praisonai --cli-backend {claude-code,...}withchoicespulled dynamically fromlist_cli_backends()so new backends auto-appear"Delegate agent turns to a CLI backend (see praisonai backends list)"--cli-backendis passed, the auto-generated agent is constructed withcli_backend=args.cli_backendpraisonai backends listprints registered backend ids, one per line--cli-backend X --external-agent Y→argparseerror"--cli-backend and --external-agent are mutually exclusive"Back-compat (MUST NOT BREAK)
praisonai "Hello" --external-agent claude --external-agent-directstill reachesExternalAgentsHandler.get_integration("claude")unchanged--external-agent claudepath still resolves the integration (mocked subprocess)Tests
src/praisonai/tests/unit/test_yaml_cli_backend.py— parse YAML withcli_backend: claude-code, assert agent wired (≥ 3 tests)src/praisonai/tests/unit/test_cli_backend_flag.py— argparse-level tests for--cli-backendandbackends list(≥ 4 tests)src/praisonai/tests/unit/test_external_agents_backcompat.py— one regression test for--external-agent claudePerf / invariants (do not regress)
praisonaiagentsimport time stays < 50 ms (currently ~11 ms)praisonaiagents.cli_backendinagents_generator.py— use lazydetails.get('cli_backend')branchpraisonaiagents) to wrapper (praisonai) — invariant from feat: CLI Backend Protocol - Claude Code as first-class Agent backend (Phase 1) #1521 must holdFile-by-file plan
Modify
src/praisonai/praisonai/agents_generator.pycli_backendparam toPraisonAgent(...)at line 1250; accept str or dictsrc/praisonai/praisonai/cli/main.py--cli-backendargparse flag near line 1061; wire into auto-agent path; add mutual-exclusion checksrc/praisonai/praisonai/cli/commands/__init__.pyor newbackends.pypraisonai backends listsubcommandCreate
src/praisonai/tests/unit/test_yaml_cli_backend.pysrc/praisonai/tests/unit/test_cli_backend_flag.pysrc/praisonai/tests/unit/test_external_agents_backcompat.pyexamples/yaml/cli_backend.yamlcli_backend: claude-codeVerification commands (paste-runnable)
Out of scope (track separately)
codex-cli,gemini-cli,cursor-cli) — landing the protocol plumbing onlyPraisonAIDocs— follow-upReferences
6ee8fdff)src/praisonai-agents/praisonaiagents/cli_backend/protocols.pysrc/praisonai/praisonai/cli_backends/registry.py