mdengine 0.8.0__tar.gz → 0.8.1__tar.gz

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 (466) hide show
  1. {mdengine-0.8.0/src/mdengine.egg-info → mdengine-0.8.1}/PKG-INFO +31 -1
  2. {mdengine-0.8.0 → mdengine-0.8.1}/README.md +23 -0
  3. {mdengine-0.8.0 → mdengine-0.8.1}/pyproject.toml +12 -1
  4. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/__init__.py +1 -1
  5. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/analyzers/flow_analyzer.py +28 -9
  6. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/api/job_manager.py +2 -19
  7. mdengine-0.8.1/src/md_generator/codeflow/api/schemas.py +364 -0
  8. mdengine-0.8.1/src/md_generator/codeflow/api/semantic_api.py +69 -0
  9. mdengine-0.8.1/src/md_generator/codeflow/cli/main.py +548 -0
  10. mdengine-0.8.1/src/md_generator/codeflow/config/__init__.py +13 -0
  11. mdengine-0.8.1/src/md_generator/codeflow/config/codeflow_yaml.py +45 -0
  12. mdengine-0.8.1/src/md_generator/codeflow/core/__init__.py +13 -0
  13. mdengine-0.8.1/src/md_generator/codeflow/core/extractor.py +683 -0
  14. mdengine-0.8.1/src/md_generator/codeflow/core/run_config.py +109 -0
  15. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/detectors/entry_detector.py +28 -1
  16. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/detectors/entry_rank.py +4 -4
  17. mdengine-0.8.1/src/md_generator/codeflow/detectors/event_detector.py +126 -0
  18. mdengine-0.8.1/src/md_generator/codeflow/detectors/liferay_portlet_detector.py +196 -0
  19. mdengine-0.8.1/src/md_generator/codeflow/generators/cfg_paths_markdown.py +114 -0
  20. mdengine-0.8.1/src/md_generator/codeflow/generators/cfg_paths_mermaid.py +30 -0
  21. mdengine-0.8.1/src/md_generator/codeflow/generators/cfg_render.py +121 -0
  22. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/generators/cytoscape_enrich.py +6 -3
  23. mdengine-0.8.1/src/md_generator/codeflow/generators/entry_markdown.py +559 -0
  24. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/generators/flow_summary.py +7 -8
  25. mdengine-0.8.1/src/md_generator/codeflow/generators/flow_tree.py +56 -0
  26. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/generators/html.py +1 -0
  27. mdengine-0.8.1/src/md_generator/codeflow/generators/html_unified.py +285 -0
  28. mdengine-0.8.1/src/md_generator/codeflow/generators/markdown.py +133 -0
  29. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/generators/mermaid.py +14 -2
  30. mdengine-0.8.1/src/md_generator/codeflow/graph/__init__.py +6 -0
  31. mdengine-0.8.1/src/md_generator/codeflow/graph/analysis.py +93 -0
  32. mdengine-0.8.1/src/md_generator/codeflow/graph/builder.py +478 -0
  33. mdengine-0.8.1/src/md_generator/codeflow/graph/call_expander.py +160 -0
  34. mdengine-0.8.1/src/md_generator/codeflow/graph/cfg_builder.py +435 -0
  35. mdengine-0.8.1/src/md_generator/codeflow/graph/cfg_copy.py +41 -0
  36. mdengine-0.8.1/src/md_generator/codeflow/graph/cfg_model.py +49 -0
  37. mdengine-0.8.1/src/md_generator/codeflow/graph/clustering.py +178 -0
  38. mdengine-0.8.1/src/md_generator/codeflow/graph/embeddings.py +193 -0
  39. mdengine-0.8.1/src/md_generator/codeflow/graph/enricher.py +74 -0
  40. mdengine-0.8.1/src/md_generator/codeflow/graph/event_graph.py +55 -0
  41. mdengine-0.8.1/src/md_generator/codeflow/graph/export_schema.py +278 -0
  42. mdengine-0.8.1/src/md_generator/codeflow/graph/multigraph_utils.py +106 -0
  43. mdengine-0.8.1/src/md_generator/codeflow/graph/path_enumerator.py +92 -0
  44. mdengine-0.8.1/src/md_generator/codeflow/graph/path_probability.py +64 -0
  45. mdengine-0.8.1/src/md_generator/codeflow/graph/query_engine.py +81 -0
  46. mdengine-0.8.1/src/md_generator/codeflow/graph/relations.py +35 -0
  47. mdengine-0.8.1/src/md_generator/codeflow/graph/runtime_integration.py +32 -0
  48. mdengine-0.8.1/src/md_generator/codeflow/graph/semantic_enricher.py +76 -0
  49. mdengine-0.8.1/src/md_generator/codeflow/graph/semantic_index.py +84 -0
  50. mdengine-0.8.1/src/md_generator/codeflow/graph/sqlite_export.py +56 -0
  51. mdengine-0.8.1/src/md_generator/codeflow/ingestion/git_loader.py +209 -0
  52. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/mcp/server.py +22 -5
  53. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/models/ir.py +30 -0
  54. mdengine-0.8.1/src/md_generator/codeflow/models/ir_cfg.py +42 -0
  55. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/__init__.py +16 -0
  56. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/cpp_adapter.py +229 -0
  57. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/go_adapter.py +15 -0
  58. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/ir_from_dump.py +75 -0
  59. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/java_adapter.py +186 -0
  60. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/php_adapter.py +15 -0
  61. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/python_adapter.py +185 -0
  62. mdengine-0.8.1/src/md_generator/codeflow/parsers/adapters/treesitter_adapter.py +218 -0
  63. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/parsers/cpp_parser.py +84 -3
  64. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/parsers/go_parser.py +26 -4
  65. mdengine-0.8.1/src/md_generator/codeflow/parsers/ir_enrich.py +41 -0
  66. mdengine-0.8.1/src/md_generator/codeflow/parsers/java_parser.py +371 -0
  67. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/parsers/php_parser.py +40 -4
  68. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/parsers/python_parser.py +31 -1
  69. mdengine-0.8.1/src/md_generator/codeflow/parsers/treesitter_js_ts_parser.py +339 -0
  70. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/rules/collector.py +17 -8
  71. mdengine-0.8.1/src/md_generator/codeflow/rules/cpp_rules.py +113 -0
  72. mdengine-0.8.1/src/md_generator/codeflow/rules/java_rules.py +188 -0
  73. mdengine-0.8.1/src/md_generator/codeflow/utils/tools_root.py +16 -0
  74. mdengine-0.8.1/src/md_generator/codeflow/worker/__init__.py +1 -0
  75. mdengine-0.8.1/src/md_generator/codeflow/worker/celery_optional.py +22 -0
  76. {mdengine-0.8.0 → mdengine-0.8.1/src/mdengine.egg-info}/PKG-INFO +31 -1
  77. {mdengine-0.8.0 → mdengine-0.8.1}/src/mdengine.egg-info/SOURCES.txt +44 -0
  78. {mdengine-0.8.0 → mdengine-0.8.1}/src/mdengine.egg-info/requires.txt +9 -0
  79. mdengine-0.8.0/src/md_generator/codeflow/api/schemas.py +0 -96
  80. mdengine-0.8.0/src/md_generator/codeflow/cli/main.py +0 -132
  81. mdengine-0.8.0/src/md_generator/codeflow/core/__init__.py +0 -6
  82. mdengine-0.8.0/src/md_generator/codeflow/core/extractor.py +0 -193
  83. mdengine-0.8.0/src/md_generator/codeflow/core/run_config.py +0 -31
  84. mdengine-0.8.0/src/md_generator/codeflow/generators/entry_markdown.py +0 -198
  85. mdengine-0.8.0/src/md_generator/codeflow/generators/markdown.py +0 -35
  86. mdengine-0.8.0/src/md_generator/codeflow/graph/__init__.py +0 -5
  87. mdengine-0.8.0/src/md_generator/codeflow/graph/builder.py +0 -181
  88. mdengine-0.8.0/src/md_generator/codeflow/parsers/java_parser.py +0 -116
  89. mdengine-0.8.0/src/md_generator/codeflow/parsers/treesitter_js_ts_parser.py +0 -119
  90. mdengine-0.8.0/src/md_generator/codeflow/utils/tools_root.py +0 -13
  91. {mdengine-0.8.0 → mdengine-0.8.1}/LICENSE +0 -0
  92. {mdengine-0.8.0 → mdengine-0.8.1}/setup.cfg +0 -0
  93. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/__init__.py +0 -0
  94. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/__init__.py +0 -0
  95. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/convert_runner.py +0 -0
  96. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/jobs.py +0 -0
  97. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/main.py +0 -0
  98. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/mcp_server.py +0 -0
  99. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/mcp_setup.py +0 -0
  100. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/query_options.py +0 -0
  101. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/api/settings.py +0 -0
  102. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/convert_impl.py +0 -0
  103. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/converter.py +0 -0
  104. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/archive/options.py +0 -0
  105. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/__init__.py +0 -0
  106. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/analyzers/__init__.py +0 -0
  107. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/api/__init__.py +0 -0
  108. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/api/main.py +0 -0
  109. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/api/mcp_server.py +0 -0
  110. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/api/run.py +0 -0
  111. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/api/settings.py +0 -0
  112. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/api/sse.py +0 -0
  113. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/cli/__init__.py +0 -0
  114. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/detectors/__init__.py +0 -0
  115. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/detectors/api_detector.py +0 -0
  116. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/detectors/kafka_detector.py +0 -0
  117. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/generators/__init__.py +0 -0
  118. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/generators/business_rules_markdown.py +0 -0
  119. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/generators/sequence.py +0 -0
  120. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/ingestion/__init__.py +0 -0
  121. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/ingestion/loader.py +0 -0
  122. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/lang_dispatch.py +0 -0
  123. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/mcp/__init__.py +0 -0
  124. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/models/__init__.py +0 -0
  125. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/parsers/__init__.py +0 -0
  126. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/parsers/base.py +0 -0
  127. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/rules/__init__.py +0 -0
  128. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/runtime/__init__.py +0 -0
  129. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/runtime/tracer.py +0 -0
  130. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/codeflow/utils/__init__.py +0 -0
  131. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/__init__.py +0 -0
  132. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/__init__.py +0 -0
  133. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/factory.py +0 -0
  134. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/mongo_adapter.py +0 -0
  135. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/mysql_adapter.py +0 -0
  136. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/oracle_adapter.py +0 -0
  137. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/postgres_adapter.py +0 -0
  138. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/sql_common.py +0 -0
  139. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/adapters/sqlite_adapter.py +0 -0
  140. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/api/__init__.py +0 -0
  141. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/api/main.py +0 -0
  142. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/api/mcp_server.py +0 -0
  143. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/api/run.py +0 -0
  144. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/api/schemas.py +0 -0
  145. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/api/settings.py +0 -0
  146. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/api/sqlite_upload.py +0 -0
  147. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/cli/__init__.py +0 -0
  148. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/cli/main.py +0 -0
  149. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/config/__init__.py +0 -0
  150. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/config/default.yaml +0 -0
  151. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/__init__.py +0 -0
  152. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/base_adapter.py +0 -0
  153. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/__init__.py +0 -0
  154. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/dot_emitter.py +0 -0
  155. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/filter.py +0 -0
  156. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/mermaid_emitter.py +0 -0
  157. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/mermaid_render.py +0 -0
  158. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/model.py +0 -0
  159. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/pipeline.py +0 -0
  160. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/erd/render.py +0 -0
  161. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/extractor.py +0 -0
  162. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/job_manager.py +0 -0
  163. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/markdown_writer.py +0 -0
  164. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/models.py +0 -0
  165. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/run_config.py +0 -0
  166. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/util.py +0 -0
  167. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/core/zip_export.py +0 -0
  168. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/mcp/__init__.py +0 -0
  169. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/mcp/server.py +0 -0
  170. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/mcp/sse.py +0 -0
  171. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/db/mcp/stream.py +0 -0
  172. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/engine_cli.py +0 -0
  173. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/__init__.py +0 -0
  174. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/adapters/__init__.py +0 -0
  175. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/adapters/factory.py +0 -0
  176. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/adapters/neo4j_adapter.py +0 -0
  177. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/adapters/networkx_adapter.py +0 -0
  178. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/api/__init__.py +0 -0
  179. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/api/main.py +0 -0
  180. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/api/mcp_server.py +0 -0
  181. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/api/run.py +0 -0
  182. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/api/schemas.py +0 -0
  183. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/api/settings.py +0 -0
  184. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/cli/__init__.py +0 -0
  185. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/cli/main.py +0 -0
  186. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/config/__init__.py +0 -0
  187. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/config/default.yaml +0 -0
  188. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/__init__.py +0 -0
  189. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/base_adapter.py +0 -0
  190. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/extractor.py +0 -0
  191. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/graph_builder.py +0 -0
  192. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/job_manager.py +0 -0
  193. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/markdown_writer.py +0 -0
  194. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/models.py +0 -0
  195. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/run_config.py +0 -0
  196. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/viz.py +0 -0
  197. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/core/zip_export.py +0 -0
  198. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/mcp/__init__.py +0 -0
  199. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/mcp/server.py +0 -0
  200. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/mcp/sse.py +0 -0
  201. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/graph/mcp/stream.py +0 -0
  202. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/__init__.py +0 -0
  203. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/api/__init__.py +0 -0
  204. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/api/main.py +0 -0
  205. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/api/mcp_server.py +0 -0
  206. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/api/query_options.py +0 -0
  207. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/api/settings.py +0 -0
  208. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/api/staging.py +0 -0
  209. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/api/zip_bundle.py +0 -0
  210. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/backends/__init__.py +0 -0
  211. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/backends/base.py +0 -0
  212. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/backends/easy.py +0 -0
  213. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/backends/paddle.py +0 -0
  214. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/backends/tesseract.py +0 -0
  215. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/convert_impl.py +0 -0
  216. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/converter.py +0 -0
  217. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/emit.py +0 -0
  218. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/io_util.py +0 -0
  219. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/image/utils.py +0 -0
  220. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/__init__.py +0 -0
  221. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/__init__.py +0 -0
  222. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/api/__init__.py +0 -0
  223. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/api/main.py +0 -0
  224. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/api/mcp_server.py +0 -0
  225. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/api/mcp_setup.py +0 -0
  226. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/api/run.py +0 -0
  227. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/api/settings.py +0 -0
  228. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/converter.py +0 -0
  229. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/formatter.py +0 -0
  230. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/audio/service.py +0 -0
  231. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/document_converter.py +0 -0
  232. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/job_store.py +0 -0
  233. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/__init__.py +0 -0
  234. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/api/__init__.py +0 -0
  235. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/api/main.py +0 -0
  236. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/api/mcp_server.py +0 -0
  237. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/api/mcp_setup.py +0 -0
  238. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/api/run.py +0 -0
  239. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/api/settings.py +0 -0
  240. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/converter.py +0 -0
  241. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/formatter.py +0 -0
  242. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/video/service.py +0 -0
  243. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/whisper_language.py +0 -0
  244. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/__init__.py +0 -0
  245. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/api/__init__.py +0 -0
  246. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/api/main.py +0 -0
  247. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/api/mcp_server.py +0 -0
  248. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/api/mcp_setup.py +0 -0
  249. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/api/run.py +0 -0
  250. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/api/settings.py +0 -0
  251. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/converter.py +0 -0
  252. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/formatter.py +0 -0
  253. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/metadata.py +0 -0
  254. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/service.py +0 -0
  255. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/media/youtube/transcript.py +0 -0
  256. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/__init__.py +0 -0
  257. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/api/__init__.py +0 -0
  258. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/api/main.py +0 -0
  259. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/api/mcp_server.py +0 -0
  260. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/api/run.py +0 -0
  261. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/api/schemas.py +0 -0
  262. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/api/settings.py +0 -0
  263. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/cli/__init__.py +0 -0
  264. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/cli/main.py +0 -0
  265. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/config/__init__.py +0 -0
  266. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/config/default.yaml +0 -0
  267. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/converters/__init__.py +0 -0
  268. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/converters/swagger2_to_openapi3.py +0 -0
  269. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/core/__init__.py +0 -0
  270. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/core/extractor.py +0 -0
  271. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/core/run_config.py +0 -0
  272. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/core/zip_export.py +0 -0
  273. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/enrichers/__init__.py +0 -0
  274. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/enrichers/rules.py +0 -0
  275. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/enrichers/sample_body.py +0 -0
  276. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/generators/__init__.py +0 -0
  277. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/generators/dependency_graph.py +0 -0
  278. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/loaders/__init__.py +0 -0
  279. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/loaders/spec_loader.py +0 -0
  280. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/mcp/__init__.py +0 -0
  281. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/mcp/server.py +0 -0
  282. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/models/__init__.py +0 -0
  283. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/models/domain.py +0 -0
  284. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/normalizers/__init__.py +0 -0
  285. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/normalizers/operations.py +0 -0
  286. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/normalizers/schema_flatten.py +0 -0
  287. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/parsers/__init__.py +0 -0
  288. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/parsers/openapi_parser.py +0 -0
  289. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/resolvers/__init__.py +0 -0
  290. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/resolvers/ref_resolver.py +0 -0
  291. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/writers/__init__.py +0 -0
  292. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/openapi/writers/markdown_writer.py +0 -0
  293. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/__init__.py +0 -0
  294. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/api/__init__.py +0 -0
  295. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/api/main.py +0 -0
  296. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/api/mcp_server.py +0 -0
  297. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/api/settings.py +0 -0
  298. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/api/zip_bundle.py +0 -0
  299. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/converter.py +0 -0
  300. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/md_emit.py +0 -0
  301. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/pdf_extract.py +0 -0
  302. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/pdf/utils.py +0 -0
  303. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/__init__.py +0 -0
  304. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/__init__.py +0 -0
  305. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/convert_runner.py +0 -0
  306. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/jobs.py +0 -0
  307. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/main.py +0 -0
  308. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/mcp_server.py +0 -0
  309. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/mcp_setup.py +0 -0
  310. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/run.py +0 -0
  311. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/api/settings.py +0 -0
  312. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/assets.py +0 -0
  313. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/chunker.py +0 -0
  314. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/cli.py +0 -0
  315. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/extractor.py +0 -0
  316. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/html_to_md.py +0 -0
  317. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/options.py +0 -0
  318. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/pipeline.py +0 -0
  319. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/playwright/playwright_fetcher.py +0 -0
  320. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/__init__.py +0 -0
  321. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/__init__.py +0 -0
  322. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/convert_runner.py +0 -0
  323. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/jobs.py +0 -0
  324. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/main.py +0 -0
  325. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/mcp_server.py +0 -0
  326. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/mcp_setup.py +0 -0
  327. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/query_options.py +0 -0
  328. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/api/settings.py +0 -0
  329. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/convert_impl.py +0 -0
  330. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/converter.py +0 -0
  331. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/embedded_extract.py +0 -0
  332. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/ooxml_media.py +0 -0
  333. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/options.py +0 -0
  334. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/post_assets.py +0 -0
  335. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/text_formatting.py +0 -0
  336. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/vendor_pdf_md/__init__.py +0 -0
  337. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/vendor_pdf_md/convert.py +0 -0
  338. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/vendor_word_md/__init__.py +0 -0
  339. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/vendor_word_md/convert.py +0 -0
  340. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/ppt/zip_deep.py +0 -0
  341. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/__init__.py +0 -0
  342. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/__init__.py +0 -0
  343. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/convert_runner.py +0 -0
  344. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/jobs.py +0 -0
  345. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/main.py +0 -0
  346. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/mcp_server.py +0 -0
  347. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/mcp_setup.py +0 -0
  348. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/query_options.py +0 -0
  349. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/api/settings.py +0 -0
  350. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/convert_impl.py +0 -0
  351. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/converter.py +0 -0
  352. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/format_detect.py +0 -0
  353. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/md_emit_json.py +0 -0
  354. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/md_emit_txt.py +0 -0
  355. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/md_emit_xml.py +0 -0
  356. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/text/options.py +0 -0
  357. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/__init__.py +0 -0
  358. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/__init__.py +0 -0
  359. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/adapters/__init__.py +0 -0
  360. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/adapters/claude.py +0 -0
  361. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/adapters/cursor.py +0 -0
  362. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/adapters/openai.py +0 -0
  363. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/agent.py +0 -0
  364. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/bundle.py +0 -0
  365. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/chunks.py +0 -0
  366. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/cli.py +0 -0
  367. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/dependency-graph.json +0 -0
  368. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/registry.json +0 -0
  369. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/global-skill.md +0 -0
  370. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-archive/SKILL.md +0 -0
  371. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-archive/references/example.md +0 -0
  372. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-codeflow/SKILL.md +0 -0
  373. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-codeflow/references/example.md +0 -0
  374. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-db/SKILL.md +0 -0
  375. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-db/references/example.md +0 -0
  376. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-global/SKILL.md +0 -0
  377. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-global/references/example.md +0 -0
  378. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-graph/SKILL.md +0 -0
  379. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-graph/references/example.md +0 -0
  380. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-image/SKILL.md +0 -0
  381. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-image/references/example.md +0 -0
  382. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-media/SKILL.md +0 -0
  383. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-media/references/example.md +0 -0
  384. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-openapi/SKILL.md +0 -0
  385. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-openapi/references/example.md +0 -0
  386. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-pdf/SKILL.md +0 -0
  387. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-pdf/references/example.md +0 -0
  388. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-playwright/SKILL.md +0 -0
  389. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-playwright/references/example.md +0 -0
  390. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-ppt/SKILL.md +0 -0
  391. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-ppt/references/example.md +0 -0
  392. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-text/SKILL.md +0 -0
  393. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-text/references/example.md +0 -0
  394. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-url/SKILL.md +0 -0
  395. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-url/references/example.md +0 -0
  396. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-word/SKILL.md +0 -0
  397. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-word/references/example.md +0 -0
  398. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-xlsx/SKILL.md +0 -0
  399. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-ai-xlsx/references/example.md +0 -0
  400. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-reference/SKILL.md +0 -0
  401. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-reference/references/entrypoints.md +0 -0
  402. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-reference/references/example.md +0 -0
  403. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/mdengine-reference/references/http-api-mcp.md +0 -0
  404. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/archive.md +0 -0
  405. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/codeflow.md +0 -0
  406. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/db.md +0 -0
  407. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/graph.md +0 -0
  408. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/image.md +0 -0
  409. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/media.md +0 -0
  410. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/openapi.md +0 -0
  411. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/pdf.md +0 -0
  412. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/playwright.md +0 -0
  413. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/ppt.md +0 -0
  414. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/text.md +0 -0
  415. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/url.md +0 -0
  416. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/word.md +0 -0
  417. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/data/skills/modules/xlsx.md +0 -0
  418. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/rag.py +0 -0
  419. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/assistant/registry.py +0 -0
  420. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/skill_builder/__init__.py +0 -0
  421. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/skill_builder/__main__.py +0 -0
  422. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/skill_builder/dependency_graph.py +0 -0
  423. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/skill_builder/generate.py +0 -0
  424. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/skill_builder/pyproject_util.py +0 -0
  425. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/tools/skill_builder/routing.py +0 -0
  426. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/__init__.py +0 -0
  427. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/__init__.py +0 -0
  428. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/convert_runner.py +0 -0
  429. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/jobs.py +0 -0
  430. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/main.py +0 -0
  431. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/mcp_server.py +0 -0
  432. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/mcp_setup.py +0 -0
  433. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/query_options.py +0 -0
  434. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/api/settings.py +0 -0
  435. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/assets.py +0 -0
  436. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/convert_impl.py +0 -0
  437. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/converter.py +0 -0
  438. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/crawl.py +0 -0
  439. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/extract.py +0 -0
  440. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/fetch.py +0 -0
  441. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/html_to_md.py +0 -0
  442. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/options.py +0 -0
  443. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/page_convert.py +0 -0
  444. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/post_convert_assets.py +0 -0
  445. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/url/robots_util.py +0 -0
  446. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/__init__.py +0 -0
  447. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/api/__init__.py +0 -0
  448. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/api/convert_util.py +0 -0
  449. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/api/jobs.py +0 -0
  450. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/api/main.py +0 -0
  451. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/api/mcp_server.py +0 -0
  452. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/artifact.py +0 -0
  453. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/converter.py +0 -0
  454. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/word/settings.py +0 -0
  455. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/__init__.py +0 -0
  456. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/api/__init__.py +0 -0
  457. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/api/app.py +0 -0
  458. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/convert_config.py +0 -0
  459. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/converter.py +0 -0
  460. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/converter_core.py +0 -0
  461. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/excel_reader.py +0 -0
  462. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/markdown_emitter.py +0 -0
  463. {mdengine-0.8.0 → mdengine-0.8.1}/src/md_generator/xlsx/mcp_server.py +0 -0
  464. {mdengine-0.8.0 → mdengine-0.8.1}/src/mdengine.egg-info/dependency_links.txt +0 -0
  465. {mdengine-0.8.0 → mdengine-0.8.1}/src/mdengine.egg-info/entry_points.txt +0 -0
  466. {mdengine-0.8.0 → mdengine-0.8.1}/src/mdengine.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdengine
3
- Version: 0.8.0
3
+ Version: 0.8.1
4
4
  Summary: Convert PDF, Office, images, audio, video, text/JSON/XML, ZIP archives, web URLs, databases, graphs, and OpenAPI specs to Markdown.
5
5
  Author: mdengine contributors
6
6
  License-Expression: MIT
@@ -106,6 +106,9 @@ Requires-Dist: openapi-spec-validator>=0.7.0; extra == "openapi"
106
106
  Provides-Extra: codeflow
107
107
  Requires-Dist: networkx>=3.2.0; extra == "codeflow"
108
108
  Requires-Dist: javalang>=0.13.0; extra == "codeflow"
109
+ Provides-Extra: codeflow-worker
110
+ Requires-Dist: celery>=5.3.0; extra == "codeflow-worker"
111
+ Requires-Dist: redis>=5.0.0; extra == "codeflow-worker"
109
112
  Provides-Extra: codeflow-treesitter
110
113
  Requires-Dist: tree-sitter>=0.21.0; extra == "codeflow-treesitter"
111
114
  Requires-Dist: tree-sitter-javascript>=0.21.0; extra == "codeflow-treesitter"
@@ -113,6 +116,10 @@ Requires-Dist: tree-sitter-typescript>=0.23.0; extra == "codeflow-treesitter"
113
116
  Requires-Dist: tree-sitter-cpp>=0.23.0; extra == "codeflow-treesitter"
114
117
  Provides-Extra: codeflow-clang
115
118
  Requires-Dist: clang>=16.0.0; extra == "codeflow-clang"
119
+ Provides-Extra: codeflow-semantic
120
+ Requires-Dist: numpy>=1.24.0; extra == "codeflow-semantic"
121
+ Requires-Dist: scikit-learn>=1.3.0; extra == "codeflow-semantic"
122
+ Requires-Dist: sentence-transformers>=3.0.0; extra == "codeflow-semantic"
116
123
  Provides-Extra: api
117
124
  Requires-Dist: fastapi>=0.115.0; extra == "api"
118
125
  Requires-Dist: uvicorn[standard]>=0.32.0; extra == "api"
@@ -876,6 +883,29 @@ python -m pytest
876
883
 
877
884
  Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/tests/`), **`graph-to-md/tests/`**, **`openapi-to-md/tests/`**, and **[`tool-assistant/tests/`](tool-assistant/tests/)** for the skill SDK; `pyproject.toml` sets `pythonpath = ["src"]` so **`md_generator`** (including **`md_generator.tools.assistant`**) resolves without a manual `PYTHONPATH` when you use `pytest` from the config.
878
885
 
886
+ ### Codeflow (`md_generator.codeflow`)
887
+
888
+ Static call graphs and per-entry Markdown/Mermaid/JSON for **Python**, **Java** (Spring REST, Kafka listeners, **Liferay / JSR-286 portlets**), and other parsers. Output is **static only** (`graph-full.json` can include unresolved `unknown::*` callees).
889
+
890
+ More detail: **[`codeflow-to-md/docs/graph-and-outputs.md`](codeflow-to-md/docs/graph-and-outputs.md)** (node/edge model, `graph-schema.json`, `methods/` layout).
891
+
892
+ - **Formats:** pass **`--formats md,mermaid,json,html`** (comma-separated). **`--output`** is the **output directory**, not the format list.
893
+ - **Graph schema export:** optional **`--emit-graph-schema`** (with `json` in formats) writes **`graph-schema.json`** — stable Node/Edge view with derived File/Class nodes and `CONTAINS` edges. **`graph-full.json`** stays unchanged for existing consumers.
894
+ - **Markdown intelligence:** flow and entry Markdown include **Called By** and **Impact** lists (capped with **`--intelligence-list-cap`**, default 80).
895
+ - **IR / CFG (optional):** **`--emit-cfg`** builds a language-agnostic IR and writes **`cfg.json`**, **`cfg.mmd`**, and appends a CFG Mermaid block to **`flow.md`** (when `md` is in formats). Cap with **`--cfg-max-nodes`** (default 500).
896
+ - **Entries:** use `--entry` (comma-separated symbol ids), **`--entries-file`** (one id per line, `#` comments allowed), or rely on detectors. **`--emit-entry-per-method`** emits one output directory per method/entry symbol under **`methods/<slug>/`**; cap volume with **`--emit-entry-max`** (default cap **10000** when the flag is set and max is omitted). **`--entry-fallback`** `none` \| `roots` \| `first_n` controls behavior when nothing is detected (default **`roots`**, in-degree 0 symbols, limited by **`--entry-fallback-max`**).
897
+ - **Java flow:** edges can record **branch context** from enclosing `if`, `switch`, or ternary expressions (`condition` / `labels` on edges). **Business rules** Markdown picks up validation-style **`@NotNull`** / **`@Size`** / **`@Valid`** (name heuristics) and throws such as **`IllegalArgumentException`**.
898
+ - **JavaScript / TypeScript / TSX:** Tree-sitter adds **`condition_label`** from enclosing control flow, **`fr.branches`** for `if` / `switch`, and **`fr.rules`** for **`throw`** and common **`assert` / `console.assert`** calls (slice-scoped in the collector).
899
+ - **C++ (tree-sitter path):** **`condition_label`** on calls, **`fr.branches`** for `if` / `switch`, plus **`rules/cpp_rules.py`** for **`throw`** / **`assert`** when methods appear in the slice. (Clang-only parses still get throw/assert via the same extractor when tree-sitter is installed.)
900
+ - **Go:** the **`codeflow_go_dump`** helper emits **`condition`** on calls inside `if` / `switch` / `for` / `range`, and **`rules`** for **`panic`** / **`log.Fatal` / `Fatalf`**; **`fr.rules`** are merged slice-scoped.
901
+ - **PHP:** the **`codeflow_php_dump`** helper adds **`condition`** on calls (from enclosing **`if` / `case`**), **`rules`** for **`throw`**, and **`branches`** for documentation. Install PHP + Composer deps under `codeflow-to-md/examples/codeflow_php_dump` (or mirror under `tools/codeflow_php_dump`) for PHP scans.
902
+ - **Kinds / filters:** `--include` accepts aliases such as **`portlet`** / **`liferay`** for portlet entries.
903
+ - **Operators:** each run writes **`scan-summary.md`** at the output root (counts, warnings, fallback mode) unless **`--no-scan-summary`** is set.
904
+
905
+ ```bash
906
+ python -m md_generator.codeflow.cli.main scan path/to/src --output ./codeflow-out --lang java
907
+ ```
908
+
879
909
  ---
880
910
 
881
911
  ## Repository layout
@@ -705,6 +705,29 @@ python -m pytest
705
705
 
706
706
  Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/tests/`), **`graph-to-md/tests/`**, **`openapi-to-md/tests/`**, and **[`tool-assistant/tests/`](tool-assistant/tests/)** for the skill SDK; `pyproject.toml` sets `pythonpath = ["src"]` so **`md_generator`** (including **`md_generator.tools.assistant`**) resolves without a manual `PYTHONPATH` when you use `pytest` from the config.
707
707
 
708
+ ### Codeflow (`md_generator.codeflow`)
709
+
710
+ Static call graphs and per-entry Markdown/Mermaid/JSON for **Python**, **Java** (Spring REST, Kafka listeners, **Liferay / JSR-286 portlets**), and other parsers. Output is **static only** (`graph-full.json` can include unresolved `unknown::*` callees).
711
+
712
+ More detail: **[`codeflow-to-md/docs/graph-and-outputs.md`](codeflow-to-md/docs/graph-and-outputs.md)** (node/edge model, `graph-schema.json`, `methods/` layout).
713
+
714
+ - **Formats:** pass **`--formats md,mermaid,json,html`** (comma-separated). **`--output`** is the **output directory**, not the format list.
715
+ - **Graph schema export:** optional **`--emit-graph-schema`** (with `json` in formats) writes **`graph-schema.json`** — stable Node/Edge view with derived File/Class nodes and `CONTAINS` edges. **`graph-full.json`** stays unchanged for existing consumers.
716
+ - **Markdown intelligence:** flow and entry Markdown include **Called By** and **Impact** lists (capped with **`--intelligence-list-cap`**, default 80).
717
+ - **IR / CFG (optional):** **`--emit-cfg`** builds a language-agnostic IR and writes **`cfg.json`**, **`cfg.mmd`**, and appends a CFG Mermaid block to **`flow.md`** (when `md` is in formats). Cap with **`--cfg-max-nodes`** (default 500).
718
+ - **Entries:** use `--entry` (comma-separated symbol ids), **`--entries-file`** (one id per line, `#` comments allowed), or rely on detectors. **`--emit-entry-per-method`** emits one output directory per method/entry symbol under **`methods/<slug>/`**; cap volume with **`--emit-entry-max`** (default cap **10000** when the flag is set and max is omitted). **`--entry-fallback`** `none` \| `roots` \| `first_n` controls behavior when nothing is detected (default **`roots`**, in-degree 0 symbols, limited by **`--entry-fallback-max`**).
719
+ - **Java flow:** edges can record **branch context** from enclosing `if`, `switch`, or ternary expressions (`condition` / `labels` on edges). **Business rules** Markdown picks up validation-style **`@NotNull`** / **`@Size`** / **`@Valid`** (name heuristics) and throws such as **`IllegalArgumentException`**.
720
+ - **JavaScript / TypeScript / TSX:** Tree-sitter adds **`condition_label`** from enclosing control flow, **`fr.branches`** for `if` / `switch`, and **`fr.rules`** for **`throw`** and common **`assert` / `console.assert`** calls (slice-scoped in the collector).
721
+ - **C++ (tree-sitter path):** **`condition_label`** on calls, **`fr.branches`** for `if` / `switch`, plus **`rules/cpp_rules.py`** for **`throw`** / **`assert`** when methods appear in the slice. (Clang-only parses still get throw/assert via the same extractor when tree-sitter is installed.)
722
+ - **Go:** the **`codeflow_go_dump`** helper emits **`condition`** on calls inside `if` / `switch` / `for` / `range`, and **`rules`** for **`panic`** / **`log.Fatal` / `Fatalf`**; **`fr.rules`** are merged slice-scoped.
723
+ - **PHP:** the **`codeflow_php_dump`** helper adds **`condition`** on calls (from enclosing **`if` / `case`**), **`rules`** for **`throw`**, and **`branches`** for documentation. Install PHP + Composer deps under `codeflow-to-md/examples/codeflow_php_dump` (or mirror under `tools/codeflow_php_dump`) for PHP scans.
724
+ - **Kinds / filters:** `--include` accepts aliases such as **`portlet`** / **`liferay`** for portlet entries.
725
+ - **Operators:** each run writes **`scan-summary.md`** at the output root (counts, warnings, fallback mode) unless **`--no-scan-summary`** is set.
726
+
727
+ ```bash
728
+ python -m md_generator.codeflow.cli.main scan path/to/src --output ./codeflow-out --lang java
729
+ ```
730
+
708
731
  ---
709
732
 
710
733
  ## Repository layout
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mdengine"
7
- version = "0.8.0"
7
+ version = "0.8.1"
8
8
  description = "Convert PDF, Office, images, audio, video, text/JSON/XML, ZIP archives, web URLs, databases, graphs, and OpenAPI specs to Markdown."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -115,6 +115,11 @@ codeflow = [
115
115
  "networkx>=3.2.0",
116
116
  "javalang>=0.13.0",
117
117
  ]
118
+ # Optional: horizontal workers for codeflow API (Celery + Redis). Default job path uses threads.
119
+ codeflow-worker = [
120
+ "celery>=5.3.0",
121
+ "redis>=5.0.0",
122
+ ]
118
123
  codeflow-treesitter = [
119
124
  "tree-sitter>=0.21.0",
120
125
  "tree-sitter-javascript>=0.21.0",
@@ -124,6 +129,12 @@ codeflow-treesitter = [
124
129
  codeflow-clang = [
125
130
  "clang>=16.0.0",
126
131
  ]
132
+ # Embeddings + semantic clustering (SentenceTransformers pulls torch; large install).
133
+ codeflow-semantic = [
134
+ "numpy>=1.24.0",
135
+ "scikit-learn>=1.3.0",
136
+ "sentence-transformers>=3.0.0",
137
+ ]
127
138
  api = [
128
139
  "fastapi>=0.115.0",
129
140
  "uvicorn[standard]>=0.32.0",
@@ -1,5 +1,5 @@
1
1
  """md_generator: PDF, Office, image, text, and ZIP to Markdown conversion."""
2
2
 
3
- __version__ = "0.8.0"
3
+ __version__ = "0.8.1"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -4,6 +4,9 @@ from dataclasses import dataclass, field
4
4
 
5
5
  import networkx as nx
6
6
 
7
+ from md_generator.codeflow.graph import relations as rel
8
+ from md_generator.codeflow.graph.multigraph_utils import CodeflowGraph, collapsed_digraph_for_relations
9
+
7
10
 
8
11
  @dataclass(slots=True)
9
12
  class FlowSlice:
@@ -15,16 +18,25 @@ class FlowSlice:
15
18
  truncated: bool = False
16
19
 
17
20
 
21
+ _DEFAULT_FLOW_RELATIONS = frozenset({rel.REL_CALLS})
22
+
23
+
18
24
  def walk_with_depth(
19
- g: nx.DiGraph,
25
+ g: CodeflowGraph,
20
26
  start: str,
21
27
  max_depth: int,
28
+ *,
29
+ relations: frozenset[str] | None = None,
22
30
  ) -> tuple[set[str], list[tuple[str, str, dict]], set[str], bool]:
23
- """Reachability from start within ``max_depth`` hops (shortest-path layering)."""
31
+ """Reachability from ``start`` within ``max_depth`` hops over selected relations (default: CALLS only)."""
24
32
  if start not in g:
25
33
  return {start}, [], set(), False
34
+ rels = relations if relations is not None else _DEFAULT_FLOW_RELATIONS
35
+ cg = collapsed_digraph_for_relations(g, rels)
36
+ if start not in cg:
37
+ return {start}, [], set(), False
26
38
  try:
27
- plen = nx.single_source_shortest_path_length(g, start, cutoff=max_depth)
39
+ plen = nx.single_source_shortest_path_length(cg, start, cutoff=max_depth)
28
40
  except nx.NetworkXError:
29
41
  plen = {start: 0}
30
42
 
@@ -36,13 +48,18 @@ def walk_with_depth(
36
48
  for u in nodes:
37
49
  du = plen[u]
38
50
  if du >= max_depth:
39
- succ = list(g.successors(u))
51
+ succ = list(cg.successors(u))
40
52
  if succ:
41
53
  truncated = True
42
54
  continue
43
- for v in g.successors(u):
44
- ed = dict(g.edges[u, v])
45
- edges_out.append((u, v, ed))
55
+ for v in cg.successors(u):
56
+ if not g.has_edge(u, v):
57
+ continue
58
+ for _k, ed in g[u][v].items():
59
+ er = ed.get("relation") or ed.get("kind") or rel.REL_CALLS
60
+ if er not in rels:
61
+ continue
62
+ edges_out.append((u, v, dict(ed)))
46
63
  dv = plen.get(v)
47
64
  if dv is not None and dv <= du:
48
65
  cycle_guess.add(v)
@@ -53,11 +70,13 @@ def walk_with_depth(
53
70
 
54
71
 
55
72
  def slice_from_entry(
56
- g: nx.DiGraph,
73
+ g: CodeflowGraph,
57
74
  entry_id: str,
58
75
  max_depth: int,
76
+ *,
77
+ relations: frozenset[str] | None = None,
59
78
  ) -> FlowSlice:
60
- nodes, edges, cycles, truncated = walk_with_depth(g, entry_id, max_depth)
79
+ nodes, edges, cycles, truncated = walk_with_depth(g, entry_id, max_depth, relations=relations)
61
80
  return FlowSlice(
62
81
  entry_id=entry_id,
63
82
  nodes=nodes,
@@ -7,7 +7,7 @@ import threading
7
7
  import time
8
8
  import uuid
9
9
  import zipfile
10
- from dataclasses import dataclass
10
+ from dataclasses import dataclass, replace
11
11
  from pathlib import Path
12
12
  from typing import Any
13
13
 
@@ -101,24 +101,7 @@ class CodeflowJobManager:
101
101
  zf.extractall(src)
102
102
 
103
103
  out = ws / "out"
104
- cfg = ScanConfig(
105
- project_root=src,
106
- output_path=out,
107
- formats=cfg_template.formats,
108
- depth=cfg_template.depth,
109
- languages=cfg_template.languages,
110
- entry=cfg_template.entry,
111
- include=cfg_template.include,
112
- exclude=cfg_template.exclude,
113
- include_internal=cfg_template.include_internal,
114
- async_mode=cfg_template.async_mode,
115
- jobs=True,
116
- runtime=cfg_template.runtime,
117
- paths_override=cfg_template.paths_override,
118
- business_rules=cfg_template.business_rules,
119
- business_rules_sql=cfg_template.business_rules_sql,
120
- business_rules_combined=cfg_template.business_rules_combined,
121
- )
104
+ cfg = replace(cfg_template, project_root=src, output_path=out, jobs=True)
122
105
  now = time.time()
123
106
  cfg_dump = json.dumps(scan_config_dump(cfg), sort_keys=True)
124
107
  assert self._conn is not None
@@ -0,0 +1,364 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any, Literal
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+ from md_generator.codeflow.core.run_config import ScanConfig
10
+
11
+
12
+ class AnalyzeOptions(BaseModel):
13
+ formats: str | None = Field(default=None, description="Comma-separated: md,html,mermaid,json")
14
+ depth: int = 5
15
+ languages: str = "mixed"
16
+ entry: str | None = None
17
+ include: str | None = None
18
+ exclude: str | None = None
19
+ business_rules: bool | None = Field(default=None, description="If set, enable/disable business rule MD")
20
+ business_rules_sql: bool | None = Field(default=None, description="If true, scan *.sql for triggers")
21
+ business_rules_combined: bool | None = Field(default=None, description="If set, control entry.combined.md")
22
+ entry_fallback: Literal["none", "roots", "first_n"] | None = Field(
23
+ default=None,
24
+ description="none | roots | first_n when no detected entries",
25
+ )
26
+ entry_fallback_max: int | None = None
27
+ emit_entry_per_method: bool | None = None
28
+ emit_entry_max: int | None = None
29
+ emit_entry_filter: str | None = None
30
+ entries_file: str | None = Field(default=None, description="Path to file of symbol ids (workspace-relative or absolute)")
31
+ write_scan_summary: bool | None = Field(default=None, description="If false, skip scan-summary.md")
32
+ liferay_portlet_base_classes: str | None = Field(
33
+ default=None,
34
+ description="Comma-separated extra portlet superclass simple names",
35
+ )
36
+ codeflow_config_path: str | None = Field(default=None, description="Path to codeflow.yaml")
37
+ emit_flow_tree_json: bool | None = None
38
+ verbose: bool | None = None
39
+ emit_graph_schema: bool | None = None
40
+ intelligence_list_cap: int | None = None
41
+ emit_cfg: bool | None = None
42
+ cfg_max_nodes: int | None = None
43
+ cfg_inline_calls: bool | None = None
44
+ cfg_call_depth: int | None = None
45
+ cfg_max_paths: int | None = None
46
+ cfg_path_max_depth: int | None = None
47
+ cfg_loop_visits: int | None = None
48
+ cfg_probability: bool | None = None
49
+ cfg_mermaid_probabilities: bool | None = None
50
+ cfg_runtime_trace: str | None = None
51
+ cfg_loop_repeat_prob: float | None = None
52
+ graph_include_structural: bool | None = None
53
+ intelligence_transitive_callers: bool | None = None
54
+ emit_system_graph_stats: bool | None = None
55
+ emit_graph_sqlite: bool | None = None
56
+ emit_graph_communities: bool | None = None
57
+ emit_llm_entry_sidecar: bool | None = None
58
+ include_references: bool | None = None
59
+ include_events: bool | None = None
60
+ cfg_ir_go: bool | None = None
61
+ cfg_ir_php: bool | None = None
62
+ cfg_ir_cpp: bool | None = None
63
+ flow_include_event_edges: bool | None = None
64
+ flow_include_reference_edges: bool | None = None
65
+ event_impact: bool | None = None
66
+ cluster_mode: Literal["file_imports", "structural", "semantic", "hybrid"] | None = None
67
+ graph_query: str | None = None
68
+ enable_embeddings: bool | None = None
69
+ embedding_model: str | None = None
70
+ embedding_max_nodes: int | None = None
71
+ embedding_k_clusters: int | None = None
72
+ semantic_top_k: int | None = None
73
+ semantic_search: str | None = None
74
+ emit_html_unified: bool | None = None
75
+
76
+
77
+ def options_to_scan_config(workspace_src: Path, output_subdir: str, raw: AnalyzeOptions | None) -> ScanConfig:
78
+ fmts = ("md", "mermaid", "json")
79
+ if raw and raw.formats:
80
+ fmts = tuple(x.strip().lower() for x in raw.formats.split(",") if x.strip())
81
+ entry_list = None
82
+ if raw and raw.entry:
83
+ entry_list = [x.strip() for x in raw.entry.split(",") if x.strip()]
84
+ br = True if not raw or raw.business_rules is None else raw.business_rules
85
+ br_sql = False if not raw or raw.business_rules_sql is None else raw.business_rules_sql
86
+ br_comb = True if not raw or raw.business_rules_combined is None else raw.business_rules_combined
87
+ ef = "roots" if not raw or raw.entry_fallback is None else raw.entry_fallback
88
+ efm = 20 if not raw or raw.entry_fallback_max is None else int(raw.entry_fallback_max)
89
+ epm = False if not raw or raw.emit_entry_per_method is None else bool(raw.emit_entry_per_method)
90
+ emx = None if not raw else raw.emit_entry_max
91
+ efilt = None if not raw else raw.emit_entry_filter
92
+ efpath = None
93
+ if raw and raw.entries_file:
94
+ efpath = Path(raw.entries_file)
95
+ if not efpath.is_absolute():
96
+ efpath = (workspace_src / efpath).resolve()
97
+ else:
98
+ efpath = efpath.resolve()
99
+ wss = True if not raw or raw.write_scan_summary is None else bool(raw.write_scan_summary)
100
+ lpbc: tuple[str, ...] = ()
101
+ if raw and raw.liferay_portlet_base_classes and str(raw.liferay_portlet_base_classes).strip():
102
+ lpbc = tuple(x.strip() for x in str(raw.liferay_portlet_base_classes).split(",") if x.strip())
103
+ ccpath = None
104
+ if raw and raw.codeflow_config_path and str(raw.codeflow_config_path).strip():
105
+ p = Path(raw.codeflow_config_path.strip())
106
+ ccpath = p if p.is_absolute() else (workspace_src / p).resolve()
107
+ eftj = False if not raw or raw.emit_flow_tree_json is None else bool(raw.emit_flow_tree_json)
108
+ verb = False if not raw or raw.verbose is None else bool(raw.verbose)
109
+ egs = False if not raw or raw.emit_graph_schema is None else bool(raw.emit_graph_schema)
110
+ ilc = 80 if not raw or raw.intelligence_list_cap is None else int(raw.intelligence_list_cap)
111
+ ecfg = False if not raw or raw.emit_cfg is None else bool(raw.emit_cfg)
112
+ cmn = 500 if not raw or raw.cfg_max_nodes is None else int(raw.cfg_max_nodes)
113
+ cic = False if not raw or raw.cfg_inline_calls is None else bool(raw.cfg_inline_calls)
114
+ ccd = 3 if not raw or raw.cfg_call_depth is None else int(raw.cfg_call_depth)
115
+ cmpaths = 100 if not raw or raw.cfg_max_paths is None else int(raw.cfg_max_paths)
116
+ cpmd = 1000 if not raw or raw.cfg_path_max_depth is None else int(raw.cfg_path_max_depth)
117
+ clv = 2 if not raw or raw.cfg_loop_visits is None else int(raw.cfg_loop_visits)
118
+ cprob = False if not raw or raw.cfg_probability is None else bool(raw.cfg_probability)
119
+ cmmdp = False if not raw or raw.cfg_mermaid_probabilities is None else bool(raw.cfg_mermaid_probabilities)
120
+ crt = None
121
+ if raw and raw.cfg_runtime_trace and str(raw.cfg_runtime_trace).strip():
122
+ crt = Path(str(raw.cfg_runtime_trace).strip())
123
+ if not crt.is_absolute():
124
+ crt = (workspace_src / crt).resolve()
125
+ else:
126
+ crt = crt.resolve()
127
+ clrp = 0.6 if not raw or raw.cfg_loop_repeat_prob is None else float(raw.cfg_loop_repeat_prob)
128
+ gis = False if not raw or raw.graph_include_structural is None else bool(raw.graph_include_structural)
129
+ itc = False if not raw or raw.intelligence_transitive_callers is None else bool(raw.intelligence_transitive_callers)
130
+ esgs = False if not raw or raw.emit_system_graph_stats is None else bool(raw.emit_system_graph_stats)
131
+ egsql = False if not raw or raw.emit_graph_sqlite is None else bool(raw.emit_graph_sqlite)
132
+ egc = False if not raw or raw.emit_graph_communities is None else bool(raw.emit_graph_communities)
133
+ ellm = False if not raw or raw.emit_llm_entry_sidecar is None else bool(raw.emit_llm_entry_sidecar)
134
+ iref = False if not raw or raw.include_references is None else bool(raw.include_references)
135
+ ievt = False if not raw or raw.include_events is None else bool(raw.include_events)
136
+ cgo = True if not raw or raw.cfg_ir_go is None else bool(raw.cfg_ir_go)
137
+ cphp = True if not raw or raw.cfg_ir_php is None else bool(raw.cfg_ir_php)
138
+ ccpp = True if not raw or raw.cfg_ir_cpp is None else bool(raw.cfg_ir_cpp)
139
+ fie = False if not raw or raw.flow_include_event_edges is None else bool(raw.flow_include_event_edges)
140
+ fir = False if not raw or raw.flow_include_reference_edges is None else bool(raw.flow_include_reference_edges)
141
+ eimp = False if not raw or raw.event_impact is None else bool(raw.event_impact)
142
+ cm = "file_imports" if not raw or raw.cluster_mode is None else raw.cluster_mode
143
+ gq = None if not raw or raw.graph_query is None or not str(raw.graph_query).strip() else str(raw.graph_query).strip()
144
+ eemb = False if not raw or raw.enable_embeddings is None else bool(raw.enable_embeddings)
145
+ emod = "all-MiniLM-L6-v2" if not raw or raw.embedding_model is None else str(raw.embedding_model)
146
+ emax = 5000 if not raw or raw.embedding_max_nodes is None else int(raw.embedding_max_nodes)
147
+ ekc = 8 if not raw or raw.embedding_k_clusters is None else int(raw.embedding_k_clusters)
148
+ stk = 10 if not raw or raw.semantic_top_k is None else int(raw.semantic_top_k)
149
+ ssr = None if not raw or raw.semantic_search is None or not str(raw.semantic_search).strip() else str(raw.semantic_search).strip()
150
+ ehu = False if not raw or raw.emit_html_unified is None else bool(raw.emit_html_unified)
151
+ return ScanConfig(
152
+ project_root=workspace_src,
153
+ output_path=workspace_src.parent / output_subdir,
154
+ formats=fmts,
155
+ depth=(raw.depth if raw else 5),
156
+ languages=(raw.languages if raw else "mixed"),
157
+ entry=entry_list,
158
+ include=raw.include if raw else None,
159
+ exclude=raw.exclude if raw else None,
160
+ business_rules=br,
161
+ business_rules_sql=br_sql,
162
+ business_rules_combined=br_comb,
163
+ entry_fallback=ef, # type: ignore[arg-type]
164
+ entry_fallback_max=efm,
165
+ emit_entry_per_method=epm,
166
+ emit_entry_max=emx,
167
+ emit_entry_filter=efilt,
168
+ entries_file=efpath,
169
+ write_scan_summary=wss,
170
+ liferay_portlet_base_classes=lpbc,
171
+ codeflow_config_path=ccpath,
172
+ emit_flow_tree_json=eftj,
173
+ verbose=verb,
174
+ emit_graph_schema=egs,
175
+ intelligence_list_cap=ilc,
176
+ emit_cfg=ecfg,
177
+ cfg_max_nodes=cmn,
178
+ cfg_inline_calls=cic,
179
+ cfg_call_depth=ccd,
180
+ cfg_max_paths=cmpaths,
181
+ cfg_path_max_depth=cpmd,
182
+ cfg_loop_visits=clv,
183
+ cfg_probability=cprob,
184
+ cfg_mermaid_probabilities=cmmdp,
185
+ cfg_runtime_trace=crt,
186
+ cfg_loop_repeat_prob=clrp,
187
+ graph_include_structural=gis,
188
+ include_references=iref,
189
+ include_events=ievt,
190
+ cfg_ir_go=cgo,
191
+ cfg_ir_php=cphp,
192
+ cfg_ir_cpp=ccpp,
193
+ flow_include_event_edges=fie,
194
+ flow_include_reference_edges=fir,
195
+ event_impact=eimp,
196
+ cluster_mode=cm, # type: ignore[arg-type]
197
+ graph_query=gq,
198
+ enable_embeddings=eemb,
199
+ embedding_model=emod,
200
+ embedding_max_nodes=emax,
201
+ embedding_k_clusters=ekc,
202
+ semantic_top_k=stk,
203
+ semantic_search=ssr,
204
+ emit_html_unified=ehu,
205
+ intelligence_transitive_callers=itc,
206
+ emit_system_graph_stats=esgs,
207
+ emit_graph_sqlite=egsql,
208
+ emit_graph_communities=egc,
209
+ emit_llm_entry_sidecar=ellm,
210
+ )
211
+
212
+
213
+ def merge_upload_options_json(options_json: str | None) -> AnalyzeOptions | None:
214
+ if not options_json:
215
+ return None
216
+ data = json.loads(options_json)
217
+ return AnalyzeOptions.model_validate(data)
218
+
219
+
220
+ def scan_config_dump(cfg: ScanConfig) -> dict[str, Any]:
221
+ return {
222
+ "project_root": str(cfg.project_root),
223
+ "output_path": str(cfg.output_path),
224
+ "paths_override": [str(p) for p in cfg.paths_override] if cfg.paths_override else None,
225
+ "formats": list(cfg.formats),
226
+ "depth": cfg.depth,
227
+ "languages": cfg.languages,
228
+ "entry": cfg.entry,
229
+ "include": cfg.include,
230
+ "exclude": cfg.exclude,
231
+ "include_internal": cfg.include_internal,
232
+ "async_mode": cfg.async_mode,
233
+ "jobs": cfg.jobs,
234
+ "runtime": cfg.runtime,
235
+ "business_rules": cfg.business_rules,
236
+ "business_rules_sql": cfg.business_rules_sql,
237
+ "business_rules_combined": cfg.business_rules_combined,
238
+ "entry_fallback": cfg.entry_fallback,
239
+ "entry_fallback_max": cfg.entry_fallback_max,
240
+ "emit_entry_per_method": cfg.emit_entry_per_method,
241
+ "emit_entry_max": cfg.emit_entry_max,
242
+ "emit_entry_filter": cfg.emit_entry_filter,
243
+ "entries_file": str(cfg.entries_file) if cfg.entries_file else None,
244
+ "write_scan_summary": cfg.write_scan_summary,
245
+ "liferay_portlet_base_classes": list(cfg.liferay_portlet_base_classes),
246
+ "codeflow_config_path": str(cfg.codeflow_config_path) if cfg.codeflow_config_path else None,
247
+ "emit_flow_tree_json": cfg.emit_flow_tree_json,
248
+ "verbose": cfg.verbose,
249
+ "emit_graph_schema": cfg.emit_graph_schema,
250
+ "intelligence_list_cap": cfg.intelligence_list_cap,
251
+ "emit_cfg": cfg.emit_cfg,
252
+ "cfg_max_nodes": cfg.cfg_max_nodes,
253
+ "cfg_inline_calls": cfg.cfg_inline_calls,
254
+ "cfg_call_depth": cfg.cfg_call_depth,
255
+ "cfg_max_paths": cfg.cfg_max_paths,
256
+ "cfg_path_max_depth": cfg.cfg_path_max_depth,
257
+ "cfg_loop_visits": cfg.cfg_loop_visits,
258
+ "cfg_probability": cfg.cfg_probability,
259
+ "cfg_mermaid_probabilities": cfg.cfg_mermaid_probabilities,
260
+ "cfg_runtime_trace": str(cfg.cfg_runtime_trace) if cfg.cfg_runtime_trace else None,
261
+ "cfg_loop_repeat_prob": cfg.cfg_loop_repeat_prob,
262
+ "graph_include_structural": cfg.graph_include_structural,
263
+ "intelligence_transitive_callers": cfg.intelligence_transitive_callers,
264
+ "emit_system_graph_stats": cfg.emit_system_graph_stats,
265
+ "emit_graph_sqlite": cfg.emit_graph_sqlite,
266
+ "emit_graph_communities": cfg.emit_graph_communities,
267
+ "emit_llm_entry_sidecar": cfg.emit_llm_entry_sidecar,
268
+ "include_references": cfg.include_references,
269
+ "include_events": cfg.include_events,
270
+ "cfg_ir_go": cfg.cfg_ir_go,
271
+ "cfg_ir_php": cfg.cfg_ir_php,
272
+ "cfg_ir_cpp": cfg.cfg_ir_cpp,
273
+ "flow_include_event_edges": cfg.flow_include_event_edges,
274
+ "flow_include_reference_edges": cfg.flow_include_reference_edges,
275
+ "event_impact": cfg.event_impact,
276
+ "cluster_mode": cfg.cluster_mode,
277
+ "graph_query": cfg.graph_query,
278
+ "enable_embeddings": cfg.enable_embeddings,
279
+ "embedding_model": cfg.embedding_model,
280
+ "embedding_max_nodes": cfg.embedding_max_nodes,
281
+ "embedding_k_clusters": cfg.embedding_k_clusters,
282
+ "semantic_top_k": cfg.semantic_top_k,
283
+ "semantic_search": cfg.semantic_search,
284
+ "emit_html_unified": cfg.emit_html_unified,
285
+ }
286
+
287
+
288
+ def scan_config_load(data: dict[str, Any]) -> ScanConfig:
289
+ po = data.get("paths_override")
290
+ return ScanConfig(
291
+ project_root=Path(data["project_root"]),
292
+ output_path=Path(data["output_path"]),
293
+ paths_override=[Path(p) for p in po] if po else None,
294
+ formats=tuple(data.get("formats") or ("md", "mermaid", "json")),
295
+ depth=int(data.get("depth", 5)),
296
+ languages=str(data.get("languages", "mixed")),
297
+ entry=list(data["entry"]) if data.get("entry") else None,
298
+ include=data.get("include"),
299
+ exclude=data.get("exclude"),
300
+ include_internal=bool(data.get("include_internal", True)),
301
+ async_mode=bool(data.get("async_mode", True)),
302
+ jobs=bool(data.get("jobs", False)),
303
+ runtime=bool(data.get("runtime", False)),
304
+ business_rules=bool(data.get("business_rules", True)),
305
+ business_rules_sql=bool(data.get("business_rules_sql", False)),
306
+ business_rules_combined=bool(data.get("business_rules_combined", True)),
307
+ entry_fallback=data.get("entry_fallback", "roots"), # type: ignore[arg-type]
308
+ entry_fallback_max=int(data.get("entry_fallback_max", 20)),
309
+ emit_entry_per_method=bool(data.get("emit_entry_per_method", False)),
310
+ emit_entry_max=data.get("emit_entry_max"),
311
+ emit_entry_filter=data.get("emit_entry_filter"),
312
+ entries_file=Path(data["entries_file"]) if data.get("entries_file") else None,
313
+ write_scan_summary=bool(data.get("write_scan_summary", True)),
314
+ liferay_portlet_base_classes=_load_liferay_bases(data.get("liferay_portlet_base_classes")),
315
+ codeflow_config_path=Path(data["codeflow_config_path"]) if data.get("codeflow_config_path") else None,
316
+ emit_flow_tree_json=bool(data.get("emit_flow_tree_json", False)),
317
+ verbose=bool(data.get("verbose", False)),
318
+ emit_graph_schema=bool(data.get("emit_graph_schema", False)),
319
+ intelligence_list_cap=int(data.get("intelligence_list_cap", 80)),
320
+ emit_cfg=bool(data.get("emit_cfg", False)),
321
+ cfg_max_nodes=int(data.get("cfg_max_nodes", 500)),
322
+ cfg_inline_calls=bool(data.get("cfg_inline_calls", False)),
323
+ cfg_call_depth=int(data.get("cfg_call_depth", 3)),
324
+ cfg_max_paths=int(data.get("cfg_max_paths", 100)),
325
+ cfg_path_max_depth=int(data.get("cfg_path_max_depth", 1000)),
326
+ cfg_loop_visits=int(data.get("cfg_loop_visits", 2)),
327
+ cfg_probability=bool(data.get("cfg_probability", False)),
328
+ cfg_mermaid_probabilities=bool(data.get("cfg_mermaid_probabilities", False)),
329
+ cfg_runtime_trace=Path(data["cfg_runtime_trace"]) if data.get("cfg_runtime_trace") else None,
330
+ cfg_loop_repeat_prob=float(data.get("cfg_loop_repeat_prob", 0.6)),
331
+ graph_include_structural=bool(data.get("graph_include_structural", False)),
332
+ intelligence_transitive_callers=bool(data.get("intelligence_transitive_callers", False)),
333
+ emit_system_graph_stats=bool(data.get("emit_system_graph_stats", False)),
334
+ emit_graph_sqlite=bool(data.get("emit_graph_sqlite", False)),
335
+ emit_graph_communities=bool(data.get("emit_graph_communities", False)),
336
+ emit_llm_entry_sidecar=bool(data.get("emit_llm_entry_sidecar", False)),
337
+ include_references=bool(data.get("include_references", False)),
338
+ include_events=bool(data.get("include_events", False)),
339
+ cfg_ir_go=bool(data.get("cfg_ir_go", True)),
340
+ cfg_ir_php=bool(data.get("cfg_ir_php", True)),
341
+ cfg_ir_cpp=bool(data.get("cfg_ir_cpp", True)),
342
+ flow_include_event_edges=bool(data.get("flow_include_event_edges", False)),
343
+ flow_include_reference_edges=bool(data.get("flow_include_reference_edges", False)),
344
+ event_impact=bool(data.get("event_impact", False)),
345
+ cluster_mode=data.get("cluster_mode", "file_imports"), # type: ignore[arg-type]
346
+ graph_query=data.get("graph_query"),
347
+ enable_embeddings=bool(data.get("enable_embeddings", False)),
348
+ embedding_model=str(data.get("embedding_model", "all-MiniLM-L6-v2")),
349
+ embedding_max_nodes=int(data.get("embedding_max_nodes", 5000)),
350
+ embedding_k_clusters=int(data.get("embedding_k_clusters", 8)),
351
+ semantic_top_k=int(data.get("semantic_top_k", 10)),
352
+ semantic_search=data.get("semantic_search"),
353
+ emit_html_unified=bool(data.get("emit_html_unified", False)),
354
+ )
355
+
356
+
357
+ def _load_liferay_bases(raw: object) -> tuple[str, ...]:
358
+ if raw is None:
359
+ return ()
360
+ if isinstance(raw, list):
361
+ return tuple(str(x).strip() for x in raw if str(x).strip())
362
+ if isinstance(raw, str):
363
+ return tuple(x.strip() for x in raw.split(",") if x.strip())
364
+ return ()