praisonai-code 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. praisonai_code/__init__.py +17 -0
  2. praisonai_code/cli/__init__.py +12 -0
  3. praisonai_code/cli/_forward_shim.py +10 -0
  4. praisonai_code/cli/_paths.py +88 -0
  5. praisonai_code/cli/_warnings.py +58 -0
  6. praisonai_code/cli/app.py +757 -0
  7. praisonai_code/cli/approval_backend.py +272 -0
  8. praisonai_code/cli/branding.py +94 -0
  9. praisonai_code/cli/commands/__init__.py +114 -0
  10. praisonai_code/cli/commands/acp.py +80 -0
  11. praisonai_code/cli/commands/agent.py +116 -0
  12. praisonai_code/cli/commands/agents.py +80 -0
  13. praisonai_code/cli/commands/app.py +139 -0
  14. praisonai_code/cli/commands/attach.py +95 -0
  15. praisonai_code/cli/commands/audit.py +102 -0
  16. praisonai_code/cli/commands/auth.py +508 -0
  17. praisonai_code/cli/commands/batch.py +848 -0
  18. praisonai_code/cli/commands/benchmark.py +286 -0
  19. praisonai_code/cli/commands/browser.py +299 -0
  20. praisonai_code/cli/commands/call.py +45 -0
  21. praisonai_code/cli/commands/chat.py +332 -0
  22. praisonai_code/cli/commands/checkpoint.py +170 -0
  23. praisonai_code/cli/commands/code.py +276 -0
  24. praisonai_code/cli/commands/command.py +114 -0
  25. praisonai_code/cli/commands/commit.py +47 -0
  26. praisonai_code/cli/commands/completion.py +333 -0
  27. praisonai_code/cli/commands/config.py +681 -0
  28. praisonai_code/cli/commands/context.py +414 -0
  29. praisonai_code/cli/commands/daemon.py +203 -0
  30. praisonai_code/cli/commands/debug.py +142 -0
  31. praisonai_code/cli/commands/deploy.py +71 -0
  32. praisonai_code/cli/commands/diag.py +55 -0
  33. praisonai_code/cli/commands/docs.py +1575 -0
  34. praisonai_code/cli/commands/doctor.py +332 -0
  35. praisonai_code/cli/commands/endpoints.py +51 -0
  36. praisonai_code/cli/commands/environment.py +179 -0
  37. praisonai_code/cli/commands/eval.py +131 -0
  38. praisonai_code/cli/commands/examples.py +953 -0
  39. praisonai_code/cli/commands/flow.py +436 -0
  40. praisonai_code/cli/commands/github.py +752 -0
  41. praisonai_code/cli/commands/hooks.py +74 -0
  42. praisonai_code/cli/commands/init.py +174 -0
  43. praisonai_code/cli/commands/knowledge.py +440 -0
  44. praisonai_code/cli/commands/langextract.py +120 -0
  45. praisonai_code/cli/commands/langfuse.py +984 -0
  46. praisonai_code/cli/commands/loop.py +211 -0
  47. praisonai_code/cli/commands/lsp.py +112 -0
  48. praisonai_code/cli/commands/managed.py +659 -0
  49. praisonai_code/cli/commands/mcp.py +763 -0
  50. praisonai_code/cli/commands/memory.py +298 -0
  51. praisonai_code/cli/commands/models.py +264 -0
  52. praisonai_code/cli/commands/n8n.py +326 -0
  53. praisonai_code/cli/commands/obs.py +19 -0
  54. praisonai_code/cli/commands/package.py +76 -0
  55. praisonai_code/cli/commands/paths.py +106 -0
  56. praisonai_code/cli/commands/permissions.py +272 -0
  57. praisonai_code/cli/commands/plugins.py +609 -0
  58. praisonai_code/cli/commands/port.py +530 -0
  59. praisonai_code/cli/commands/profile.py +466 -0
  60. praisonai_code/cli/commands/publish.py +193 -0
  61. praisonai_code/cli/commands/rag.py +913 -0
  62. praisonai_code/cli/commands/realtime.py +52 -0
  63. praisonai_code/cli/commands/recipe.py +684 -0
  64. praisonai_code/cli/commands/registry.py +59 -0
  65. praisonai_code/cli/commands/replay.py +830 -0
  66. praisonai_code/cli/commands/research.py +49 -0
  67. praisonai_code/cli/commands/retrieval.py +377 -0
  68. praisonai_code/cli/commands/rules.py +71 -0
  69. praisonai_code/cli/commands/run.py +1573 -0
  70. praisonai_code/cli/commands/sandbox.py +371 -0
  71. praisonai_code/cli/commands/schedule.py +529 -0
  72. praisonai_code/cli/commands/serve.py +690 -0
  73. praisonai_code/cli/commands/session.py +450 -0
  74. praisonai_code/cli/commands/setup.py +174 -0
  75. praisonai_code/cli/commands/skills.py +545 -0
  76. praisonai_code/cli/commands/standardise.py +711 -0
  77. praisonai_code/cli/commands/templates.py +54 -0
  78. praisonai_code/cli/commands/test.py +558 -0
  79. praisonai_code/cli/commands/todo.py +74 -0
  80. praisonai_code/cli/commands/tools.py +205 -0
  81. praisonai_code/cli/commands/traces.py +145 -0
  82. praisonai_code/cli/commands/tracker.py +852 -0
  83. praisonai_code/cli/commands/train.py +613 -0
  84. praisonai_code/cli/commands/ui.py +172 -0
  85. praisonai_code/cli/commands/up.py +354 -0
  86. praisonai_code/cli/commands/validate.py +291 -0
  87. praisonai_code/cli/commands/version.py +101 -0
  88. praisonai_code/cli/commands/workflow.py +97 -0
  89. praisonai_code/cli/config_loader.py +437 -0
  90. praisonai_code/cli/configuration/__init__.py +27 -0
  91. praisonai_code/cli/configuration/config.schema.json +57 -0
  92. praisonai_code/cli/configuration/credentials.py +446 -0
  93. praisonai_code/cli/configuration/loader.py +364 -0
  94. praisonai_code/cli/configuration/model_resolver.py +161 -0
  95. praisonai_code/cli/configuration/oauth.py +389 -0
  96. praisonai_code/cli/configuration/paths.py +224 -0
  97. praisonai_code/cli/configuration/resolver.py +687 -0
  98. praisonai_code/cli/configuration/schema.py +317 -0
  99. praisonai_code/cli/execution/__init__.py +99 -0
  100. praisonai_code/cli/execution/core.py +208 -0
  101. praisonai_code/cli/execution/profiler.py +898 -0
  102. praisonai_code/cli/execution/request.py +85 -0
  103. praisonai_code/cli/execution/result.py +74 -0
  104. praisonai_code/cli/fallback_schema.py +416 -0
  105. praisonai_code/cli/features/__init__.py +278 -0
  106. praisonai_code/cli/features/_endpoint_registry.py +64 -0
  107. praisonai_code/cli/features/_search_registry.py +43 -0
  108. praisonai_code/cli/features/acp.py +236 -0
  109. praisonai_code/cli/features/action_orchestrator.py +576 -0
  110. praisonai_code/cli/features/agent_scheduler.py +773 -0
  111. praisonai_code/cli/features/agent_tools.py +603 -0
  112. praisonai_code/cli/features/agents.py +397 -0
  113. praisonai_code/cli/features/at_mentions.py +471 -0
  114. praisonai_code/cli/features/audit_cli.py +270 -0
  115. praisonai_code/cli/features/auto_memory.py +182 -0
  116. praisonai_code/cli/features/auto_mode.py +552 -0
  117. praisonai_code/cli/features/autonomy_mode.py +546 -0
  118. praisonai_code/cli/features/background.py +356 -0
  119. praisonai_code/cli/features/base.py +168 -0
  120. praisonai_code/cli/features/benchmark.py +1462 -0
  121. praisonai_code/cli/features/capabilities.py +1326 -0
  122. praisonai_code/cli/features/checkpoints.py +345 -0
  123. praisonai_code/cli/features/cli_profiler.py +335 -0
  124. praisonai_code/cli/features/code_intelligence.py +666 -0
  125. praisonai_code/cli/features/compaction.py +294 -0
  126. praisonai_code/cli/features/compare.py +534 -0
  127. praisonai_code/cli/features/config_hierarchy.py +366 -0
  128. praisonai_code/cli/features/context_manager.py +597 -0
  129. praisonai_code/cli/features/cost_tracker.py +514 -0
  130. praisonai_code/cli/features/csv_test_runner.py +736 -0
  131. praisonai_code/cli/features/custom_definitions.py +790 -0
  132. praisonai_code/cli/features/debug.py +810 -0
  133. praisonai_code/cli/features/deploy.py +605 -0
  134. praisonai_code/cli/features/diag.py +289 -0
  135. praisonai_code/cli/features/display_jsonl.py +173 -0
  136. praisonai_code/cli/features/doctor/__init__.py +63 -0
  137. praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
  138. praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
  139. praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
  140. praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
  141. praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
  142. praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
  143. praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
  144. praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
  145. praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
  146. praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
  147. praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
  148. praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
  149. praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
  150. praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
  151. praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
  152. praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
  153. praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
  154. praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
  155. praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
  156. praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
  157. praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
  158. praisonai_code/cli/features/doctor/engine.py +266 -0
  159. praisonai_code/cli/features/doctor/formatters.py +377 -0
  160. praisonai_code/cli/features/doctor/handler.py +564 -0
  161. praisonai_code/cli/features/doctor/models.py +276 -0
  162. praisonai_code/cli/features/doctor/registry.py +239 -0
  163. praisonai_code/cli/features/endpoints.py +1016 -0
  164. praisonai_code/cli/features/eval.py +559 -0
  165. praisonai_code/cli/features/examples.py +707 -0
  166. praisonai_code/cli/features/external_agents.py +231 -0
  167. praisonai_code/cli/features/fast_context.py +410 -0
  168. praisonai_code/cli/features/file_history.py +320 -0
  169. praisonai_code/cli/features/flow_display.py +566 -0
  170. praisonai_code/cli/features/git_attribution.py +159 -0
  171. praisonai_code/cli/features/git_integration.py +651 -0
  172. praisonai_code/cli/features/guardrail.py +171 -0
  173. praisonai_code/cli/features/handoff.py +252 -0
  174. praisonai_code/cli/features/hooks.py +583 -0
  175. praisonai_code/cli/features/hybrid_workflow.py +391 -0
  176. praisonai_code/cli/features/image.py +384 -0
  177. praisonai_code/cli/features/interactive_core_headless.py +450 -0
  178. praisonai_code/cli/features/interactive_runtime.py +600 -0
  179. praisonai_code/cli/features/interactive_test_harness.py +537 -0
  180. praisonai_code/cli/features/interactive_tools.py +428 -0
  181. praisonai_code/cli/features/interactive_tui.py +603 -0
  182. praisonai_code/cli/features/job_workflow.py +906 -0
  183. praisonai_code/cli/features/jobs.py +632 -0
  184. praisonai_code/cli/features/knowledge.py +531 -0
  185. praisonai_code/cli/features/knowledge_cli.py +438 -0
  186. praisonai_code/cli/features/lite.py +244 -0
  187. praisonai_code/cli/features/logs.py +200 -0
  188. praisonai_code/cli/features/lsp_cli.py +225 -0
  189. praisonai_code/cli/features/lsp_diagnostics.py +185 -0
  190. praisonai_code/cli/features/mcp.py +344 -0
  191. praisonai_code/cli/features/message_queue.py +587 -0
  192. praisonai_code/cli/features/metrics.py +210 -0
  193. praisonai_code/cli/features/migrate.py +1329 -0
  194. praisonai_code/cli/features/migration_flow.py +463 -0
  195. praisonai_code/cli/features/migration_spec.py +276 -0
  196. praisonai_code/cli/features/n8n.py +703 -0
  197. praisonai_code/cli/features/observability.py +293 -0
  198. praisonai_code/cli/features/ollama.py +361 -0
  199. praisonai_code/cli/features/output_modes.py +155 -0
  200. praisonai_code/cli/features/output_style.py +273 -0
  201. praisonai_code/cli/features/package.py +631 -0
  202. praisonai_code/cli/features/performance.py +308 -0
  203. praisonai_code/cli/features/persistence.py +636 -0
  204. praisonai_code/cli/features/profiler/__init__.py +81 -0
  205. praisonai_code/cli/features/profiler/core.py +558 -0
  206. praisonai_code/cli/features/profiler/optimizations.py +652 -0
  207. praisonai_code/cli/features/profiler/suite.py +386 -0
  208. praisonai_code/cli/features/queue/__init__.py +73 -0
  209. praisonai_code/cli/features/queue/manager.py +435 -0
  210. praisonai_code/cli/features/queue/models.py +289 -0
  211. praisonai_code/cli/features/queue/persistence.py +564 -0
  212. praisonai_code/cli/features/queue/scheduler.py +529 -0
  213. praisonai_code/cli/features/queue/worker.py +400 -0
  214. praisonai_code/cli/features/recipe.py +2187 -0
  215. praisonai_code/cli/features/recipe_creator.py +996 -0
  216. praisonai_code/cli/features/recipe_optimizer.py +1364 -0
  217. praisonai_code/cli/features/recipe_prompts.py +226 -0
  218. praisonai_code/cli/features/registry.py +229 -0
  219. praisonai_code/cli/features/repo_map.py +860 -0
  220. praisonai_code/cli/features/router.py +466 -0
  221. praisonai_code/cli/features/safe_shell.py +427 -0
  222. praisonai_code/cli/features/sandbox_cli.py +283 -0
  223. praisonai_code/cli/features/sandbox_executor.py +536 -0
  224. praisonai_code/cli/features/sdk_knowledge.py +500 -0
  225. praisonai_code/cli/features/session.py +222 -0
  226. praisonai_code/cli/features/session_checkpoints.py +208 -0
  227. praisonai_code/cli/features/setup/__init__.py +9 -0
  228. praisonai_code/cli/features/setup/handler.py +355 -0
  229. praisonai_code/cli/features/setup/templates.py +62 -0
  230. praisonai_code/cli/features/skills.py +940 -0
  231. praisonai_code/cli/features/slash_commands.py +692 -0
  232. praisonai_code/cli/features/telemetry.py +179 -0
  233. praisonai_code/cli/features/templates.py +1390 -0
  234. praisonai_code/cli/features/thinking.py +343 -0
  235. praisonai_code/cli/features/todo.py +334 -0
  236. praisonai_code/cli/features/tools.py +680 -0
  237. praisonai_code/cli/features/tui/__init__.py +83 -0
  238. praisonai_code/cli/features/tui/app.py +871 -0
  239. praisonai_code/cli/features/tui/cli.py +580 -0
  240. praisonai_code/cli/features/tui/config.py +150 -0
  241. praisonai_code/cli/features/tui/debug.py +526 -0
  242. praisonai_code/cli/features/tui/events.py +99 -0
  243. praisonai_code/cli/features/tui/mock_provider.py +328 -0
  244. praisonai_code/cli/features/tui/orchestrator.py +652 -0
  245. praisonai_code/cli/features/tui/screens/__init__.py +50 -0
  246. praisonai_code/cli/features/tui/screens/help.py +157 -0
  247. praisonai_code/cli/features/tui/screens/main.py +568 -0
  248. praisonai_code/cli/features/tui/screens/queue.py +174 -0
  249. praisonai_code/cli/features/tui/screens/session.py +124 -0
  250. praisonai_code/cli/features/tui/screens/settings.py +148 -0
  251. praisonai_code/cli/features/tui/session_store.py +198 -0
  252. praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
  253. praisonai_code/cli/features/tui/widgets/chat.py +263 -0
  254. praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
  255. praisonai_code/cli/features/tui/widgets/composer.py +292 -0
  256. praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
  257. praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
  258. praisonai_code/cli/features/tui/widgets/status.py +181 -0
  259. praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
  260. praisonai_code/cli/features/wizard.py +289 -0
  261. praisonai_code/cli/features/workflow.py +802 -0
  262. praisonai_code/cli/features/yaml_utils.py +321 -0
  263. praisonai_code/cli/interactive/__init__.py +48 -0
  264. praisonai_code/cli/interactive/async_tui.py +1218 -0
  265. praisonai_code/cli/interactive/config.py +139 -0
  266. praisonai_code/cli/interactive/core.py +618 -0
  267. praisonai_code/cli/interactive/events.py +131 -0
  268. praisonai_code/cli/interactive/frontends/__init__.py +31 -0
  269. praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
  270. praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
  271. praisonai_code/cli/interactive/praison_io.py +502 -0
  272. praisonai_code/cli/interactive/repl.py +297 -0
  273. praisonai_code/cli/interactive/split_tui.py +456 -0
  274. praisonai_code/cli/interactive/tui_app.py +457 -0
  275. praisonai_code/cli/langfuse_client.py +360 -0
  276. praisonai_code/cli/main.py +7421 -0
  277. praisonai_code/cli/output/__init__.py +25 -0
  278. praisonai_code/cli/output/console.py +456 -0
  279. praisonai_code/cli/output/event_bridge.py +191 -0
  280. praisonai_code/cli/schedule_cli.py +54 -0
  281. praisonai_code/cli/schema_provider.py +23 -0
  282. praisonai_code/cli/session/__init__.py +16 -0
  283. praisonai_code/cli/session/resume.py +148 -0
  284. praisonai_code/cli/session/unified.py +548 -0
  285. praisonai_code/cli/state/__init__.py +31 -0
  286. praisonai_code/cli/state/identifiers.py +161 -0
  287. praisonai_code/cli/state/project_sessions.py +383 -0
  288. praisonai_code/cli/state/sessions.py +390 -0
  289. praisonai_code/cli/ui/__init__.py +160 -0
  290. praisonai_code/cli/ui/config.py +46 -0
  291. praisonai_code/cli/ui/events.py +61 -0
  292. praisonai_code/cli/ui/mg_backend.py +342 -0
  293. praisonai_code/cli/ui/plain.py +133 -0
  294. praisonai_code/cli/ui/rich_backend.py +162 -0
  295. praisonai_code/cli/unified_schema.py +655 -0
  296. praisonai_code/cli/utils/env_utils.py +126 -0
  297. praisonai_code/cli/utils/project.py +131 -0
  298. praisonai_code/cli_backends/__init__.py +73 -0
  299. praisonai_code/cli_backends/claude.py +373 -0
  300. praisonai_code/cli_backends/registry.py +113 -0
  301. praisonai_code/runtime/__init__.py +36 -0
  302. praisonai_code/runtime/__main__.py +81 -0
  303. praisonai_code/runtime/client.py +131 -0
  304. praisonai_code/runtime/descriptor.py +209 -0
  305. praisonai_code/runtime/server.py +356 -0
  306. praisonai_code-0.0.1.dist-info/METADATA +80 -0
  307. praisonai_code-0.0.1.dist-info/RECORD +309 -0
  308. praisonai_code-0.0.1.dist-info/WHEEL +5 -0
  309. praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,852 @@
1
+ """
2
+ Tracker command for autonomous agent execution with step-by-step tracking.
3
+
4
+ Uses the centralized autonomy system with:
5
+ - All default autonomy tools (search, file, shell, code, etc.)
6
+ - Auto-approval enabled by default (full_auto mode)
7
+ - Real-time step tracking and status output
8
+ - Summary table at completion
9
+ - Gap analysis for debugging
10
+
11
+ The tracker integrates with the Agent's autonomy system to provide
12
+ comprehensive tracking of autonomous agent execution.
13
+ """
14
+
15
+ from typing import Optional, List
16
+ from dataclasses import dataclass, field
17
+ from datetime import datetime
18
+ import os
19
+ import time
20
+
21
+ import typer
22
+ from rich.console import Console
23
+ from rich.table import Table
24
+ from rich.panel import Panel
25
+
26
+ console = Console()
27
+
28
+ app = typer.Typer(
29
+ name="tracker",
30
+ help="Run agents in autonomous mode with step-by-step tracking",
31
+ no_args_is_help=False,
32
+ )
33
+
34
+
35
+ @dataclass
36
+ class TrackedStep:
37
+ """A single tracked step in the autonomous execution."""
38
+ step_number: int
39
+ timestamp: str
40
+ action_type: str # chat, tool_call, completion, error
41
+ action_name: str # tool name or "thinking"
42
+ input_summary: str
43
+ output_summary: str
44
+ duration_seconds: float
45
+ success: bool
46
+ error: Optional[str] = None
47
+
48
+
49
+ @dataclass
50
+ class TrackerResult:
51
+ """Result of a tracked autonomous execution."""
52
+ task: str
53
+ success: bool
54
+ completion_reason: str
55
+ total_steps: int
56
+ total_duration: float
57
+ steps: List[TrackedStep] = field(default_factory=list)
58
+ tools_used: List[str] = field(default_factory=list)
59
+ gaps_identified: List[str] = field(default_factory=list)
60
+
61
+
62
+ # ============================================================================
63
+ # CENTRALIZED AUTONOMY DEFAULT TOOLS
64
+ # These are the default tools available in autonomy mode (no API keys required)
65
+ # ============================================================================
66
+ AUTONOMY_DEFAULT_TOOLS = [
67
+ # Web Search & Crawl
68
+ "search_web", # Unified web search (DuckDuckGo fallback)
69
+ "internet_search", # DuckDuckGo search
70
+ "web_crawl", # Web page crawling
71
+ "scrape_page", # Page scraping
72
+ "extract_text", # Text extraction
73
+ "extract_links", # Link extraction
74
+
75
+ # File Operations
76
+ "read_file", # Read file contents
77
+ "write_file", # Write to files
78
+ "list_files", # List directory contents
79
+ "copy_file", # Copy files
80
+ "move_file", # Move files
81
+ "delete_file", # Delete files
82
+ "get_file_info", # Get file metadata
83
+
84
+ # Shell & System
85
+ "execute_command", # Execute shell commands
86
+ "list_processes", # List running processes
87
+ "get_system_info", # Get system information
88
+
89
+ # Python Code Execution
90
+ "execute_code", # Execute Python code
91
+ "analyze_code", # Analyze code structure
92
+ "format_code", # Format Python code
93
+ "lint_code", # Lint Python code
94
+
95
+ # ACP Tools (Agent-Centric Protocol)
96
+ "acp_create_file", # Create files (with approval tracking)
97
+ "acp_edit_file", # Edit files (with approval tracking)
98
+ "acp_delete_file", # Delete files (with approval tracking)
99
+ "acp_execute_command", # Execute commands (with approval tracking)
100
+
101
+ # LSP Tools (Language Server Protocol)
102
+ "lsp_list_symbols", # List symbols in a file
103
+ "lsp_find_definition", # Go to definition
104
+ "lsp_find_references", # Find all references
105
+ "lsp_get_diagnostics", # Get code diagnostics/errors
106
+
107
+ # Scheduling
108
+ "schedule_add", # Add scheduled task
109
+ "schedule_list", # List scheduled tasks
110
+ "schedule_remove", # Remove scheduled task
111
+ ]
112
+
113
+ # Extended tools (may require API keys)
114
+ EXTENDED_TOOLS = [
115
+ "tavily_search", # Tavily search (requires TAVILY_API_KEY)
116
+ "tavily_extract", # Tavily extract
117
+ "exa_search", # Exa search (requires EXA_API_KEY)
118
+ "exa_answer", # Exa answer
119
+ "crawl4ai", # Advanced crawling (requires playwright)
120
+ "ydc_search", # You.com search (requires YDC_API_KEY)
121
+ ]
122
+
123
+
124
+ def _get_tools(tool_names: List[str]) -> List:
125
+ """Resolve tool names to actual tool functions."""
126
+ tools = []
127
+ resolved_names = set()
128
+ try:
129
+ from praisonaiagents import tools as tool_module
130
+ for name in tool_names:
131
+ try:
132
+ tool_func = getattr(tool_module, name)
133
+ # Check if it's a module (e.g., web_crawl returns module)
134
+ # In that case, get the function with the same name from the module
135
+ if hasattr(tool_func, '__file__') and hasattr(tool_func, name):
136
+ tool_func = getattr(tool_func, name)
137
+ tools.append(tool_func)
138
+ resolved_names.add(name)
139
+ except AttributeError:
140
+ pass # Try interactive tools below
141
+ except ImportError:
142
+ console.print("[red]Error: praisonaiagents not installed[/red]")
143
+
144
+ # Resolve ACP/LSP tools via interactive_tools (they need runtime initialization)
145
+ unresolved = [n for n in tool_names if n not in resolved_names]
146
+ if any(n.startswith(("acp_", "lsp_")) for n in unresolved):
147
+ try:
148
+ from praisonai.cli.features.interactive_tools import get_interactive_tools, ToolConfig
149
+ config = ToolConfig.from_env()
150
+ config.workspace = os.getcwd()
151
+ interactive = get_interactive_tools(config=config)
152
+ interactive_map = {t.__name__: t for t in interactive}
153
+ for name in unresolved:
154
+ if name in interactive_map:
155
+ tools.append(interactive_map[name])
156
+ resolved_names.add(name)
157
+ except ImportError:
158
+ pass # Interactive tools not available
159
+ except Exception as e:
160
+ console.print(f"[dim]Note: ACP/LSP tools not loaded: {e}[/dim]")
161
+
162
+ return tools
163
+
164
+
165
+ def _summarize_text(text: str, max_len: int = 50) -> str:
166
+ """Summarize text to max length."""
167
+ if not text:
168
+ return ""
169
+ text = str(text).replace("\n", " ").strip()
170
+ if len(text) > max_len:
171
+ return text[:max_len-3] + "..."
172
+ return text
173
+
174
+
175
+ def _run_tracked_task(
176
+ task: str,
177
+ tools: List,
178
+ max_iterations: int,
179
+ model: Optional[str],
180
+ verbose: bool,
181
+ step_callback: Optional[callable] = None,
182
+ auto_approve: bool = True,
183
+ ) -> TrackerResult:
184
+ """Run a task with step tracking using centralized autonomy system.
185
+
186
+ Args:
187
+ task: The task to execute
188
+ tools: List of tool functions to use
189
+ max_iterations: Maximum autonomous iterations
190
+ model: LLM model to use (optional)
191
+ verbose: Show verbose output
192
+ step_callback: Callback for each step (optional)
193
+ auto_approve: Auto-approve all tool calls (default: True)
194
+ """
195
+ from praisonaiagents import Agent
196
+
197
+ # ========================================================================
198
+ # AUTO-APPROVE: Set env var so @require_approval decorators on tool
199
+ # functions (execute_code, execute_command, write_file, etc.) auto-approve.
200
+ # The decorator checks PRAISONAI_AUTO_APPROVE env var independently of
201
+ # the Agent's approval= parameter.
202
+ # ========================================================================
203
+ prev_auto_approve = os.environ.get("PRAISONAI_AUTO_APPROVE")
204
+ if auto_approve:
205
+ os.environ["PRAISONAI_AUTO_APPROVE"] = "true"
206
+
207
+ steps: List[TrackedStep] = []
208
+ tools_used: set = set()
209
+ start_time = time.time()
210
+ step_number = 0
211
+
212
+ # ========================================================================
213
+ # CENTRALIZED AUTONOMY CONFIGURATION
214
+ # Uses full_auto level with auto-approval for autonomous tracking
215
+ # ========================================================================
216
+ agent_kwargs = {
217
+ "name": "tracker_agent",
218
+ "instructions": """You are an autonomous agent with access to various tools.
219
+ You MUST use tools to complete tasks - do NOT rely on your training knowledge alone.
220
+
221
+ TOOL USAGE RULES:
222
+ - For research/search tasks: use search_web or internet_search
223
+ - For file operations: use read_file, write_file, list_files
224
+ - For safe file creation/editing: use acp_create_file, acp_edit_file
225
+ - For code tasks: use execute_code, analyze_code
226
+ - For shell commands: use execute_command or acp_execute_command
227
+ - For web scraping: use web_crawl, scrape_page, extract_text
228
+ - For code intelligence: use lsp_list_symbols, lsp_find_definition, lsp_find_references, lsp_get_diagnostics
229
+
230
+ When you have fully completed the task, say 'Task completed' or 'Done'.""",
231
+ "tools": tools,
232
+ # Centralized autonomy configuration
233
+ "autonomy": {
234
+ "enabled": True,
235
+ "level": "full_auto", # Full autonomous mode
236
+ "max_iterations": max_iterations,
237
+ "doom_loop_threshold": 5,
238
+ "auto_escalate": True,
239
+ },
240
+ # Auto-approve all tools for tracker (agent-centric approval)
241
+ "approval": auto_approve,
242
+ }
243
+
244
+ if model:
245
+ agent_kwargs["llm"] = model
246
+
247
+ if verbose:
248
+ pass # Use default full output
249
+ else:
250
+ agent_kwargs["output"] = "status" # Real-time status like Agent(output="status")
251
+
252
+ agent = Agent(**agent_kwargs)
253
+
254
+ # Wrap execute_tool to track tool calls
255
+ original_execute_tool = agent.execute_tool
256
+
257
+ def tracked_execute_tool(tool_name, *args, **kwargs):
258
+ nonlocal step_number
259
+ step_number += 1
260
+ step_start = time.time()
261
+ tools_used.add(tool_name)
262
+
263
+ try:
264
+ result = original_execute_tool(tool_name, *args, **kwargs)
265
+ duration = time.time() - step_start
266
+
267
+ step = TrackedStep(
268
+ step_number=step_number,
269
+ timestamp=datetime.now().isoformat(),
270
+ action_type="tool_call",
271
+ action_name=tool_name,
272
+ input_summary=_summarize_text(str(args) + str(kwargs)),
273
+ output_summary=_summarize_text(str(result)),
274
+ duration_seconds=duration,
275
+ success=True,
276
+ )
277
+ steps.append(step)
278
+ if step_callback:
279
+ step_callback(step)
280
+ return result
281
+ except Exception as e:
282
+ duration = time.time() - step_start
283
+ step = TrackedStep(
284
+ step_number=step_number,
285
+ timestamp=datetime.now().isoformat(),
286
+ action_type="tool_error",
287
+ action_name=tool_name,
288
+ input_summary=_summarize_text(str(args) + str(kwargs)),
289
+ output_summary="",
290
+ duration_seconds=duration,
291
+ success=False,
292
+ error=str(e),
293
+ )
294
+ steps.append(step)
295
+ if step_callback:
296
+ step_callback(step)
297
+ raise
298
+
299
+ agent.execute_tool = tracked_execute_tool
300
+
301
+ # Track chat calls
302
+ original_chat = agent.chat
303
+
304
+ def tracked_chat(prompt, *args, **kwargs):
305
+ nonlocal step_number
306
+ step_number += 1
307
+ step_start = time.time()
308
+
309
+ try:
310
+ result = original_chat(prompt, *args, **kwargs)
311
+ duration = time.time() - step_start
312
+
313
+ # Record step
314
+ step = TrackedStep(
315
+ step_number=step_number,
316
+ timestamp=datetime.now().isoformat(),
317
+ action_type="chat",
318
+ action_name="thinking",
319
+ input_summary=_summarize_text(prompt),
320
+ output_summary=_summarize_text(str(result)),
321
+ duration_seconds=duration,
322
+ success=True,
323
+ )
324
+ steps.append(step)
325
+
326
+ if step_callback:
327
+ step_callback(step)
328
+
329
+ return result
330
+ except Exception as e:
331
+ duration = time.time() - step_start
332
+ step = TrackedStep(
333
+ step_number=step_number,
334
+ timestamp=datetime.now().isoformat(),
335
+ action_type="error",
336
+ action_name="chat_error",
337
+ input_summary=_summarize_text(prompt),
338
+ output_summary="",
339
+ duration_seconds=duration,
340
+ success=False,
341
+ error=str(e),
342
+ )
343
+ steps.append(step)
344
+ if step_callback:
345
+ step_callback(step)
346
+ raise
347
+
348
+ agent.chat = tracked_chat
349
+
350
+ # Run autonomous loop
351
+ try:
352
+ result = agent.run_autonomous(
353
+ prompt=task,
354
+ max_iterations=max_iterations,
355
+ )
356
+
357
+ total_duration = time.time() - start_time
358
+
359
+ # Extract tool usage from AutonomyResult.actions
360
+ if hasattr(result, 'actions') and result.actions:
361
+ for action in result.actions:
362
+ if isinstance(action, dict):
363
+ action_type = action.get('type', 'unknown')
364
+ action_name = action.get('name', action.get('tool', 'unknown'))
365
+ if action_type == 'tool_call' or 'tool' in action:
366
+ tools_used.add(action_name)
367
+
368
+ # Analyze gaps
369
+ gaps = []
370
+ if result.iterations >= max_iterations:
371
+ gaps.append("Hit max iterations - task may be incomplete")
372
+ if result.completion_reason == "doom_loop":
373
+ gaps.append("Doom loop detected - agent repeated same actions")
374
+ if result.completion_reason == "needs_help":
375
+ gaps.append("Agent requested human help")
376
+ if not tools_used and len(tools) > 0:
377
+ gaps.append("No tools were used despite being available")
378
+
379
+ return TrackerResult(
380
+ task=task,
381
+ success=result.success,
382
+ completion_reason=result.completion_reason,
383
+ total_steps=len(steps) if steps else result.iterations,
384
+ total_duration=total_duration,
385
+ steps=steps,
386
+ tools_used=list(tools_used),
387
+ gaps_identified=gaps,
388
+ )
389
+
390
+ except Exception as e:
391
+ total_duration = time.time() - start_time
392
+ return TrackerResult(
393
+ task=task,
394
+ success=False,
395
+ completion_reason="error",
396
+ total_steps=len(steps),
397
+ total_duration=total_duration,
398
+ steps=steps,
399
+ tools_used=list(tools_used),
400
+ gaps_identified=[f"Error: {str(e)}"],
401
+ )
402
+ finally:
403
+ # Stop the LSP/ACP runtime if it was started
404
+ try:
405
+ from praisonai.cli.features.interactive_tools import cleanup_runtime
406
+ cleanup_runtime()
407
+ except Exception:
408
+ pass
409
+
410
+ # Restore original PRAISONAI_AUTO_APPROVE env var
411
+ if prev_auto_approve is None:
412
+ os.environ.pop("PRAISONAI_AUTO_APPROVE", None)
413
+ else:
414
+ os.environ["PRAISONAI_AUTO_APPROVE"] = prev_auto_approve
415
+
416
+
417
+ def _print_step_table(steps: List[TrackedStep]) -> None:
418
+ """Print a table of all steps."""
419
+ table = Table(title="📊 Execution Steps", show_header=True, header_style="bold cyan")
420
+ table.add_column("#", style="dim", width=4)
421
+ table.add_column("Type", width=10)
422
+ table.add_column("Action", width=15)
423
+ table.add_column("Input", width=30)
424
+ table.add_column("Output", width=30)
425
+ table.add_column("Time", width=8)
426
+ table.add_column("Status", width=8)
427
+
428
+ for step in steps:
429
+ status = "✅" if step.success else "❌"
430
+ table.add_row(
431
+ str(step.step_number),
432
+ step.action_type,
433
+ step.action_name,
434
+ step.input_summary,
435
+ step.output_summary,
436
+ f"{step.duration_seconds:.2f}s",
437
+ status,
438
+ )
439
+
440
+ console.print(table)
441
+
442
+
443
+ def _print_summary(result: TrackerResult) -> None:
444
+ """Print execution summary."""
445
+ status_icon = "✅" if result.success else "❌"
446
+
447
+ console.print(Panel(
448
+ f"""
449
+ [bold]{status_icon} Task: {_summarize_text(result.task, 60)}[/bold]
450
+
451
+ [cyan]Completion Reason:[/cyan] {result.completion_reason}
452
+ [cyan]Total Steps:[/cyan] {result.total_steps}
453
+ [cyan]Total Duration:[/cyan] {result.total_duration:.2f}s
454
+ [cyan]Tools Used:[/cyan] {', '.join(result.tools_used) if result.tools_used else 'None'}
455
+ """,
456
+ title="📋 Execution Summary",
457
+ border_style="green" if result.success else "red",
458
+ ))
459
+
460
+ if result.gaps_identified:
461
+ console.print("\n[bold yellow]⚠️ Gaps Identified:[/bold yellow]")
462
+ for gap in result.gaps_identified:
463
+ console.print(f" • {gap}")
464
+
465
+
466
+ @app.callback()
467
+ def tracker_main(ctx: typer.Context):
468
+ """Autonomous agent tracking with step-by-step analysis.
469
+
470
+ Run tasks with full execution tracing and optional quality judging.
471
+
472
+ Commands:
473
+
474
+ praisonai tracker run "Search for Python best practices"
475
+
476
+ praisonai tracker judge "What is 2+2?" --expected "4"
477
+
478
+ praisonai tracker tools
479
+
480
+ praisonai tracker batch tasks.json
481
+ """
482
+ pass
483
+
484
+
485
+ @app.command(name="run")
486
+ def tracker_run(
487
+ task: str = typer.Argument(..., help="Task for the agent to complete"),
488
+ max_iterations: int = typer.Option(20, "--max-iterations", "-n", help="Maximum iterations (default: 20)"),
489
+ model: Optional[str] = typer.Option(None, "--model", "-m", help="LLM model to use"),
490
+ tools: Optional[str] = typer.Option(None, "--tools", "-t", help="Comma-separated tool names to use"),
491
+ extended: bool = typer.Option(False, "--extended", "-e", help="Include extended tools (may require API keys)"),
492
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Show verbose output"),
493
+ live: bool = typer.Option(True, "--live/--no-live", help="Show live step updates (default: on)"),
494
+ ):
495
+ """Run an agent with step-by-step tracking.
496
+
497
+ Examples:
498
+
499
+ praisonai tracker run "Search for Python best practices and summarize"
500
+
501
+ praisonai tracker run "Read config.yaml and explain its structure" -v
502
+
503
+ praisonai tracker run "Find trending AI news" --tools search_web,web_crawl
504
+ """
505
+ _run_and_display(task, max_iterations, model, tools, extended, verbose, live)
506
+
507
+
508
+ def _run_and_display(
509
+ task: str,
510
+ max_iterations: int = 20,
511
+ model: Optional[str] = None,
512
+ tools: Optional[str] = None,
513
+ extended: bool = False,
514
+ verbose: bool = False,
515
+ live: bool = True,
516
+ ) -> TrackerResult:
517
+ """Shared logic: run a tracked task and display results."""
518
+ # Resolve tools
519
+ tool_names = AUTONOMY_DEFAULT_TOOLS.copy()
520
+ if extended:
521
+ tool_names.extend(EXTENDED_TOOLS)
522
+ if tools:
523
+ tool_names = [t.strip() for t in tools.split(",")]
524
+
525
+ resolved_tools = _get_tools(tool_names)
526
+
527
+ console.print(f"\n[bold cyan]🔍 Agent Tracker[/bold cyan]")
528
+ console.print(f"[dim]Task: {_summarize_text(task, 70)}[/dim]")
529
+ console.print(f"[dim]Tools: {len(resolved_tools)} loaded ({', '.join(tool_names[:5])}{'...' if len(tool_names) > 5 else ''})[/dim]")
530
+ console.print(f"[dim]Max iterations: {max_iterations}[/dim]\n")
531
+
532
+ # Step callback for live updates
533
+ def step_callback(step: TrackedStep):
534
+ if live:
535
+ status = "✅" if step.success else "❌"
536
+ console.print(f" [{step.step_number}] {status} {step.action_type}: {step.action_name} ({step.duration_seconds:.2f}s)")
537
+
538
+ # Run tracked task
539
+ with console.status("[bold green]Running autonomous agent...[/bold green]") if not live else console:
540
+ result = _run_tracked_task(
541
+ task=task,
542
+ tools=resolved_tools,
543
+ max_iterations=max_iterations,
544
+ model=model,
545
+ verbose=verbose,
546
+ step_callback=step_callback if live else None,
547
+ )
548
+
549
+ # Print results
550
+ console.print("\n")
551
+ _print_step_table(result.steps)
552
+ console.print("\n")
553
+ _print_summary(result)
554
+
555
+ return result
556
+
557
+
558
+ @app.command(name="batch")
559
+ def tracker_batch(
560
+ tasks_file: str = typer.Argument(..., help="JSON file with list of tasks"),
561
+ max_iterations: int = typer.Option(20, "--max-iterations", "-n", help="Maximum iterations per task"),
562
+ model: Optional[str] = typer.Option(None, "--model", "-m", help="LLM model to use"),
563
+ output: Optional[str] = typer.Option(None, "--output", "-o", help="Output JSON file for results"),
564
+ ):
565
+ """Run multiple tasks and generate a comparison table.
566
+
567
+ The tasks file should be a JSON array of task strings:
568
+
569
+ ["Task 1", "Task 2", "Task 3"]
570
+
571
+ Example:
572
+
573
+ praisonai tracker batch tasks.json -o results.json
574
+ """
575
+ import json
576
+
577
+ try:
578
+ with open(tasks_file) as f:
579
+ tasks = json.load(f)
580
+ except Exception as e:
581
+ console.print(f"[red]Error reading tasks file: {e}[/red]")
582
+ raise typer.Exit(1)
583
+
584
+ if not isinstance(tasks, list):
585
+ console.print("[red]Tasks file must contain a JSON array of strings[/red]")
586
+ raise typer.Exit(1)
587
+
588
+ console.print(f"\n[bold cyan]🔍 Batch Agent Tracker[/bold cyan]")
589
+ console.print(f"[dim]Running {len(tasks)} tasks...[/dim]\n")
590
+
591
+ resolved_tools = _get_tools(AUTONOMY_DEFAULT_TOOLS)
592
+ results: List[TrackerResult] = []
593
+
594
+ for i, task in enumerate(tasks, 1):
595
+ console.print(f"\n[bold]Task {i}/{len(tasks)}:[/bold] {_summarize_text(task, 50)}")
596
+
597
+ result = _run_tracked_task(
598
+ task=task,
599
+ tools=resolved_tools,
600
+ max_iterations=max_iterations,
601
+ model=model,
602
+ verbose=False,
603
+ step_callback=None,
604
+ )
605
+ results.append(result)
606
+
607
+ status = "✅" if result.success else "❌"
608
+ console.print(f" {status} {result.completion_reason} ({result.total_steps} steps, {result.total_duration:.2f}s)")
609
+
610
+ # Print summary table
611
+ console.print("\n")
612
+ table = Table(title="📊 Batch Results Summary", show_header=True, header_style="bold cyan")
613
+ table.add_column("#", style="dim", width=4)
614
+ table.add_column("Task", width=40)
615
+ table.add_column("Status", width=8)
616
+ table.add_column("Reason", width=15)
617
+ table.add_column("Steps", width=6)
618
+ table.add_column("Duration", width=10)
619
+ table.add_column("Gaps", width=20)
620
+
621
+ for i, result in enumerate(results, 1):
622
+ status = "✅" if result.success else "❌"
623
+ gaps = ", ".join(result.gaps_identified[:2]) if result.gaps_identified else "-"
624
+ table.add_row(
625
+ str(i),
626
+ _summarize_text(result.task, 40),
627
+ status,
628
+ result.completion_reason,
629
+ str(result.total_steps),
630
+ f"{result.total_duration:.2f}s",
631
+ _summarize_text(gaps, 20),
632
+ )
633
+
634
+ console.print(table)
635
+
636
+ # Calculate stats
637
+ success_count = sum(1 for r in results if r.success)
638
+ total_steps = sum(r.total_steps for r in results)
639
+ total_duration = sum(r.total_duration for r in results)
640
+
641
+ console.print(f"\n[bold]Summary:[/bold]")
642
+ console.print(f" Success rate: {success_count}/{len(results)} ({100*success_count/len(results):.1f}%)")
643
+ console.print(f" Total steps: {total_steps}")
644
+ console.print(f" Total duration: {total_duration:.2f}s")
645
+
646
+ # Save results if output specified
647
+ if output:
648
+ output_data = {
649
+ "tasks": len(results),
650
+ "success_rate": success_count / len(results),
651
+ "total_steps": total_steps,
652
+ "total_duration": total_duration,
653
+ "results": [
654
+ {
655
+ "task": r.task,
656
+ "success": r.success,
657
+ "completion_reason": r.completion_reason,
658
+ "steps": r.total_steps,
659
+ "duration": r.total_duration,
660
+ "gaps": r.gaps_identified,
661
+ }
662
+ for r in results
663
+ ],
664
+ }
665
+ with open(output, "w") as f:
666
+ json.dump(output_data, f, indent=2)
667
+ console.print(f"\n[green]Results saved to {output}[/green]")
668
+
669
+
670
+ @app.command(name="tools")
671
+ def tracker_tools():
672
+ """List all available tools for the tracker."""
673
+ console.print("\n[bold cyan]🛠️ Available Tools[/bold cyan]\n")
674
+
675
+ console.print("[bold]Default Tools (no API keys required):[/bold]")
676
+ for tool in AUTONOMY_DEFAULT_TOOLS:
677
+ console.print(f" • {tool}")
678
+
679
+ console.print("\n[bold]Extended Tools (may require API keys):[/bold]")
680
+ for tool in EXTENDED_TOOLS:
681
+ console.print(f" • {tool}")
682
+
683
+ console.print("\n[dim]Use --tools to specify custom tools, e.g.:[/dim]")
684
+ console.print("[dim] praisonai tracker 'task' --tools search_web,read_file[/dim]")
685
+
686
+
687
+ # ============================================================================
688
+ # JUDGE FEATURE
689
+ # ============================================================================
690
+
691
+ DEFAULT_JUDGE_CRITERIA = """Evaluate this autonomous agent execution trace:
692
+ 1. Task Completion: Did the agent fully complete the assigned task?
693
+ 2. Tool Selection: Were appropriate tools chosen for each step?
694
+ 3. Efficiency: Was the task completed with minimal unnecessary steps?
695
+ 4. Error Handling: Were errors handled gracefully without crashing?
696
+ 5. Output Quality: Is the final output accurate and useful?"""
697
+
698
+
699
+ def _format_trace_for_judge(result: TrackerResult) -> str:
700
+ """Convert TrackerResult into a structured string for LLM evaluation."""
701
+ lines = []
702
+ lines.append(f"Task: \"{result.task}\"")
703
+ status = "success" if result.success else "failed"
704
+ lines.append(f"Completion: {status} (reason: {result.completion_reason})")
705
+ lines.append(f"Duration: {result.total_duration:.1f}s | Steps: {result.total_steps} | Tools Used: {', '.join(result.tools_used) if result.tools_used else 'None'}")
706
+ lines.append("")
707
+
708
+ for step in result.steps:
709
+ icon = "✅" if step.success else "❌"
710
+ lines.append(f"Step {step.step_number}: [{step.action_type}] {step.action_name} ({step.duration_seconds:.1f}s) {icon}")
711
+ lines.append(f" Input: {step.input_summary}")
712
+ lines.append(f" Output: {step.output_summary}")
713
+ if step.error:
714
+ lines.append(f" Error: {step.error}")
715
+ lines.append("")
716
+
717
+ if result.gaps_identified:
718
+ lines.append(f"Gaps: {'; '.join(result.gaps_identified)}")
719
+ else:
720
+ lines.append("Gaps: None")
721
+
722
+ return "\n".join(lines)
723
+
724
+
725
+ def _print_judge_verdict(judge_result, threshold: float) -> None:
726
+ """Print the judge verdict with rich formatting."""
727
+ score = getattr(judge_result, 'score', 0) or 0
728
+ passed = score >= threshold
729
+ reasoning = getattr(judge_result, 'reasoning', '') or ''
730
+ suggestions = getattr(judge_result, 'suggestions', []) or []
731
+
732
+ # Score bar
733
+ bar_len = 20
734
+ filled = int(score / 10 * bar_len)
735
+ bar = "█" * filled + "░" * (bar_len - filled)
736
+
737
+ color = "green" if passed else ("yellow" if score >= 5 else "red")
738
+ icon = "✅" if passed else "❌"
739
+
740
+ console.print(Panel(
741
+ f"""
742
+ [bold]{icon} Score: [{color}]{score:.1f}/10[/{color}][/bold] [{color}]{bar}[/{color}]
743
+ [bold]Threshold:[/bold] {threshold} | [bold]Verdict:[/bold] [{'green' if passed else 'red'}]{'PASS' if passed else 'FAIL'}[/{'green' if passed else 'red'}]
744
+
745
+ [bold]Reasoning:[/bold]
746
+ {reasoning}
747
+ """,
748
+ title="⚖️ Judge Verdict",
749
+ border_style=color,
750
+ ))
751
+
752
+ if suggestions:
753
+ console.print("[bold yellow]💡 Suggestions:[/bold yellow]")
754
+ for s in suggestions:
755
+ console.print(f" • {s}")
756
+
757
+
758
+ @app.command(name="judge")
759
+ def tracker_judge(
760
+ task: str = typer.Argument(..., help="Task to execute and judge"),
761
+ criteria: Optional[str] = typer.Option(None, "--criteria", "-c", help="Custom evaluation criteria"),
762
+ expected: Optional[str] = typer.Option(None, "--expected", "-e", help="Expected output for accuracy evaluation"),
763
+ threshold: float = typer.Option(7.0, "--threshold", help="Pass/fail score threshold (1-10)"),
764
+ max_iterations: int = typer.Option(20, "--max-iterations", "-n", help="Maximum iterations (default: 20)"),
765
+ model: Optional[str] = typer.Option(None, "--model", "-m", help="LLM model to use"),
766
+ judge_model: Optional[str] = typer.Option(None, "--judge-model", help="LLM model for judge (default: same as agent)"),
767
+ tools: Optional[str] = typer.Option(None, "--tools", "-t", help="Comma-separated tool names to use"),
768
+ extended: bool = typer.Option(False, "--extended", help="Include extended tools (may require API keys)"),
769
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Show verbose output"),
770
+ ):
771
+ """Run a task and judge the execution quality.
772
+
773
+ Executes the task with step tracking, then evaluates the execution
774
+ trace using an LLM judge. Reports a score (1-10), pass/fail verdict,
775
+ reasoning, and improvement suggestions.
776
+
777
+ Examples:
778
+
779
+ praisonai tracker judge "Calculate fibonacci(10) using execute_code"
780
+
781
+ praisonai tracker judge "Search for AI news" --criteria "Must use search_web"
782
+
783
+ praisonai tracker judge "What is 2+2?" --expected "4" --threshold 8.0
784
+ """
785
+ # Resolve tools
786
+ tool_names = AUTONOMY_DEFAULT_TOOLS.copy()
787
+ if extended:
788
+ tool_names.extend(EXTENDED_TOOLS)
789
+ if tools:
790
+ tool_names = [t.strip() for t in tools.split(",")]
791
+
792
+ resolved_tools = _get_tools(tool_names)
793
+
794
+ console.print(f"\n[bold cyan]⚖️ Agent Tracker + Judge[/bold cyan]")
795
+ console.print(f"[dim]Task: {_summarize_text(task, 70)}[/dim]")
796
+ console.print(f"[dim]Tools: {len(resolved_tools)} loaded | Threshold: {threshold}[/dim]\n")
797
+
798
+ # Step 1: Run the task
799
+ console.print("[bold]Phase 1: Executing task...[/bold]\n")
800
+
801
+ def step_callback(step: TrackedStep):
802
+ status = "✅" if step.success else "❌"
803
+ console.print(f" [{step.step_number}] {status} {step.action_type}: {step.action_name} ({step.duration_seconds:.2f}s)")
804
+
805
+ result = _run_tracked_task(
806
+ task=task,
807
+ tools=resolved_tools,
808
+ max_iterations=max_iterations,
809
+ model=model,
810
+ verbose=verbose,
811
+ step_callback=step_callback,
812
+ )
813
+
814
+ # Print step table + summary
815
+ console.print("\n")
816
+ _print_step_table(result.steps)
817
+ console.print("\n")
818
+ _print_summary(result)
819
+
820
+ # Step 2: Judge the execution
821
+ console.print("\n[bold]Phase 2: Judging execution...[/bold]\n")
822
+
823
+ try:
824
+ from praisonaiagents.eval import Judge
825
+
826
+ trace_text = _format_trace_for_judge(result)
827
+
828
+ judge_kwargs = {}
829
+ if judge_model:
830
+ judge_kwargs["model"] = judge_model
831
+ elif model:
832
+ judge_kwargs["model"] = model
833
+
834
+ judge = Judge(threshold=threshold, **judge_kwargs)
835
+
836
+ eval_criteria = criteria or DEFAULT_JUDGE_CRITERIA
837
+
838
+ judge_result = judge.run(
839
+ output=trace_text,
840
+ criteria=eval_criteria,
841
+ expected=expected,
842
+ input=task,
843
+ )
844
+
845
+ _print_judge_verdict(judge_result, threshold)
846
+
847
+ except ImportError:
848
+ console.print("[red]Error: praisonaiagents.eval not available[/red]")
849
+ console.print("[dim]Install with: pip install praisonaiagents[/dim]")
850
+ except Exception as e:
851
+ console.print(f"[red]Judge error: {e}[/red]")
852
+