PraisonAI 3.0.0__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 (393) hide show
  1. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,810 @@
1
+ """
2
+ Debug CLI handler for PraisonAI.
3
+
4
+ Provides non-interactive debug commands for testing interactive flows:
5
+ - debug interactive: Run single interactive turn
6
+ - debug lsp: Direct LSP probes
7
+ - debug acp: Direct ACP probes
8
+ - debug trace: Trace record/replay/diff
9
+ """
10
+
11
+ import argparse
12
+ import asyncio
13
+ import json
14
+ import logging
15
+ import sys
16
+ import time
17
+ from typing import Any, Dict, List
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class DebugHandler:
23
+ """Handler for debug CLI commands."""
24
+
25
+ @staticmethod
26
+ def setup_subparser(subparsers) -> None:
27
+ """Set up the debug subcommand parser."""
28
+ debug_parser = subparsers.add_parser(
29
+ "debug",
30
+ help="Debug and test interactive flows",
31
+ description="Debug commands for testing interactive coding assistant flows"
32
+ )
33
+
34
+ debug_subparsers = debug_parser.add_subparsers(dest="debug_command", help="Debug subcommands")
35
+
36
+ # debug interactive
37
+ interactive_parser = debug_subparsers.add_parser(
38
+ "interactive",
39
+ help="Run single interactive turn non-interactively",
40
+ description="Execute one interactive turn with full LSP/ACP pipeline"
41
+ )
42
+ interactive_parser.add_argument(
43
+ "-p", "--prompt",
44
+ type=str,
45
+ required=True,
46
+ help="Prompt to execute"
47
+ )
48
+ interactive_parser.add_argument(
49
+ "--lsp",
50
+ action="store_true",
51
+ default=True,
52
+ help="Enable LSP (default: enabled)"
53
+ )
54
+ interactive_parser.add_argument(
55
+ "--no-lsp",
56
+ action="store_true",
57
+ help="Disable LSP"
58
+ )
59
+ interactive_parser.add_argument(
60
+ "--acp",
61
+ action="store_true",
62
+ default=True,
63
+ help="Enable ACP (default: enabled)"
64
+ )
65
+ interactive_parser.add_argument(
66
+ "--no-acp",
67
+ action="store_true",
68
+ help="Disable ACP"
69
+ )
70
+ interactive_parser.add_argument(
71
+ "--approval",
72
+ type=str,
73
+ choices=["manual", "auto", "scoped"],
74
+ default="auto",
75
+ help="Approval mode (default: auto for debug)"
76
+ )
77
+ interactive_parser.add_argument(
78
+ "--workspace", "-w",
79
+ type=str,
80
+ default=".",
81
+ help="Workspace root directory"
82
+ )
83
+ interactive_parser.add_argument(
84
+ "--json",
85
+ action="store_true",
86
+ help="Output JSON trace"
87
+ )
88
+ interactive_parser.add_argument(
89
+ "--trace-file",
90
+ type=str,
91
+ help="Save trace to file"
92
+ )
93
+ interactive_parser.add_argument(
94
+ "--timeout",
95
+ type=float,
96
+ default=60.0,
97
+ help="Timeout in seconds"
98
+ )
99
+ interactive_parser.add_argument(
100
+ "--model",
101
+ type=str,
102
+ help="LLM model to use"
103
+ )
104
+ interactive_parser.set_defaults(func=DebugHandler.handle_interactive)
105
+
106
+ # debug lsp
107
+ lsp_parser = debug_subparsers.add_parser(
108
+ "lsp",
109
+ help="Direct LSP probes",
110
+ description="Probe LSP server directly"
111
+ )
112
+ lsp_subparsers = lsp_parser.add_subparsers(dest="lsp_command", help="LSP subcommands")
113
+
114
+ # debug lsp status
115
+ lsp_status = lsp_subparsers.add_parser("status", help="Show LSP status")
116
+ lsp_status.add_argument("--workspace", "-w", type=str, default=".")
117
+ lsp_status.add_argument("--json", action="store_true")
118
+ lsp_status.set_defaults(func=DebugHandler.handle_lsp_status)
119
+
120
+ # debug lsp symbols
121
+ lsp_symbols = lsp_subparsers.add_parser("symbols", help="List symbols in file")
122
+ lsp_symbols.add_argument("file", type=str, help="File path")
123
+ lsp_symbols.add_argument("--workspace", "-w", type=str, default=".")
124
+ lsp_symbols.add_argument("--json", action="store_true")
125
+ lsp_symbols.set_defaults(func=DebugHandler.handle_lsp_symbols)
126
+
127
+ # debug lsp definition
128
+ lsp_def = lsp_subparsers.add_parser("definition", help="Get definition location")
129
+ lsp_def.add_argument("location", type=str, help="file:line:col")
130
+ lsp_def.add_argument("--workspace", "-w", type=str, default=".")
131
+ lsp_def.add_argument("--json", action="store_true")
132
+ lsp_def.set_defaults(func=DebugHandler.handle_lsp_definition)
133
+
134
+ # debug lsp references
135
+ lsp_refs = lsp_subparsers.add_parser("references", help="Get references")
136
+ lsp_refs.add_argument("location", type=str, help="file:line:col")
137
+ lsp_refs.add_argument("--workspace", "-w", type=str, default=".")
138
+ lsp_refs.add_argument("--json", action="store_true")
139
+ lsp_refs.set_defaults(func=DebugHandler.handle_lsp_references)
140
+
141
+ # debug lsp diagnostics
142
+ lsp_diag = lsp_subparsers.add_parser("diagnostics", help="Get diagnostics")
143
+ lsp_diag.add_argument("file", type=str, nargs="?", help="File path (optional)")
144
+ lsp_diag.add_argument("--all", action="store_true", help="Get all diagnostics")
145
+ lsp_diag.add_argument("--workspace", "-w", type=str, default=".")
146
+ lsp_diag.add_argument("--json", action="store_true")
147
+ lsp_diag.set_defaults(func=DebugHandler.handle_lsp_diagnostics)
148
+
149
+ # debug acp
150
+ acp_parser = debug_subparsers.add_parser(
151
+ "acp",
152
+ help="Direct ACP probes",
153
+ description="Probe ACP server directly"
154
+ )
155
+ acp_subparsers = acp_parser.add_subparsers(dest="acp_command", help="ACP subcommands")
156
+
157
+ # debug acp status
158
+ acp_status = acp_subparsers.add_parser("status", help="Show ACP status")
159
+ acp_status.add_argument("--workspace", "-w", type=str, default=".")
160
+ acp_status.add_argument("--json", action="store_true")
161
+ acp_status.set_defaults(func=DebugHandler.handle_acp_status)
162
+
163
+ # debug acp plan
164
+ acp_plan = acp_subparsers.add_parser("plan", help="Create action plan")
165
+ acp_plan.add_argument("-p", "--prompt", type=str, required=True)
166
+ acp_plan.add_argument("--workspace", "-w", type=str, default=".")
167
+ acp_plan.add_argument("--json", action="store_true")
168
+ acp_plan.set_defaults(func=DebugHandler.handle_acp_plan)
169
+
170
+ # debug acp apply
171
+ acp_apply = acp_subparsers.add_parser("apply", help="Apply action plan")
172
+ acp_apply.add_argument("-p", "--prompt", type=str, required=True)
173
+ acp_apply.add_argument("--approval", type=str, choices=["manual", "auto", "scoped"], default="auto")
174
+ acp_apply.add_argument("--workspace", "-w", type=str, default=".")
175
+ acp_apply.add_argument("--json", action="store_true")
176
+ acp_apply.set_defaults(func=DebugHandler.handle_acp_apply)
177
+
178
+ # debug trace
179
+ trace_parser = debug_subparsers.add_parser(
180
+ "trace",
181
+ help="Trace record/replay/diff",
182
+ description="Manage trace recordings"
183
+ )
184
+ trace_subparsers = trace_parser.add_subparsers(dest="trace_command", help="Trace subcommands")
185
+
186
+ # debug trace record
187
+ trace_record = trace_subparsers.add_parser("record", help="Record session trace")
188
+ trace_record.add_argument("-o", "--output", type=str, required=True, help="Output file")
189
+ trace_record.set_defaults(func=DebugHandler.handle_trace_record)
190
+
191
+ # debug trace replay
192
+ trace_replay = trace_subparsers.add_parser("replay", help="Replay recorded trace")
193
+ trace_replay.add_argument("file", type=str, help="Trace file to replay")
194
+ trace_replay.add_argument("--json", action="store_true")
195
+ trace_replay.set_defaults(func=DebugHandler.handle_trace_replay)
196
+
197
+ # debug trace diff
198
+ trace_diff = trace_subparsers.add_parser("diff", help="Compare two traces")
199
+ trace_diff.add_argument("file1", type=str, help="First trace file")
200
+ trace_diff.add_argument("file2", type=str, help="Second trace file")
201
+ trace_diff.add_argument("--json", action="store_true")
202
+ trace_diff.set_defaults(func=DebugHandler.handle_trace_diff)
203
+
204
+ @staticmethod
205
+ def handle_interactive(args: argparse.Namespace) -> int:
206
+ """Handle debug interactive command."""
207
+ return asyncio.run(DebugHandler._run_interactive(args))
208
+
209
+ @staticmethod
210
+ async def _run_interactive(args: argparse.Namespace) -> int:
211
+ """Run interactive turn asynchronously."""
212
+ from .interactive_runtime import create_runtime
213
+ from .code_intelligence import CodeIntelligenceRouter
214
+ from .action_orchestrator import ActionOrchestrator
215
+
216
+ start_time = time.time()
217
+
218
+ # Create runtime
219
+ runtime = create_runtime(
220
+ workspace=args.workspace,
221
+ lsp=args.lsp and not args.no_lsp,
222
+ acp=args.acp and not args.no_acp,
223
+ approval=args.approval,
224
+ trace=True,
225
+ trace_file=args.trace_file,
226
+ json_output=args.json,
227
+ timeout=args.timeout,
228
+ model=getattr(args, 'model', None)
229
+ )
230
+
231
+ result = {
232
+ "prompt": args.prompt,
233
+ "workspace": args.workspace,
234
+ "start_time": start_time
235
+ }
236
+
237
+ try:
238
+ # Start runtime
239
+ status = await runtime.start()
240
+ result["runtime_status"] = status
241
+
242
+ if not status.get("started"):
243
+ result["error"] = "Failed to start runtime"
244
+ DebugHandler._output_result(result, args.json)
245
+ return 1
246
+
247
+ # Classify intent
248
+ code_router = CodeIntelligenceRouter(runtime)
249
+ action_orchestrator = ActionOrchestrator(runtime)
250
+
251
+ prompt = args.prompt.lower()
252
+
253
+ # Determine if this is a code query or action
254
+ is_code_query = any(kw in prompt for kw in [
255
+ "list", "show", "find", "where", "definition", "reference",
256
+ "symbol", "function", "class", "diagnostic", "error"
257
+ ])
258
+
259
+ is_action = any(kw in prompt for kw in [
260
+ "create", "edit", "modify", "delete", "rename", "add", "remove",
261
+ "change", "update", "refactor"
262
+ ])
263
+
264
+ if is_code_query:
265
+ # Route to code intelligence
266
+ query_result = await code_router.handle_query(args.prompt)
267
+ result["type"] = "code_query"
268
+ result["query_result"] = query_result.to_dict()
269
+ result["lsp_used"] = query_result.lsp_used
270
+ result["citations"] = query_result.citations
271
+
272
+ elif is_action:
273
+ # Route to action orchestrator
274
+ action_result = await action_orchestrator.execute(
275
+ args.prompt,
276
+ auto_approve=(args.approval == "auto")
277
+ )
278
+ result["type"] = "action"
279
+ result["action_result"] = action_result.to_dict()
280
+ result["read_only_blocked"] = action_result.read_only_blocked
281
+
282
+ else:
283
+ # General query - just return status
284
+ result["type"] = "general"
285
+ result["message"] = "Query processed"
286
+
287
+ # Get trace
288
+ trace = runtime.get_trace()
289
+ if trace:
290
+ result["trace"] = trace.to_dict()
291
+
292
+ result["success"] = True
293
+
294
+ except Exception as e:
295
+ result["error"] = str(e)
296
+ result["success"] = False
297
+ logger.exception("Debug interactive failed")
298
+
299
+ finally:
300
+ await runtime.stop()
301
+ result["end_time"] = time.time()
302
+ result["duration_ms"] = (result["end_time"] - start_time) * 1000
303
+
304
+ # Save trace if requested
305
+ if args.trace_file and "trace" in result:
306
+ with open(args.trace_file, "w") as f:
307
+ json.dump(result["trace"], f, indent=2, default=str)
308
+ if not args.json:
309
+ print(f"Trace saved to: {args.trace_file}")
310
+
311
+ DebugHandler._output_result(result, args.json)
312
+ return 0 if result.get("success") else 1
313
+
314
+ @staticmethod
315
+ def handle_lsp_status(args: argparse.Namespace) -> int:
316
+ """Handle debug lsp status command."""
317
+ return asyncio.run(DebugHandler._run_lsp_status(args))
318
+
319
+ @staticmethod
320
+ async def _run_lsp_status(args: argparse.Namespace) -> int:
321
+ """Run LSP status check."""
322
+ from .interactive_runtime import create_runtime
323
+
324
+ runtime = create_runtime(
325
+ workspace=args.workspace,
326
+ lsp=True,
327
+ acp=False
328
+ )
329
+
330
+ try:
331
+ await runtime.start()
332
+ result = {
333
+ "lsp_enabled": True,
334
+ "lsp_ready": runtime.lsp_ready,
335
+ "lsp_status": runtime._lsp_state.status.value,
336
+ "lsp_error": runtime._lsp_state.error,
337
+ "workspace": args.workspace
338
+ }
339
+
340
+ DebugHandler._output_result(result, args.json)
341
+ return 0 if runtime.lsp_ready else 1
342
+
343
+ finally:
344
+ await runtime.stop()
345
+
346
+ @staticmethod
347
+ def handle_lsp_symbols(args: argparse.Namespace) -> int:
348
+ """Handle debug lsp symbols command."""
349
+ return asyncio.run(DebugHandler._run_lsp_symbols(args))
350
+
351
+ @staticmethod
352
+ async def _run_lsp_symbols(args: argparse.Namespace) -> int:
353
+ """Run LSP symbols query."""
354
+ from .interactive_runtime import create_runtime
355
+ from .code_intelligence import CodeIntelligenceRouter
356
+
357
+ runtime = create_runtime(
358
+ workspace=args.workspace,
359
+ lsp=True,
360
+ acp=False
361
+ )
362
+
363
+ try:
364
+ await runtime.start()
365
+
366
+ if not runtime.lsp_ready:
367
+ # Use fallback
368
+ router = CodeIntelligenceRouter(runtime)
369
+ result = await router._fallback_list_symbols(args.file)
370
+ else:
371
+ symbols = await runtime.lsp_get_symbols(args.file)
372
+ result = {
373
+ "file": args.file,
374
+ "symbols": symbols,
375
+ "count": len(symbols) if symbols else 0,
376
+ "lsp_used": True
377
+ }
378
+
379
+ if hasattr(result, 'to_dict'):
380
+ result = result.to_dict()
381
+
382
+ DebugHandler._output_result(result, args.json)
383
+ return 0
384
+
385
+ finally:
386
+ await runtime.stop()
387
+
388
+ @staticmethod
389
+ def handle_lsp_definition(args: argparse.Namespace) -> int:
390
+ """Handle debug lsp definition command."""
391
+ return asyncio.run(DebugHandler._run_lsp_definition(args))
392
+
393
+ @staticmethod
394
+ async def _run_lsp_definition(args: argparse.Namespace) -> int:
395
+ """Run LSP definition query."""
396
+ from .interactive_runtime import create_runtime
397
+
398
+ # Parse location
399
+ parts = args.location.split(":")
400
+ if len(parts) != 3:
401
+ print("Error: Location must be in format file:line:col", file=sys.stderr)
402
+ return 1
403
+
404
+ file_path, line, col = parts[0], int(parts[1]), int(parts[2])
405
+
406
+ runtime = create_runtime(
407
+ workspace=args.workspace,
408
+ lsp=True,
409
+ acp=False
410
+ )
411
+
412
+ try:
413
+ await runtime.start()
414
+
415
+ definitions = await runtime.lsp_get_definition(file_path, line, col)
416
+ result = {
417
+ "file": file_path,
418
+ "line": line,
419
+ "col": col,
420
+ "definitions": definitions,
421
+ "count": len(definitions) if definitions else 0,
422
+ "lsp_used": runtime.lsp_ready
423
+ }
424
+
425
+ DebugHandler._output_result(result, args.json)
426
+ return 0
427
+
428
+ finally:
429
+ await runtime.stop()
430
+
431
+ @staticmethod
432
+ def handle_lsp_references(args: argparse.Namespace) -> int:
433
+ """Handle debug lsp references command."""
434
+ return asyncio.run(DebugHandler._run_lsp_references(args))
435
+
436
+ @staticmethod
437
+ async def _run_lsp_references(args: argparse.Namespace) -> int:
438
+ """Run LSP references query."""
439
+ from .interactive_runtime import create_runtime
440
+
441
+ # Parse location
442
+ parts = args.location.split(":")
443
+ if len(parts) != 3:
444
+ print("Error: Location must be in format file:line:col", file=sys.stderr)
445
+ return 1
446
+
447
+ file_path, line, col = parts[0], int(parts[1]), int(parts[2])
448
+
449
+ runtime = create_runtime(
450
+ workspace=args.workspace,
451
+ lsp=True,
452
+ acp=False
453
+ )
454
+
455
+ try:
456
+ await runtime.start()
457
+
458
+ references = await runtime.lsp_get_references(file_path, line, col)
459
+ result = {
460
+ "file": file_path,
461
+ "line": line,
462
+ "col": col,
463
+ "references": references,
464
+ "count": len(references) if references else 0,
465
+ "lsp_used": runtime.lsp_ready
466
+ }
467
+
468
+ DebugHandler._output_result(result, args.json)
469
+ return 0
470
+
471
+ finally:
472
+ await runtime.stop()
473
+
474
+ @staticmethod
475
+ def handle_lsp_diagnostics(args: argparse.Namespace) -> int:
476
+ """Handle debug lsp diagnostics command."""
477
+ return asyncio.run(DebugHandler._run_lsp_diagnostics(args))
478
+
479
+ @staticmethod
480
+ async def _run_lsp_diagnostics(args: argparse.Namespace) -> int:
481
+ """Run LSP diagnostics query."""
482
+ from .interactive_runtime import create_runtime
483
+
484
+ runtime = create_runtime(
485
+ workspace=args.workspace,
486
+ lsp=True,
487
+ acp=False
488
+ )
489
+
490
+ try:
491
+ await runtime.start()
492
+
493
+ file_path = args.file if not args.all else None
494
+ diagnostics = await runtime.lsp_get_diagnostics(file_path)
495
+
496
+ result = {
497
+ "file": file_path or "all",
498
+ "diagnostics": diagnostics,
499
+ "count": len(diagnostics) if diagnostics else 0,
500
+ "lsp_used": runtime.lsp_ready
501
+ }
502
+
503
+ DebugHandler._output_result(result, args.json)
504
+ return 0
505
+
506
+ finally:
507
+ await runtime.stop()
508
+
509
+ @staticmethod
510
+ def handle_acp_status(args: argparse.Namespace) -> int:
511
+ """Handle debug acp status command."""
512
+ return asyncio.run(DebugHandler._run_acp_status(args))
513
+
514
+ @staticmethod
515
+ async def _run_acp_status(args: argparse.Namespace) -> int:
516
+ """Run ACP status check."""
517
+ from .interactive_runtime import create_runtime
518
+
519
+ runtime = create_runtime(
520
+ workspace=args.workspace,
521
+ lsp=False,
522
+ acp=True
523
+ )
524
+
525
+ try:
526
+ await runtime.start()
527
+ result = {
528
+ "acp_enabled": True,
529
+ "acp_ready": runtime.acp_ready,
530
+ "acp_status": runtime._acp_state.status.value,
531
+ "acp_error": runtime._acp_state.error,
532
+ "read_only": runtime.read_only,
533
+ "workspace": args.workspace
534
+ }
535
+
536
+ DebugHandler._output_result(result, args.json)
537
+ return 0 if runtime.acp_ready else 1
538
+
539
+ finally:
540
+ await runtime.stop()
541
+
542
+ @staticmethod
543
+ def handle_acp_plan(args: argparse.Namespace) -> int:
544
+ """Handle debug acp plan command."""
545
+ return asyncio.run(DebugHandler._run_acp_plan(args))
546
+
547
+ @staticmethod
548
+ async def _run_acp_plan(args: argparse.Namespace) -> int:
549
+ """Run ACP plan creation."""
550
+ from .interactive_runtime import create_runtime
551
+ from .action_orchestrator import ActionOrchestrator
552
+
553
+ runtime = create_runtime(
554
+ workspace=args.workspace,
555
+ lsp=False,
556
+ acp=True
557
+ )
558
+
559
+ try:
560
+ await runtime.start()
561
+
562
+ orchestrator = ActionOrchestrator(runtime)
563
+ result = await orchestrator.create_plan(args.prompt)
564
+
565
+ DebugHandler._output_result(result.to_dict(), args.json)
566
+ return 0 if result.success else 1
567
+
568
+ finally:
569
+ await runtime.stop()
570
+
571
+ @staticmethod
572
+ def handle_acp_apply(args: argparse.Namespace) -> int:
573
+ """Handle debug acp apply command."""
574
+ return asyncio.run(DebugHandler._run_acp_apply(args))
575
+
576
+ @staticmethod
577
+ async def _run_acp_apply(args: argparse.Namespace) -> int:
578
+ """Run ACP plan and apply."""
579
+ from .interactive_runtime import create_runtime
580
+ from .action_orchestrator import ActionOrchestrator
581
+
582
+ runtime = create_runtime(
583
+ workspace=args.workspace,
584
+ lsp=False,
585
+ acp=True,
586
+ approval=args.approval
587
+ )
588
+
589
+ try:
590
+ await runtime.start()
591
+
592
+ orchestrator = ActionOrchestrator(runtime)
593
+ result = await orchestrator.execute(
594
+ args.prompt,
595
+ auto_approve=(args.approval == "auto")
596
+ )
597
+
598
+ DebugHandler._output_result(result.to_dict(), args.json)
599
+ return 0 if result.success else 1
600
+
601
+ finally:
602
+ await runtime.stop()
603
+
604
+ @staticmethod
605
+ def handle_trace_record(args: argparse.Namespace) -> int:
606
+ """Handle debug trace record command."""
607
+ print(f"Trace recording will be saved to: {args.output}")
608
+ print("Start an interactive session with --trace-file to record.")
609
+ return 0
610
+
611
+ @staticmethod
612
+ def handle_trace_replay(args: argparse.Namespace) -> int:
613
+ """Handle debug trace replay command."""
614
+ try:
615
+ with open(args.file) as f:
616
+ trace = json.load(f)
617
+
618
+ if args.json:
619
+ print(json.dumps(trace, indent=2))
620
+ else:
621
+ print(f"Trace from: {args.file}")
622
+ print(f"Start time: {trace.get('start_time')}")
623
+ print(f"End time: {trace.get('end_time')}")
624
+ print(f"Entries: {len(trace.get('entries', []))}")
625
+ print("\nEntries:")
626
+ for entry in trace.get("entries", []):
627
+ print(f" [{entry.get('category')}] {entry.get('action')}")
628
+ if entry.get('duration_ms'):
629
+ print(f" Duration: {entry.get('duration_ms'):.2f}ms")
630
+ if entry.get('error'):
631
+ print(f" Error: {entry.get('error')}")
632
+
633
+ return 0
634
+ except Exception as e:
635
+ print(f"Error: {e}", file=sys.stderr)
636
+ return 1
637
+
638
+ @staticmethod
639
+ def handle_trace_diff(args: argparse.Namespace) -> int:
640
+ """Handle debug trace diff command."""
641
+ try:
642
+ with open(args.file1) as f:
643
+ trace1 = json.load(f)
644
+ with open(args.file2) as f:
645
+ trace2 = json.load(f)
646
+
647
+ diff = {
648
+ "file1": args.file1,
649
+ "file2": args.file2,
650
+ "entries_diff": {
651
+ "file1_count": len(trace1.get("entries", [])),
652
+ "file2_count": len(trace2.get("entries", []))
653
+ },
654
+ "duration_diff": {
655
+ "file1": trace1.get("end_time", 0) - trace1.get("start_time", 0),
656
+ "file2": trace2.get("end_time", 0) - trace2.get("start_time", 0)
657
+ }
658
+ }
659
+
660
+ DebugHandler._output_result(diff, args.json)
661
+ return 0
662
+ except Exception as e:
663
+ print(f"Error: {e}", file=sys.stderr)
664
+ return 1
665
+
666
+ @staticmethod
667
+ def _output_result(result: Dict[str, Any], as_json: bool) -> None:
668
+ """Output result in appropriate format."""
669
+ if as_json:
670
+ print(json.dumps(result, indent=2, default=str))
671
+ else:
672
+ for key, value in result.items():
673
+ if isinstance(value, dict):
674
+ print(f"{key}:")
675
+ for k, v in value.items():
676
+ print(f" {k}: {v}")
677
+ elif isinstance(value, list):
678
+ print(f"{key}: ({len(value)} items)")
679
+ for item in value[:5]: # Show first 5
680
+ print(f" - {item}")
681
+ if len(value) > 5:
682
+ print(f" ... and {len(value) - 5} more")
683
+ else:
684
+ print(f"{key}: {value}")
685
+
686
+
687
+ def setup_debug_subparser(subparsers) -> None:
688
+ """Set up the debug subcommand parser."""
689
+ DebugHandler.setup_subparser(subparsers)
690
+
691
+
692
+ def run_debug_command(args: List[str]) -> int:
693
+ """Run debug command from CLI."""
694
+ parser = argparse.ArgumentParser(
695
+ prog="praisonai debug",
696
+ description="Debug and test interactive coding assistant flows"
697
+ )
698
+ subparsers = parser.add_subparsers(dest="debug_command", help="Debug subcommands")
699
+
700
+ # debug interactive
701
+ interactive_parser = subparsers.add_parser(
702
+ "interactive",
703
+ help="Run single interactive turn non-interactively",
704
+ description="Execute one interactive turn with full LSP/ACP pipeline"
705
+ )
706
+ interactive_parser.add_argument("-p", "--prompt", type=str, required=True, help="Prompt to execute")
707
+ interactive_parser.add_argument("--lsp", action="store_true", default=True, help="Enable LSP (default: enabled)")
708
+ interactive_parser.add_argument("--no-lsp", action="store_true", help="Disable LSP")
709
+ interactive_parser.add_argument("--acp", action="store_true", default=True, help="Enable ACP (default: enabled)")
710
+ interactive_parser.add_argument("--no-acp", action="store_true", help="Disable ACP")
711
+ interactive_parser.add_argument("--approval", type=str, choices=["manual", "auto", "scoped"], default="auto", help="Approval mode")
712
+ interactive_parser.add_argument("--workspace", "-w", type=str, default=".", help="Workspace root directory")
713
+ interactive_parser.add_argument("--json", action="store_true", help="Output JSON trace")
714
+ interactive_parser.add_argument("--trace-file", type=str, help="Save trace to file")
715
+ interactive_parser.add_argument("--timeout", type=float, default=60.0, help="Timeout in seconds")
716
+ interactive_parser.add_argument("--model", type=str, help="LLM model to use")
717
+ interactive_parser.set_defaults(func=DebugHandler.handle_interactive)
718
+
719
+ # debug lsp
720
+ lsp_parser = subparsers.add_parser("lsp", help="Direct LSP probes")
721
+ lsp_subparsers = lsp_parser.add_subparsers(dest="lsp_command", help="LSP subcommands")
722
+
723
+ # debug lsp status
724
+ lsp_status = lsp_subparsers.add_parser("status", help="Show LSP status")
725
+ lsp_status.add_argument("--workspace", "-w", type=str, default=".")
726
+ lsp_status.add_argument("--json", action="store_true")
727
+ lsp_status.set_defaults(func=DebugHandler.handle_lsp_status)
728
+
729
+ # debug lsp symbols
730
+ lsp_symbols = lsp_subparsers.add_parser("symbols", help="List symbols in file")
731
+ lsp_symbols.add_argument("file", type=str, help="File path")
732
+ lsp_symbols.add_argument("--workspace", "-w", type=str, default=".")
733
+ lsp_symbols.add_argument("--json", action="store_true")
734
+ lsp_symbols.set_defaults(func=DebugHandler.handle_lsp_symbols)
735
+
736
+ # debug lsp definition
737
+ lsp_def = lsp_subparsers.add_parser("definition", help="Get definition location")
738
+ lsp_def.add_argument("location", type=str, help="file:line:col")
739
+ lsp_def.add_argument("--workspace", "-w", type=str, default=".")
740
+ lsp_def.add_argument("--json", action="store_true")
741
+ lsp_def.set_defaults(func=DebugHandler.handle_lsp_definition)
742
+
743
+ # debug lsp references
744
+ lsp_refs = lsp_subparsers.add_parser("references", help="Get references")
745
+ lsp_refs.add_argument("location", type=str, help="file:line:col")
746
+ lsp_refs.add_argument("--workspace", "-w", type=str, default=".")
747
+ lsp_refs.add_argument("--json", action="store_true")
748
+ lsp_refs.set_defaults(func=DebugHandler.handle_lsp_references)
749
+
750
+ # debug lsp diagnostics
751
+ lsp_diag = lsp_subparsers.add_parser("diagnostics", help="Get diagnostics")
752
+ lsp_diag.add_argument("file", type=str, nargs="?", help="File path (optional)")
753
+ lsp_diag.add_argument("--all", action="store_true", help="Get all diagnostics")
754
+ lsp_diag.add_argument("--workspace", "-w", type=str, default=".")
755
+ lsp_diag.add_argument("--json", action="store_true")
756
+ lsp_diag.set_defaults(func=DebugHandler.handle_lsp_diagnostics)
757
+
758
+ # debug acp
759
+ acp_parser = subparsers.add_parser("acp", help="Direct ACP probes")
760
+ acp_subparsers = acp_parser.add_subparsers(dest="acp_command", help="ACP subcommands")
761
+
762
+ # debug acp status
763
+ acp_status = acp_subparsers.add_parser("status", help="Show ACP status")
764
+ acp_status.add_argument("--workspace", "-w", type=str, default=".")
765
+ acp_status.add_argument("--json", action="store_true")
766
+ acp_status.set_defaults(func=DebugHandler.handle_acp_status)
767
+
768
+ # debug acp plan
769
+ acp_plan = acp_subparsers.add_parser("plan", help="Create action plan")
770
+ acp_plan.add_argument("-p", "--prompt", type=str, required=True)
771
+ acp_plan.add_argument("--workspace", "-w", type=str, default=".")
772
+ acp_plan.add_argument("--json", action="store_true")
773
+ acp_plan.set_defaults(func=DebugHandler.handle_acp_plan)
774
+
775
+ # debug acp apply
776
+ acp_apply = acp_subparsers.add_parser("apply", help="Apply action plan")
777
+ acp_apply.add_argument("-p", "--prompt", type=str, required=True)
778
+ acp_apply.add_argument("--approval", type=str, choices=["manual", "auto", "scoped"], default="auto")
779
+ acp_apply.add_argument("--workspace", "-w", type=str, default=".")
780
+ acp_apply.add_argument("--json", action="store_true")
781
+ acp_apply.set_defaults(func=DebugHandler.handle_acp_apply)
782
+
783
+ # debug trace
784
+ trace_parser = subparsers.add_parser("trace", help="Trace record/replay/diff")
785
+ trace_subparsers = trace_parser.add_subparsers(dest="trace_command", help="Trace subcommands")
786
+
787
+ # debug trace record
788
+ trace_record = trace_subparsers.add_parser("record", help="Record session trace")
789
+ trace_record.add_argument("-o", "--output", type=str, required=True, help="Output file")
790
+ trace_record.set_defaults(func=DebugHandler.handle_trace_record)
791
+
792
+ # debug trace replay
793
+ trace_replay = trace_subparsers.add_parser("replay", help="Replay recorded trace")
794
+ trace_replay.add_argument("file", type=str, help="Trace file to replay")
795
+ trace_replay.add_argument("--json", action="store_true")
796
+ trace_replay.set_defaults(func=DebugHandler.handle_trace_replay)
797
+
798
+ # debug trace diff
799
+ trace_diff = trace_subparsers.add_parser("diff", help="Compare two traces")
800
+ trace_diff.add_argument("file1", type=str, help="First trace file")
801
+ trace_diff.add_argument("file2", type=str, help="Second trace file")
802
+ trace_diff.add_argument("--json", action="store_true")
803
+ trace_diff.set_defaults(func=DebugHandler.handle_trace_diff)
804
+
805
+ parsed = parser.parse_args(args)
806
+ if hasattr(parsed, 'func'):
807
+ return parsed.func(parsed)
808
+ else:
809
+ parser.print_help()
810
+ return 0