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,367 @@
1
+ """
2
+ MCP (Model Context Protocol) checks for the Doctor CLI module.
3
+
4
+ Validates MCP server configurations and connectivity.
5
+ """
6
+
7
+ import os
8
+ import shutil
9
+ from pathlib import Path
10
+
11
+ from ..models import (
12
+ CheckCategory,
13
+ CheckResult,
14
+ CheckStatus,
15
+ CheckSeverity,
16
+ DoctorConfig,
17
+ )
18
+ from ..registry import register_check
19
+
20
+
21
+ def _find_mcp_config() -> dict:
22
+ """Find MCP configuration files."""
23
+ # Use canonical MCP paths from praisonaiagents.paths
24
+ try:
25
+ from praisonaiagents.paths import get_mcp_dir, get_project_data_dir
26
+ canonical_mcp_dir = get_mcp_dir()
27
+ project_mcp_dir = get_project_data_dir() / "mcp"
28
+
29
+ locations = [
30
+ project_mcp_dir / "config.json",
31
+ canonical_mcp_dir / "config.json",
32
+ # Legacy paths for backward compatibility
33
+ Path.cwd() / ".praison" / "mcp.json",
34
+ Path.cwd() / ".praison" / "mcp" / "config.json",
35
+ Path.home() / ".praison" / "mcp.json",
36
+ Path.home() / ".config" / "praison" / "mcp.json",
37
+ ]
38
+ except ImportError:
39
+ # Fallback to legacy paths if praisonaiagents not available
40
+ locations = [
41
+ Path.cwd() / ".praisonai" / "mcp" / "config.json",
42
+ Path.cwd() / ".praison" / "mcp.json",
43
+ Path.cwd() / ".praison" / "mcp" / "config.json",
44
+ Path.home() / ".praison" / "mcp.json",
45
+ Path.home() / ".config" / "praison" / "mcp.json",
46
+ ]
47
+
48
+ for loc in locations:
49
+ if loc.exists():
50
+ try:
51
+ import json
52
+ with open(loc) as f:
53
+ return {"path": str(loc), "config": json.load(f)}
54
+ except Exception:
55
+ pass
56
+
57
+ return {}
58
+
59
+
60
+ @register_check(
61
+ id="mcp_config",
62
+ title="MCP Configuration",
63
+ description="Check MCP configuration file",
64
+ category=CheckCategory.MCP,
65
+ severity=CheckSeverity.INFO,
66
+ )
67
+ def check_mcp_config(config: DoctorConfig) -> CheckResult:
68
+ """Check MCP configuration file."""
69
+ mcp_config = _find_mcp_config()
70
+
71
+ if mcp_config:
72
+ servers = mcp_config.get("config", {}).get("mcpServers", {})
73
+ server_count = len(servers) if isinstance(servers, dict) else 0
74
+
75
+ return CheckResult(
76
+ id="mcp_config",
77
+ title="MCP Configuration",
78
+ category=CheckCategory.MCP,
79
+ status=CheckStatus.PASS,
80
+ message=f"MCP config found with {server_count} server(s)",
81
+ details=f"Path: {mcp_config['path']}",
82
+ metadata={"path": mcp_config["path"], "server_count": server_count},
83
+ )
84
+ else:
85
+ return CheckResult(
86
+ id="mcp_config",
87
+ title="MCP Configuration",
88
+ category=CheckCategory.MCP,
89
+ status=CheckStatus.SKIP,
90
+ message="No MCP configuration found (optional)",
91
+ details="Create ~/.praisonai/mcp/config.json to configure MCP servers",
92
+ )
93
+
94
+
95
+ @register_check(
96
+ id="mcp_npx",
97
+ title="npx for MCP",
98
+ description="Check npx availability for MCP servers",
99
+ category=CheckCategory.MCP,
100
+ severity=CheckSeverity.LOW,
101
+ )
102
+ def check_mcp_npx(config: DoctorConfig) -> CheckResult:
103
+ """Check npx availability for MCP servers."""
104
+ npx_path = shutil.which("npx")
105
+
106
+ if npx_path:
107
+ return CheckResult(
108
+ id="mcp_npx",
109
+ title="npx for MCP",
110
+ category=CheckCategory.MCP,
111
+ status=CheckStatus.PASS,
112
+ message=f"npx available at {npx_path}",
113
+ )
114
+ else:
115
+ return CheckResult(
116
+ id="mcp_npx",
117
+ title="npx for MCP",
118
+ category=CheckCategory.MCP,
119
+ status=CheckStatus.WARN,
120
+ message="npx not found (required for most MCP servers)",
121
+ remediation="Install Node.js to use MCP servers",
122
+ )
123
+
124
+
125
+ @register_check(
126
+ id="mcp_python",
127
+ title="Python MCP Support",
128
+ description="Check Python MCP package availability",
129
+ category=CheckCategory.MCP,
130
+ severity=CheckSeverity.LOW,
131
+ )
132
+ def check_mcp_python(config: DoctorConfig) -> CheckResult:
133
+ """Check Python MCP package availability."""
134
+ try:
135
+ import mcp
136
+ version = getattr(mcp, "__version__", "unknown")
137
+ return CheckResult(
138
+ id="mcp_python",
139
+ title="Python MCP Support",
140
+ category=CheckCategory.MCP,
141
+ status=CheckStatus.PASS,
142
+ message=f"mcp package {version} available",
143
+ )
144
+ except ImportError:
145
+ return CheckResult(
146
+ id="mcp_python",
147
+ title="Python MCP Support",
148
+ category=CheckCategory.MCP,
149
+ status=CheckStatus.SKIP,
150
+ message="mcp package not installed (optional)",
151
+ remediation="Install with: pip install mcp",
152
+ )
153
+
154
+
155
+ @register_check(
156
+ id="mcp_servers_valid",
157
+ title="MCP Server Configs Valid",
158
+ description="Validate MCP server configurations",
159
+ category=CheckCategory.MCP,
160
+ severity=CheckSeverity.MEDIUM,
161
+ dependencies=["mcp_config"],
162
+ )
163
+ def check_mcp_servers_valid(config: DoctorConfig) -> CheckResult:
164
+ """Validate MCP server configurations."""
165
+ mcp_config = _find_mcp_config()
166
+
167
+ if not mcp_config:
168
+ return CheckResult(
169
+ id="mcp_servers_valid",
170
+ title="MCP Server Configs Valid",
171
+ category=CheckCategory.MCP,
172
+ status=CheckStatus.SKIP,
173
+ message="No MCP configuration to validate",
174
+ )
175
+
176
+ servers = mcp_config.get("config", {}).get("mcpServers", {})
177
+ if not servers:
178
+ return CheckResult(
179
+ id="mcp_servers_valid",
180
+ title="MCP Server Configs Valid",
181
+ category=CheckCategory.MCP,
182
+ status=CheckStatus.SKIP,
183
+ message="No MCP servers configured",
184
+ )
185
+
186
+ valid = []
187
+ invalid = []
188
+
189
+ for name, server_config in servers.items():
190
+ if not isinstance(server_config, dict):
191
+ invalid.append(f"{name}: invalid config format")
192
+ continue
193
+
194
+ command = server_config.get("command")
195
+ if not command:
196
+ invalid.append(f"{name}: missing 'command'")
197
+ continue
198
+
199
+ # Check if command executable exists
200
+ cmd_parts = command.split() if isinstance(command, str) else [command]
201
+ cmd_exe = cmd_parts[0] if cmd_parts else ""
202
+
203
+ if cmd_exe and shutil.which(cmd_exe):
204
+ valid.append(name)
205
+ else:
206
+ invalid.append(f"{name}: command '{cmd_exe}' not found")
207
+
208
+ if invalid:
209
+ return CheckResult(
210
+ id="mcp_servers_valid",
211
+ title="MCP Server Configs Valid",
212
+ category=CheckCategory.MCP,
213
+ status=CheckStatus.WARN,
214
+ message=f"{len(valid)} valid, {len(invalid)} invalid server(s)",
215
+ details="; ".join(invalid[:3]) + ("..." if len(invalid) > 3 else ""),
216
+ metadata={"valid": valid, "invalid": invalid},
217
+ )
218
+ else:
219
+ return CheckResult(
220
+ id="mcp_servers_valid",
221
+ title="MCP Server Configs Valid",
222
+ category=CheckCategory.MCP,
223
+ status=CheckStatus.PASS,
224
+ message=f"All {len(valid)} MCP server config(s) valid",
225
+ metadata={"valid": valid},
226
+ )
227
+
228
+
229
+ @register_check(
230
+ id="mcp_server_spawn",
231
+ title="MCP Server Spawn Test",
232
+ description="Test spawning MCP servers",
233
+ category=CheckCategory.MCP,
234
+ severity=CheckSeverity.LOW,
235
+ requires_deep=True,
236
+ )
237
+ def check_mcp_server_spawn(config: DoctorConfig) -> CheckResult:
238
+ """Test spawning MCP servers (deep mode only)."""
239
+ mcp_config = _find_mcp_config()
240
+
241
+ if not mcp_config:
242
+ return CheckResult(
243
+ id="mcp_server_spawn",
244
+ title="MCP Server Spawn Test",
245
+ category=CheckCategory.MCP,
246
+ status=CheckStatus.SKIP,
247
+ message="No MCP configuration to test",
248
+ )
249
+
250
+ servers = mcp_config.get("config", {}).get("mcpServers", {})
251
+ if not servers:
252
+ return CheckResult(
253
+ id="mcp_server_spawn",
254
+ title="MCP Server Spawn Test",
255
+ category=CheckCategory.MCP,
256
+ status=CheckStatus.SKIP,
257
+ message="No MCP servers to test",
258
+ )
259
+
260
+ # Only test first server to avoid long delays
261
+ test_server = config.name if config.name else list(servers.keys())[0]
262
+ server_config = servers.get(test_server)
263
+
264
+ if not server_config:
265
+ return CheckResult(
266
+ id="mcp_server_spawn",
267
+ title="MCP Server Spawn Test",
268
+ category=CheckCategory.MCP,
269
+ status=CheckStatus.SKIP,
270
+ message=f"Server '{test_server}' not found",
271
+ )
272
+
273
+ try:
274
+ import subprocess
275
+
276
+ command = server_config.get("command", "")
277
+ args = server_config.get("args", [])
278
+
279
+ if isinstance(command, str):
280
+ cmd = command.split() + args
281
+ else:
282
+ cmd = [command] + args
283
+
284
+ # Try to start the server with a short timeout
285
+ proc = subprocess.Popen(
286
+ cmd,
287
+ stdout=subprocess.PIPE,
288
+ stderr=subprocess.PIPE,
289
+ env={**os.environ, **server_config.get("env", {})},
290
+ )
291
+
292
+ # Wait briefly to see if it starts
293
+ import time
294
+ time.sleep(1)
295
+
296
+ if proc.poll() is None:
297
+ # Process is still running - success
298
+ proc.terminate()
299
+ proc.wait(timeout=2)
300
+ return CheckResult(
301
+ id="mcp_server_spawn",
302
+ title="MCP Server Spawn Test",
303
+ category=CheckCategory.MCP,
304
+ status=CheckStatus.PASS,
305
+ message=f"Server '{test_server}' started successfully",
306
+ )
307
+ else:
308
+ # Process exited
309
+ stderr = proc.stderr.read().decode()[:200] if proc.stderr else ""
310
+ return CheckResult(
311
+ id="mcp_server_spawn",
312
+ title="MCP Server Spawn Test",
313
+ category=CheckCategory.MCP,
314
+ status=CheckStatus.WARN,
315
+ message=f"Server '{test_server}' exited immediately",
316
+ details=stderr if stderr else "No error output",
317
+ )
318
+
319
+ except FileNotFoundError as e:
320
+ return CheckResult(
321
+ id="mcp_server_spawn",
322
+ title="MCP Server Spawn Test",
323
+ category=CheckCategory.MCP,
324
+ status=CheckStatus.FAIL,
325
+ message=f"Command not found for '{test_server}'",
326
+ details=str(e),
327
+ remediation="Install the required MCP server package",
328
+ )
329
+ except Exception as e:
330
+ return CheckResult(
331
+ id="mcp_server_spawn",
332
+ title="MCP Server Spawn Test",
333
+ category=CheckCategory.MCP,
334
+ status=CheckStatus.WARN,
335
+ message=f"Could not test server '{test_server}'",
336
+ details=str(e)[:200],
337
+ )
338
+
339
+
340
+ @register_check(
341
+ id="mcp_praisonai_integration",
342
+ title="PraisonAI MCP Integration",
343
+ description="Check PraisonAI MCP integration",
344
+ category=CheckCategory.MCP,
345
+ severity=CheckSeverity.LOW,
346
+ )
347
+ def check_mcp_praisonai_integration(config: DoctorConfig) -> CheckResult:
348
+ """Check PraisonAI MCP integration."""
349
+ try:
350
+ from praisonaiagents.mcp import MCP
351
+ return CheckResult(
352
+ id="mcp_praisonai_integration",
353
+ title="PraisonAI MCP Integration",
354
+ category=CheckCategory.MCP,
355
+ status=CheckStatus.PASS,
356
+ message="PraisonAI MCP integration available",
357
+ )
358
+ except ImportError as e:
359
+ return CheckResult(
360
+ id="mcp_praisonai_integration",
361
+ title="PraisonAI MCP Integration",
362
+ category=CheckCategory.MCP,
363
+ status=CheckStatus.SKIP,
364
+ message="MCP integration not available",
365
+ details=str(e),
366
+ remediation="Install with: pip install praisonaiagents[mcp]",
367
+ )
@@ -0,0 +1,268 @@
1
+ """
2
+ Memory checks for the Doctor CLI module.
3
+
4
+ Validates memory/session storage and integrity.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+
10
+ from ..models import (
11
+ CheckCategory,
12
+ CheckResult,
13
+ CheckStatus,
14
+ CheckSeverity,
15
+ DoctorConfig,
16
+ )
17
+ from ..registry import register_check
18
+
19
+
20
+ def _find_memory_dirs() -> list:
21
+ """Find memory storage directories."""
22
+ locations = [
23
+ Path.cwd() / ".praison" / "memory",
24
+ Path.cwd() / ".praison" / "sessions",
25
+ Path.home() / ".praison" / "memory",
26
+ Path.home() / ".praison" / "sessions",
27
+ Path.home() / ".config" / "praison" / "memory",
28
+ ]
29
+
30
+ found = []
31
+ for loc in locations:
32
+ if loc.exists() and loc.is_dir():
33
+ found.append(str(loc))
34
+
35
+ return found
36
+
37
+
38
+ @register_check(
39
+ id="memory_dirs",
40
+ title="Memory Directories",
41
+ description="Check memory storage directories",
42
+ category=CheckCategory.MEMORY,
43
+ severity=CheckSeverity.INFO,
44
+ )
45
+ def check_memory_dirs(config: DoctorConfig) -> CheckResult:
46
+ """Check memory storage directories."""
47
+ dirs = _find_memory_dirs()
48
+
49
+ if dirs:
50
+ return CheckResult(
51
+ id="memory_dirs",
52
+ title="Memory Directories",
53
+ category=CheckCategory.MEMORY,
54
+ status=CheckStatus.PASS,
55
+ message=f"Found {len(dirs)} memory directory(ies)",
56
+ details=", ".join(dirs),
57
+ metadata={"directories": dirs},
58
+ )
59
+ else:
60
+ return CheckResult(
61
+ id="memory_dirs",
62
+ title="Memory Directories",
63
+ category=CheckCategory.MEMORY,
64
+ status=CheckStatus.SKIP,
65
+ message="No memory directories found (will be created on first use)",
66
+ )
67
+
68
+
69
+ @register_check(
70
+ id="memory_files",
71
+ title="Memory Files",
72
+ description="Check memory file integrity",
73
+ category=CheckCategory.MEMORY,
74
+ severity=CheckSeverity.MEDIUM,
75
+ dependencies=["memory_dirs"],
76
+ )
77
+ def check_memory_files(config: DoctorConfig) -> CheckResult:
78
+ """Check memory file integrity."""
79
+ dirs = _find_memory_dirs()
80
+
81
+ if not dirs:
82
+ return CheckResult(
83
+ id="memory_files",
84
+ title="Memory Files",
85
+ category=CheckCategory.MEMORY,
86
+ status=CheckStatus.SKIP,
87
+ message="No memory directories to check",
88
+ )
89
+
90
+ valid_files = 0
91
+ corrupt_files = []
92
+ total_size = 0
93
+
94
+ for dir_path in dirs:
95
+ memory_dir = Path(dir_path)
96
+ for file_path in memory_dir.rglob("*.json"):
97
+ try:
98
+ import json
99
+ with open(file_path) as f:
100
+ json.load(f)
101
+ valid_files += 1
102
+ total_size += file_path.stat().st_size
103
+ except json.JSONDecodeError:
104
+ corrupt_files.append(str(file_path))
105
+ except Exception:
106
+ pass
107
+
108
+ if corrupt_files:
109
+ return CheckResult(
110
+ id="memory_files",
111
+ title="Memory Files",
112
+ category=CheckCategory.MEMORY,
113
+ status=CheckStatus.WARN,
114
+ message=f"{len(corrupt_files)} corrupt file(s) found",
115
+ details="; ".join(corrupt_files[:3]) + ("..." if len(corrupt_files) > 3 else ""),
116
+ remediation="Remove or repair corrupt memory files",
117
+ metadata={"valid": valid_files, "corrupt": corrupt_files},
118
+ )
119
+ elif valid_files > 0:
120
+ size_kb = total_size / 1024
121
+ return CheckResult(
122
+ id="memory_files",
123
+ title="Memory Files",
124
+ category=CheckCategory.MEMORY,
125
+ status=CheckStatus.PASS,
126
+ message=f"{valid_files} valid memory file(s) ({size_kb:.1f} KB)",
127
+ metadata={"valid_count": valid_files, "total_size_bytes": total_size},
128
+ )
129
+ else:
130
+ return CheckResult(
131
+ id="memory_files",
132
+ title="Memory Files",
133
+ category=CheckCategory.MEMORY,
134
+ status=CheckStatus.SKIP,
135
+ message="No memory files found",
136
+ )
137
+
138
+
139
+ @register_check(
140
+ id="memory_sessions",
141
+ title="Session Storage",
142
+ description="Check session storage",
143
+ category=CheckCategory.MEMORY,
144
+ severity=CheckSeverity.INFO,
145
+ )
146
+ def check_memory_sessions(config: DoctorConfig) -> CheckResult:
147
+ """Check session storage."""
148
+ session_dirs = [
149
+ Path.cwd() / ".praison" / "sessions",
150
+ Path.home() / ".praison" / "sessions",
151
+ ]
152
+
153
+ sessions = []
154
+ for dir_path in session_dirs:
155
+ if dir_path.exists():
156
+ for item in dir_path.iterdir():
157
+ if item.is_dir() or item.suffix == ".json":
158
+ sessions.append(item.name)
159
+
160
+ if sessions:
161
+ return CheckResult(
162
+ id="memory_sessions",
163
+ title="Session Storage",
164
+ category=CheckCategory.MEMORY,
165
+ status=CheckStatus.PASS,
166
+ message=f"Found {len(sessions)} saved session(s)",
167
+ metadata={"sessions": sessions[:10]},
168
+ )
169
+ else:
170
+ return CheckResult(
171
+ id="memory_sessions",
172
+ title="Session Storage",
173
+ category=CheckCategory.MEMORY,
174
+ status=CheckStatus.SKIP,
175
+ message="No saved sessions found",
176
+ )
177
+
178
+
179
+ @register_check(
180
+ id="memory_chromadb",
181
+ title="ChromaDB Memory",
182
+ description="Check ChromaDB vector memory",
183
+ category=CheckCategory.MEMORY,
184
+ severity=CheckSeverity.LOW,
185
+ )
186
+ def check_memory_chromadb(config: DoctorConfig) -> CheckResult:
187
+ """Check ChromaDB vector memory."""
188
+ chroma_dirs = [
189
+ Path.cwd() / ".praison" / "chroma",
190
+ Path.cwd() / "chroma_db",
191
+ Path.home() / ".praison" / "chroma",
192
+ ]
193
+
194
+ found = None
195
+ for dir_path in chroma_dirs:
196
+ if dir_path.exists():
197
+ found = dir_path
198
+ break
199
+
200
+ if found:
201
+ try:
202
+ import chromadb
203
+ # Try to open the database
204
+ client = chromadb.PersistentClient(path=str(found))
205
+ collections = client.list_collections()
206
+ return CheckResult(
207
+ id="memory_chromadb",
208
+ title="ChromaDB Memory",
209
+ category=CheckCategory.MEMORY,
210
+ status=CheckStatus.PASS,
211
+ message=f"ChromaDB found with {len(collections)} collection(s)",
212
+ metadata={"path": str(found), "collections": len(collections)},
213
+ )
214
+ except ImportError:
215
+ return CheckResult(
216
+ id="memory_chromadb",
217
+ title="ChromaDB Memory",
218
+ category=CheckCategory.MEMORY,
219
+ status=CheckStatus.WARN,
220
+ message="ChromaDB directory found but chromadb not installed",
221
+ remediation="Install with: pip install chromadb",
222
+ )
223
+ except Exception as e:
224
+ return CheckResult(
225
+ id="memory_chromadb",
226
+ title="ChromaDB Memory",
227
+ category=CheckCategory.MEMORY,
228
+ status=CheckStatus.WARN,
229
+ message=f"ChromaDB found but cannot open: {type(e).__name__}",
230
+ details=str(e)[:200],
231
+ )
232
+ else:
233
+ return CheckResult(
234
+ id="memory_chromadb",
235
+ title="ChromaDB Memory",
236
+ category=CheckCategory.MEMORY,
237
+ status=CheckStatus.SKIP,
238
+ message="No ChromaDB storage found (optional)",
239
+ )
240
+
241
+
242
+ @register_check(
243
+ id="memory_praisonai_module",
244
+ title="PraisonAI Memory Module",
245
+ description="Check PraisonAI memory module",
246
+ category=CheckCategory.MEMORY,
247
+ severity=CheckSeverity.LOW,
248
+ )
249
+ def check_memory_praisonai_module(config: DoctorConfig) -> CheckResult:
250
+ """Check PraisonAI memory module."""
251
+ try:
252
+ from praisonaiagents.memory import FileMemory, Memory
253
+ return CheckResult(
254
+ id="memory_praisonai_module",
255
+ title="PraisonAI Memory Module",
256
+ category=CheckCategory.MEMORY,
257
+ status=CheckStatus.PASS,
258
+ message="Memory modules available (FileMemory, Memory)",
259
+ )
260
+ except ImportError as e:
261
+ return CheckResult(
262
+ id="memory_praisonai_module",
263
+ title="PraisonAI Memory Module",
264
+ category=CheckCategory.MEMORY,
265
+ status=CheckStatus.WARN,
266
+ message="Memory module import failed",
267
+ details=str(e),
268
+ )