mdengine 0.6.0__tar.gz → 0.7.0__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 (341) hide show
  1. {mdengine-0.6.0 → mdengine-0.7.0}/PKG-INFO +14 -2
  2. {mdengine-0.6.0 → mdengine-0.7.0}/pyproject.toml +22 -2
  3. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/__init__.py +1 -1
  4. mdengine-0.7.0/src/md_generator/codeflow/__init__.py +7 -0
  5. mdengine-0.7.0/src/md_generator/codeflow/analyzers/__init__.py +9 -0
  6. mdengine-0.7.0/src/md_generator/codeflow/analyzers/flow_analyzer.py +68 -0
  7. mdengine-0.7.0/src/md_generator/codeflow/api/job_manager.py +249 -0
  8. mdengine-0.7.0/src/md_generator/codeflow/api/main.py +185 -0
  9. mdengine-0.7.0/src/md_generator/codeflow/api/mcp_server.py +16 -0
  10. mdengine-0.7.0/src/md_generator/codeflow/api/run.py +15 -0
  11. mdengine-0.7.0/src/md_generator/codeflow/api/schemas.py +96 -0
  12. mdengine-0.7.0/src/md_generator/codeflow/api/settings.py +28 -0
  13. mdengine-0.7.0/src/md_generator/codeflow/cli/main.py +132 -0
  14. mdengine-0.7.0/src/md_generator/codeflow/core/__init__.py +6 -0
  15. mdengine-0.7.0/src/md_generator/codeflow/core/extractor.py +193 -0
  16. mdengine-0.7.0/src/md_generator/codeflow/core/run_config.py +31 -0
  17. mdengine-0.7.0/src/md_generator/codeflow/detectors/__init__.py +7 -0
  18. mdengine-0.7.0/src/md_generator/codeflow/detectors/api_detector.py +222 -0
  19. mdengine-0.7.0/src/md_generator/codeflow/detectors/entry_detector.py +46 -0
  20. mdengine-0.7.0/src/md_generator/codeflow/detectors/entry_rank.py +51 -0
  21. mdengine-0.7.0/src/md_generator/codeflow/detectors/kafka_detector.py +47 -0
  22. mdengine-0.7.0/src/md_generator/codeflow/generators/business_rules_markdown.py +68 -0
  23. mdengine-0.7.0/src/md_generator/codeflow/generators/cytoscape_enrich.py +97 -0
  24. mdengine-0.7.0/src/md_generator/codeflow/generators/entry_markdown.py +198 -0
  25. mdengine-0.7.0/src/md_generator/codeflow/generators/flow_summary.py +262 -0
  26. mdengine-0.7.0/src/md_generator/codeflow/generators/html.py +361 -0
  27. mdengine-0.7.0/src/md_generator/codeflow/generators/markdown.py +35 -0
  28. mdengine-0.7.0/src/md_generator/codeflow/generators/mermaid.py +45 -0
  29. mdengine-0.7.0/src/md_generator/codeflow/generators/sequence.py +33 -0
  30. mdengine-0.7.0/src/md_generator/codeflow/graph/__init__.py +5 -0
  31. mdengine-0.7.0/src/md_generator/codeflow/graph/builder.py +181 -0
  32. mdengine-0.7.0/src/md_generator/codeflow/ingestion/__init__.py +10 -0
  33. mdengine-0.7.0/src/md_generator/codeflow/ingestion/loader.py +66 -0
  34. mdengine-0.7.0/src/md_generator/codeflow/lang_dispatch.py +64 -0
  35. mdengine-0.7.0/src/md_generator/codeflow/mcp/server.py +42 -0
  36. mdengine-0.7.0/src/md_generator/codeflow/models/__init__.py +21 -0
  37. mdengine-0.7.0/src/md_generator/codeflow/models/ir.py +97 -0
  38. mdengine-0.7.0/src/md_generator/codeflow/parsers/__init__.py +18 -0
  39. mdengine-0.7.0/src/md_generator/codeflow/parsers/base.py +57 -0
  40. mdengine-0.7.0/src/md_generator/codeflow/parsers/cpp_parser.py +155 -0
  41. mdengine-0.7.0/src/md_generator/codeflow/parsers/go_parser.py +82 -0
  42. mdengine-0.7.0/src/md_generator/codeflow/parsers/java_parser.py +116 -0
  43. mdengine-0.7.0/src/md_generator/codeflow/parsers/php_parser.py +79 -0
  44. mdengine-0.7.0/src/md_generator/codeflow/parsers/python_parser.py +295 -0
  45. mdengine-0.7.0/src/md_generator/codeflow/parsers/treesitter_js_ts_parser.py +119 -0
  46. mdengine-0.7.0/src/md_generator/codeflow/rules/__init__.py +5 -0
  47. mdengine-0.7.0/src/md_generator/codeflow/rules/collector.py +284 -0
  48. mdengine-0.7.0/src/md_generator/codeflow/runtime/tracer.py +14 -0
  49. mdengine-0.7.0/src/md_generator/codeflow/utils/__init__.py +1 -0
  50. mdengine-0.7.0/src/md_generator/codeflow/utils/tools_root.py +13 -0
  51. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/engine_cli.py +6 -2
  52. mdengine-0.7.0/src/md_generator/graph/mcp/sse.py +8 -0
  53. mdengine-0.7.0/src/md_generator/openapi/api/__init__.py +1 -0
  54. mdengine-0.7.0/src/md_generator/openapi/cli/__init__.py +1 -0
  55. mdengine-0.7.0/src/md_generator/openapi/config/__init__.py +1 -0
  56. mdengine-0.7.0/src/md_generator/openapi/core/__init__.py +1 -0
  57. mdengine-0.7.0/src/md_generator/openapi/mcp/__init__.py +1 -0
  58. {mdengine-0.6.0 → mdengine-0.7.0}/src/mdengine.egg-info/PKG-INFO +14 -2
  59. {mdengine-0.6.0 → mdengine-0.7.0}/src/mdengine.egg-info/SOURCES.txt +53 -0
  60. {mdengine-0.6.0 → mdengine-0.7.0}/src/mdengine.egg-info/entry_points.txt +4 -0
  61. {mdengine-0.6.0 → mdengine-0.7.0}/src/mdengine.egg-info/requires.txt +15 -0
  62. {mdengine-0.6.0 → mdengine-0.7.0}/LICENSE +0 -0
  63. {mdengine-0.6.0 → mdengine-0.7.0}/README.md +0 -0
  64. {mdengine-0.6.0 → mdengine-0.7.0}/setup.cfg +0 -0
  65. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/__init__.py +0 -0
  66. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/__init__.py +0 -0
  67. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/convert_runner.py +0 -0
  68. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/jobs.py +0 -0
  69. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/main.py +0 -0
  70. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/mcp_server.py +0 -0
  71. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/mcp_setup.py +0 -0
  72. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/query_options.py +0 -0
  73. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/api/settings.py +0 -0
  74. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/convert_impl.py +0 -0
  75. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/converter.py +0 -0
  76. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/archive/options.py +0 -0
  77. {mdengine-0.6.0/src/md_generator/openapi → mdengine-0.7.0/src/md_generator/codeflow}/api/__init__.py +0 -0
  78. {mdengine-0.6.0/src/md_generator/graph/mcp → mdengine-0.7.0/src/md_generator/codeflow/api}/sse.py +0 -0
  79. {mdengine-0.6.0/src/md_generator/openapi → mdengine-0.7.0/src/md_generator/codeflow}/cli/__init__.py +0 -0
  80. {mdengine-0.6.0/src/md_generator/openapi/config → mdengine-0.7.0/src/md_generator/codeflow/generators}/__init__.py +0 -0
  81. {mdengine-0.6.0/src/md_generator/openapi/core → mdengine-0.7.0/src/md_generator/codeflow/mcp}/__init__.py +0 -0
  82. {mdengine-0.6.0/src/md_generator/openapi/mcp → mdengine-0.7.0/src/md_generator/codeflow/runtime}/__init__.py +0 -0
  83. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/__init__.py +0 -0
  84. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/__init__.py +0 -0
  85. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/factory.py +0 -0
  86. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/mongo_adapter.py +0 -0
  87. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/mysql_adapter.py +0 -0
  88. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/oracle_adapter.py +0 -0
  89. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/postgres_adapter.py +0 -0
  90. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/sql_common.py +0 -0
  91. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/adapters/sqlite_adapter.py +0 -0
  92. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/api/__init__.py +0 -0
  93. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/api/main.py +0 -0
  94. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/api/mcp_server.py +0 -0
  95. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/api/run.py +0 -0
  96. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/api/schemas.py +0 -0
  97. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/api/settings.py +0 -0
  98. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/api/sqlite_upload.py +0 -0
  99. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/cli/__init__.py +0 -0
  100. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/cli/main.py +0 -0
  101. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/config/__init__.py +0 -0
  102. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/config/default.yaml +0 -0
  103. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/__init__.py +0 -0
  104. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/base_adapter.py +0 -0
  105. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/__init__.py +0 -0
  106. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/dot_emitter.py +0 -0
  107. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/filter.py +0 -0
  108. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/mermaid_emitter.py +0 -0
  109. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/mermaid_render.py +0 -0
  110. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/model.py +0 -0
  111. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/pipeline.py +0 -0
  112. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/erd/render.py +0 -0
  113. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/extractor.py +0 -0
  114. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/job_manager.py +0 -0
  115. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/markdown_writer.py +0 -0
  116. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/models.py +0 -0
  117. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/run_config.py +0 -0
  118. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/util.py +0 -0
  119. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/core/zip_export.py +0 -0
  120. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/mcp/__init__.py +0 -0
  121. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/mcp/server.py +0 -0
  122. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/mcp/sse.py +0 -0
  123. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/db/mcp/stream.py +0 -0
  124. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/__init__.py +0 -0
  125. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/adapters/__init__.py +0 -0
  126. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/adapters/factory.py +0 -0
  127. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/adapters/neo4j_adapter.py +0 -0
  128. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/adapters/networkx_adapter.py +0 -0
  129. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/api/__init__.py +0 -0
  130. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/api/main.py +0 -0
  131. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/api/mcp_server.py +0 -0
  132. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/api/run.py +0 -0
  133. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/api/schemas.py +0 -0
  134. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/api/settings.py +0 -0
  135. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/cli/__init__.py +0 -0
  136. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/cli/main.py +0 -0
  137. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/config/__init__.py +0 -0
  138. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/config/default.yaml +0 -0
  139. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/__init__.py +0 -0
  140. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/base_adapter.py +0 -0
  141. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/extractor.py +0 -0
  142. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/graph_builder.py +0 -0
  143. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/job_manager.py +0 -0
  144. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/markdown_writer.py +0 -0
  145. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/models.py +0 -0
  146. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/run_config.py +0 -0
  147. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/viz.py +0 -0
  148. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/core/zip_export.py +0 -0
  149. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/mcp/__init__.py +0 -0
  150. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/mcp/server.py +0 -0
  151. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/graph/mcp/stream.py +0 -0
  152. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/__init__.py +0 -0
  153. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/api/__init__.py +0 -0
  154. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/api/main.py +0 -0
  155. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/api/mcp_server.py +0 -0
  156. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/api/query_options.py +0 -0
  157. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/api/settings.py +0 -0
  158. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/api/staging.py +0 -0
  159. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/api/zip_bundle.py +0 -0
  160. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/backends/__init__.py +0 -0
  161. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/backends/base.py +0 -0
  162. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/backends/easy.py +0 -0
  163. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/backends/paddle.py +0 -0
  164. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/backends/tesseract.py +0 -0
  165. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/convert_impl.py +0 -0
  166. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/converter.py +0 -0
  167. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/emit.py +0 -0
  168. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/io_util.py +0 -0
  169. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/image/utils.py +0 -0
  170. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/__init__.py +0 -0
  171. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/__init__.py +0 -0
  172. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/api/__init__.py +0 -0
  173. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/api/main.py +0 -0
  174. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/api/mcp_server.py +0 -0
  175. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/api/mcp_setup.py +0 -0
  176. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/api/run.py +0 -0
  177. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/api/settings.py +0 -0
  178. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/converter.py +0 -0
  179. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/formatter.py +0 -0
  180. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/audio/service.py +0 -0
  181. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/document_converter.py +0 -0
  182. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/job_store.py +0 -0
  183. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/__init__.py +0 -0
  184. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/api/__init__.py +0 -0
  185. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/api/main.py +0 -0
  186. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/api/mcp_server.py +0 -0
  187. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/api/mcp_setup.py +0 -0
  188. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/api/run.py +0 -0
  189. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/api/settings.py +0 -0
  190. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/converter.py +0 -0
  191. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/formatter.py +0 -0
  192. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/video/service.py +0 -0
  193. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/whisper_language.py +0 -0
  194. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/__init__.py +0 -0
  195. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/api/__init__.py +0 -0
  196. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/api/main.py +0 -0
  197. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/api/mcp_server.py +0 -0
  198. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/api/mcp_setup.py +0 -0
  199. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/api/run.py +0 -0
  200. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/api/settings.py +0 -0
  201. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/converter.py +0 -0
  202. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/formatter.py +0 -0
  203. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/metadata.py +0 -0
  204. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/service.py +0 -0
  205. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/media/youtube/transcript.py +0 -0
  206. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/__init__.py +0 -0
  207. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/api/main.py +0 -0
  208. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/api/mcp_server.py +0 -0
  209. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/api/run.py +0 -0
  210. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/api/schemas.py +0 -0
  211. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/api/settings.py +0 -0
  212. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/cli/main.py +0 -0
  213. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/config/default.yaml +0 -0
  214. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/converters/__init__.py +0 -0
  215. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/converters/swagger2_to_openapi3.py +0 -0
  216. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/core/extractor.py +0 -0
  217. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/core/run_config.py +0 -0
  218. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/core/zip_export.py +0 -0
  219. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/enrichers/__init__.py +0 -0
  220. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/enrichers/rules.py +0 -0
  221. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/enrichers/sample_body.py +0 -0
  222. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/generators/__init__.py +0 -0
  223. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/generators/dependency_graph.py +0 -0
  224. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/loaders/__init__.py +0 -0
  225. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/loaders/spec_loader.py +0 -0
  226. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/mcp/server.py +0 -0
  227. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/models/__init__.py +0 -0
  228. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/models/domain.py +0 -0
  229. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/normalizers/__init__.py +0 -0
  230. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/normalizers/operations.py +0 -0
  231. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/normalizers/schema_flatten.py +0 -0
  232. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/parsers/__init__.py +0 -0
  233. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/parsers/openapi_parser.py +0 -0
  234. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/resolvers/__init__.py +0 -0
  235. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/resolvers/ref_resolver.py +0 -0
  236. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/writers/__init__.py +0 -0
  237. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/openapi/writers/markdown_writer.py +0 -0
  238. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/__init__.py +0 -0
  239. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/api/__init__.py +0 -0
  240. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/api/main.py +0 -0
  241. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/api/mcp_server.py +0 -0
  242. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/api/settings.py +0 -0
  243. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/api/zip_bundle.py +0 -0
  244. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/converter.py +0 -0
  245. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/md_emit.py +0 -0
  246. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/pdf_extract.py +0 -0
  247. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/pdf/utils.py +0 -0
  248. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/__init__.py +0 -0
  249. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/__init__.py +0 -0
  250. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/convert_runner.py +0 -0
  251. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/jobs.py +0 -0
  252. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/main.py +0 -0
  253. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/mcp_server.py +0 -0
  254. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/mcp_setup.py +0 -0
  255. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/run.py +0 -0
  256. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/api/settings.py +0 -0
  257. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/assets.py +0 -0
  258. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/chunker.py +0 -0
  259. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/cli.py +0 -0
  260. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/extractor.py +0 -0
  261. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/html_to_md.py +0 -0
  262. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/options.py +0 -0
  263. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/pipeline.py +0 -0
  264. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/playwright/playwright_fetcher.py +0 -0
  265. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/__init__.py +0 -0
  266. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/__init__.py +0 -0
  267. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/convert_runner.py +0 -0
  268. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/jobs.py +0 -0
  269. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/main.py +0 -0
  270. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/mcp_server.py +0 -0
  271. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/mcp_setup.py +0 -0
  272. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/query_options.py +0 -0
  273. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/api/settings.py +0 -0
  274. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/convert_impl.py +0 -0
  275. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/converter.py +0 -0
  276. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/embedded_extract.py +0 -0
  277. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/ooxml_media.py +0 -0
  278. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/options.py +0 -0
  279. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/post_assets.py +0 -0
  280. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/text_formatting.py +0 -0
  281. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/vendor_pdf_md/__init__.py +0 -0
  282. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/vendor_pdf_md/convert.py +0 -0
  283. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/vendor_word_md/__init__.py +0 -0
  284. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/vendor_word_md/convert.py +0 -0
  285. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/ppt/zip_deep.py +0 -0
  286. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/__init__.py +0 -0
  287. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/__init__.py +0 -0
  288. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/convert_runner.py +0 -0
  289. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/jobs.py +0 -0
  290. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/main.py +0 -0
  291. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/mcp_server.py +0 -0
  292. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/mcp_setup.py +0 -0
  293. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/query_options.py +0 -0
  294. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/api/settings.py +0 -0
  295. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/convert_impl.py +0 -0
  296. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/converter.py +0 -0
  297. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/format_detect.py +0 -0
  298. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/md_emit_json.py +0 -0
  299. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/md_emit_txt.py +0 -0
  300. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/md_emit_xml.py +0 -0
  301. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/text/options.py +0 -0
  302. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/__init__.py +0 -0
  303. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/__init__.py +0 -0
  304. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/convert_runner.py +0 -0
  305. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/jobs.py +0 -0
  306. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/main.py +0 -0
  307. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/mcp_server.py +0 -0
  308. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/mcp_setup.py +0 -0
  309. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/query_options.py +0 -0
  310. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/api/settings.py +0 -0
  311. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/assets.py +0 -0
  312. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/convert_impl.py +0 -0
  313. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/converter.py +0 -0
  314. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/crawl.py +0 -0
  315. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/extract.py +0 -0
  316. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/fetch.py +0 -0
  317. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/html_to_md.py +0 -0
  318. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/options.py +0 -0
  319. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/page_convert.py +0 -0
  320. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/post_convert_assets.py +0 -0
  321. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/url/robots_util.py +0 -0
  322. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/__init__.py +0 -0
  323. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/api/__init__.py +0 -0
  324. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/api/convert_util.py +0 -0
  325. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/api/jobs.py +0 -0
  326. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/api/main.py +0 -0
  327. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/api/mcp_server.py +0 -0
  328. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/artifact.py +0 -0
  329. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/converter.py +0 -0
  330. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/word/settings.py +0 -0
  331. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/__init__.py +0 -0
  332. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/api/__init__.py +0 -0
  333. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/api/app.py +0 -0
  334. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/convert_config.py +0 -0
  335. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/converter.py +0 -0
  336. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/converter_core.py +0 -0
  337. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/excel_reader.py +0 -0
  338. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/markdown_emitter.py +0 -0
  339. {mdengine-0.6.0 → mdengine-0.7.0}/src/md_generator/xlsx/mcp_server.py +0 -0
  340. {mdengine-0.6.0 → mdengine-0.7.0}/src/mdengine.egg-info/dependency_links.txt +0 -0
  341. {mdengine-0.6.0 → mdengine-0.7.0}/src/mdengine.egg-info/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdengine
3
- Version: 0.6.0
3
+ Version: 0.7.0
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
7
7
  Project-URL: Homepage, https://github.com/vishal7090/md-generator
8
8
  Project-URL: Repository, https://github.com/vishal7090/md-generator
9
9
  Project-URL: Issues, https://github.com/vishal7090/md-generator/issues
10
- Keywords: markdown,pdf,docx,pptx,xlsx,ocr,zip,url,html,youtube,playwright,database,openapi
10
+ Keywords: markdown,pdf,docx,pptx,xlsx,ocr,zip,url,html,youtube,playwright,database,openapi,codeflow
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3 :: Only
13
13
  Classifier: Topic :: Text Processing :: Markup :: Markdown
@@ -103,6 +103,16 @@ Provides-Extra: openapi
103
103
  Requires-Dist: pyyaml>=6.0.0; extra == "openapi"
104
104
  Requires-Dist: prance>=23.6.0; extra == "openapi"
105
105
  Requires-Dist: openapi-spec-validator>=0.7.0; extra == "openapi"
106
+ Provides-Extra: codeflow
107
+ Requires-Dist: networkx>=3.2.0; extra == "codeflow"
108
+ Requires-Dist: javalang>=0.13.0; extra == "codeflow"
109
+ Provides-Extra: codeflow-treesitter
110
+ Requires-Dist: tree-sitter>=0.21.0; extra == "codeflow-treesitter"
111
+ Requires-Dist: tree-sitter-javascript>=0.21.0; extra == "codeflow-treesitter"
112
+ Requires-Dist: tree-sitter-typescript>=0.23.0; extra == "codeflow-treesitter"
113
+ Requires-Dist: tree-sitter-cpp>=0.23.0; extra == "codeflow-treesitter"
114
+ Provides-Extra: codeflow-clang
115
+ Requires-Dist: clang>=16.0.0; extra == "codeflow-clang"
106
116
  Provides-Extra: api
107
117
  Requires-Dist: fastapi>=0.115.0; extra == "api"
108
118
  Requires-Dist: uvicorn[standard]>=0.32.0; extra == "api"
@@ -125,6 +135,8 @@ Requires-Dist: mdengine[playwright]; extra == "dev"
125
135
  Requires-Dist: mdengine[db]; extra == "dev"
126
136
  Requires-Dist: mdengine[graph]; extra == "dev"
127
137
  Requires-Dist: mdengine[openapi]; extra == "dev"
138
+ Requires-Dist: mdengine[codeflow]; extra == "dev"
139
+ Requires-Dist: mdengine[codeflow-treesitter]; extra == "dev"
128
140
  Provides-Extra: all
129
141
  Requires-Dist: pymupdf>=1.24.0; extra == "all"
130
142
  Requires-Dist: pdfplumber>=0.11.0; extra == "all"
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mdengine"
7
- version = "0.6.0"
7
+ version = "0.7.0"
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"
11
11
  license = "MIT"
12
12
  license-files = ["LICENSE"]
13
13
  authors = [{ name = "mdengine contributors" }]
14
- keywords = ["markdown", "pdf", "docx", "pptx", "xlsx", "ocr", "zip", "url", "html", "youtube", "playwright", "database", "openapi"]
14
+ keywords = ["markdown", "pdf", "docx", "pptx", "xlsx", "ocr", "zip", "url", "html", "youtube", "playwright", "database", "openapi", "codeflow"]
15
15
  classifiers = [
16
16
  "Programming Language :: Python :: 3",
17
17
  "Programming Language :: Python :: 3 :: Only",
@@ -111,6 +111,19 @@ openapi = [
111
111
  "prance>=23.6.0",
112
112
  "openapi-spec-validator>=0.7.0",
113
113
  ]
114
+ codeflow = [
115
+ "networkx>=3.2.0",
116
+ "javalang>=0.13.0",
117
+ ]
118
+ codeflow-treesitter = [
119
+ "tree-sitter>=0.21.0",
120
+ "tree-sitter-javascript>=0.21.0",
121
+ "tree-sitter-typescript>=0.23.0",
122
+ "tree-sitter-cpp>=0.23.0",
123
+ ]
124
+ codeflow-clang = [
125
+ "clang>=16.0.0",
126
+ ]
114
127
  api = [
115
128
  "fastapi>=0.115.0",
116
129
  "uvicorn[standard]>=0.32.0",
@@ -132,6 +145,8 @@ dev = [
132
145
  "mdengine[db]",
133
146
  "mdengine[graph]",
134
147
  "mdengine[openapi]",
148
+ "mdengine[codeflow]",
149
+ "mdengine[codeflow-treesitter]",
135
150
  ]
136
151
  all = [
137
152
  "pymupdf>=1.24.0",
@@ -196,6 +211,10 @@ md-graph-mcp = "md_generator.graph.api.mcp_server:main"
196
211
  md-openapi = "md_generator.openapi.cli.main:main"
197
212
  md-openapi-api = "md_generator.openapi.api.run:main"
198
213
  md-openapi-mcp = "md_generator.openapi.api.mcp_server:main"
214
+ md-codeflow = "md_generator.codeflow.cli.main:main"
215
+ codeflow = "md_generator.codeflow.cli.main:main"
216
+ md-codeflow-api = "md_generator.codeflow.api.run:main"
217
+ md-codeflow-mcp = "md_generator.codeflow.api.mcp_server:main"
199
218
  mdengine = "md_generator.engine_cli:main"
200
219
 
201
220
  [tool.setuptools.packages.find]
@@ -224,5 +243,6 @@ testpaths = [
224
243
  "db-to-md/tests",
225
244
  "graph-to-md/tests",
226
245
  "openapi-to-md/tests",
246
+ "codeflow-to-md/tests",
227
247
  ]
228
248
  markers = ["integration: optional dependency or slower checks"]
@@ -1,5 +1,5 @@
1
1
  """md_generator: PDF, Office, image, text, and ZIP to Markdown conversion."""
2
2
 
3
- __version__ = "0.6.0"
3
+ __version__ = "0.7.0"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -0,0 +1,7 @@
1
+ """Multi-language execution flow extraction → Markdown, Mermaid, graph JSON, HTML."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __all__ = ["__version__"]
6
+
7
+ __version__ = "0.1.0"
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from md_generator.codeflow.analyzers.flow_analyzer import (
4
+ FlowSlice,
5
+ slice_from_entry,
6
+ walk_with_depth,
7
+ )
8
+
9
+ __all__ = ["FlowSlice", "slice_from_entry", "walk_with_depth"]
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ import networkx as nx
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class FlowSlice:
10
+ entry_id: str
11
+ nodes: set[str]
12
+ edges: list[tuple[str, str, dict]]
13
+ depth: int
14
+ cycle_nodes: set[str] = field(default_factory=set)
15
+ truncated: bool = False
16
+
17
+
18
+ def walk_with_depth(
19
+ g: nx.DiGraph,
20
+ start: str,
21
+ max_depth: int,
22
+ ) -> tuple[set[str], list[tuple[str, str, dict]], set[str], bool]:
23
+ """Reachability from start within ``max_depth`` hops (shortest-path layering)."""
24
+ if start not in g:
25
+ return {start}, [], set(), False
26
+ try:
27
+ plen = nx.single_source_shortest_path_length(g, start, cutoff=max_depth)
28
+ except nx.NetworkXError:
29
+ plen = {start: 0}
30
+
31
+ nodes = set(plen.keys())
32
+ edges_out: list[tuple[str, str, dict]] = []
33
+ cycle_guess: set[str] = set()
34
+ truncated = False
35
+
36
+ for u in nodes:
37
+ du = plen[u]
38
+ if du >= max_depth:
39
+ succ = list(g.successors(u))
40
+ if succ:
41
+ truncated = True
42
+ continue
43
+ for v in g.successors(u):
44
+ ed = dict(g.edges[u, v])
45
+ edges_out.append((u, v, ed))
46
+ dv = plen.get(v)
47
+ if dv is not None and dv <= du:
48
+ cycle_guess.add(v)
49
+ if v not in plen and du + 1 > max_depth:
50
+ truncated = True
51
+
52
+ return nodes, edges_out, cycle_guess, truncated
53
+
54
+
55
+ def slice_from_entry(
56
+ g: nx.DiGraph,
57
+ entry_id: str,
58
+ max_depth: int,
59
+ ) -> FlowSlice:
60
+ nodes, edges, cycles, truncated = walk_with_depth(g, entry_id, max_depth)
61
+ return FlowSlice(
62
+ entry_id=entry_id,
63
+ nodes=nodes,
64
+ edges=edges,
65
+ depth=max_depth,
66
+ cycle_nodes=cycles,
67
+ truncated=truncated,
68
+ )
@@ -0,0 +1,249 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import shutil
5
+ import sqlite3
6
+ import threading
7
+ import time
8
+ import uuid
9
+ import zipfile
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from md_generator.codeflow.api.schemas import scan_config_dump, scan_config_load
15
+ from md_generator.codeflow.core.extractor import run_scan
16
+ from md_generator.codeflow.core.run_config import ScanConfig
17
+ from md_generator.codeflow.ingestion.loader import LoadedWorkspace
18
+
19
+
20
+ @dataclass
21
+ class CodeflowJobRecord:
22
+ job_id: str
23
+ status: str
24
+ progress: int
25
+ current: str
26
+ workspace: str
27
+ zip_path: str | None
28
+ error: str | None
29
+ created_at: float
30
+ updated_at: float
31
+
32
+ def to_api_dict(self) -> dict[str, Any]:
33
+ return {
34
+ "job_id": self.job_id,
35
+ "status": self.status,
36
+ "progress": self.progress,
37
+ "current": self.current,
38
+ "workspace": self.workspace,
39
+ "zip_path": self.zip_path,
40
+ "error": self.error,
41
+ "created_at": self.created_at,
42
+ "updated_at": self.updated_at,
43
+ }
44
+
45
+
46
+ class CodeflowJobManager:
47
+ """SQLite-backed job store (same pattern as db-to-md ``JobManager``)."""
48
+
49
+ def __init__(
50
+ self,
51
+ *,
52
+ sqlite_path: str | Path | None = None,
53
+ workspace_root: Path | None = None,
54
+ in_memory: bool = False,
55
+ ) -> None:
56
+ self._lock = threading.Lock()
57
+ self._root = Path(workspace_root) if workspace_root else None
58
+ if in_memory:
59
+ self._conn = sqlite3.connect(":memory:", check_same_thread=False)
60
+ else:
61
+ path = Path(sqlite_path or _default_sqlite_path())
62
+ path.parent.mkdir(parents=True, exist_ok=True)
63
+ self._conn = sqlite3.connect(str(path), check_same_thread=False)
64
+ self._conn.row_factory = sqlite3.Row
65
+ self._init_db()
66
+
67
+ def _init_db(self) -> None:
68
+ assert self._conn is not None
69
+ self._conn.execute(
70
+ """
71
+ CREATE TABLE IF NOT EXISTS jobs (
72
+ job_id TEXT PRIMARY KEY,
73
+ status TEXT NOT NULL,
74
+ progress INTEGER NOT NULL DEFAULT 0,
75
+ current TEXT NOT NULL DEFAULT '',
76
+ workspace TEXT NOT NULL,
77
+ zip_path TEXT,
78
+ error TEXT,
79
+ created_at REAL NOT NULL,
80
+ updated_at REAL NOT NULL,
81
+ config_json TEXT NOT NULL
82
+ )
83
+ """
84
+ )
85
+ self._conn.commit()
86
+
87
+ def close(self) -> None:
88
+ if self._conn:
89
+ self._conn.close()
90
+
91
+ def create_zip_job(self, zip_bytes: bytes, cfg_template: ScanConfig) -> CodeflowJobRecord:
92
+ jid = str(uuid.uuid4())
93
+ base = self._root or Path.cwd() / "codeflow-jobs"
94
+ ws = (base / jid).resolve()
95
+ ws.mkdir(parents=True, exist_ok=True)
96
+ src = ws / "src"
97
+ src.mkdir(parents=True, exist_ok=True)
98
+ raw_zip = ws / "upload.zip"
99
+ raw_zip.write_bytes(zip_bytes)
100
+ with zipfile.ZipFile(raw_zip, "r") as zf:
101
+ zf.extractall(src)
102
+
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
+ )
122
+ now = time.time()
123
+ cfg_dump = json.dumps(scan_config_dump(cfg), sort_keys=True)
124
+ assert self._conn is not None
125
+ with self._lock:
126
+ self._conn.execute(
127
+ """
128
+ INSERT INTO jobs (job_id, status, progress, current, workspace, zip_path, error, created_at, updated_at, config_json)
129
+ VALUES (?, ?, 0, '', ?, NULL, NULL, ?, ?, ?)
130
+ """,
131
+ (jid, "PENDING", str(ws), now, now, cfg_dump),
132
+ )
133
+ self._conn.commit()
134
+ return self.get(jid) # type: ignore[return-value]
135
+
136
+ def get(self, job_id: str) -> CodeflowJobRecord | None:
137
+ assert self._conn is not None
138
+ with self._lock:
139
+ row = self._conn.execute("SELECT * FROM jobs WHERE job_id = ?", (job_id,)).fetchone()
140
+ if not row:
141
+ return None
142
+ return CodeflowJobRecord(
143
+ job_id=row["job_id"],
144
+ status=row["status"],
145
+ progress=row["progress"],
146
+ current=row["current"],
147
+ workspace=row["workspace"],
148
+ zip_path=row["zip_path"],
149
+ error=row["error"],
150
+ created_at=row["created_at"],
151
+ updated_at=row["updated_at"],
152
+ )
153
+
154
+ def update_progress(self, job_id: str, progress: int, current: str) -> None:
155
+ assert self._conn is not None
156
+ now = time.time()
157
+ with self._lock:
158
+ self._conn.execute(
159
+ "UPDATE jobs SET progress = ?, current = ?, updated_at = ? WHERE job_id = ?",
160
+ (progress, current, now, job_id),
161
+ )
162
+ self._conn.commit()
163
+
164
+ def mark_running(self, job_id: str) -> None:
165
+ self._set_status(job_id, "RUNNING")
166
+
167
+ def mark_completed(self, job_id: str, zip_path: Path | None) -> None:
168
+ assert self._conn is not None
169
+ now = time.time()
170
+ with self._lock:
171
+ self._conn.execute(
172
+ "UPDATE jobs SET status = ?, progress = 100, zip_path = ?, updated_at = ?, current = ? WHERE job_id = ?",
173
+ ("COMPLETED", str(zip_path) if zip_path else None, now, "completed", job_id),
174
+ )
175
+ self._conn.commit()
176
+
177
+ def mark_failed(self, job_id: str, err: str) -> None:
178
+ assert self._conn is not None
179
+ now = time.time()
180
+ with self._lock:
181
+ self._conn.execute(
182
+ "UPDATE jobs SET status = ?, error = ?, updated_at = ?, current = ? WHERE job_id = ?",
183
+ ("FAILED", err[:8000], now, "failed", job_id),
184
+ )
185
+ self._conn.commit()
186
+
187
+ def _set_status(self, job_id: str, status: str) -> None:
188
+ assert self._conn is not None
189
+ now = time.time()
190
+ with self._lock:
191
+ self._conn.execute(
192
+ "UPDATE jobs SET status = ?, updated_at = ? WHERE job_id = ?",
193
+ (status, now, job_id),
194
+ )
195
+ self._conn.commit()
196
+
197
+ def load_config(self, job_id: str) -> ScanConfig | None:
198
+ assert self._conn is not None
199
+ with self._lock:
200
+ row = self._conn.execute(
201
+ "SELECT config_json FROM jobs WHERE job_id = ?", (job_id,)
202
+ ).fetchone()
203
+ if not row:
204
+ return None
205
+ return scan_config_load(json.loads(row["config_json"]))
206
+
207
+ def run_job_thread(self, job_id: str) -> None:
208
+ def target() -> None:
209
+ try:
210
+ self.mark_running(job_id)
211
+ cfg = self.load_config(job_id)
212
+ if not cfg:
213
+ self.mark_failed(job_id, "missing config")
214
+ return
215
+ rec = self.get(job_id)
216
+ if not rec:
217
+ return
218
+ ws_path = Path(rec.workspace)
219
+
220
+ def on_prog(p: int, cur: str) -> None:
221
+ self.update_progress(job_id, p, cur)
222
+
223
+ lw = LoadedWorkspace(root=cfg.project_root, cleanup_dir=None)
224
+
225
+ self.update_progress(job_id, 10, "scan")
226
+ run_scan(cfg, workspace=lw)
227
+ on_prog(90, "zip")
228
+
229
+ zpath = ws_path / "output.zip"
230
+ self._zip_output(ws_path, cfg.output_path, zpath)
231
+ self.mark_completed(job_id, zpath)
232
+ except Exception as e:
233
+ self.mark_failed(job_id, str(e))
234
+
235
+ threading.Thread(target=target, daemon=True).start()
236
+
237
+ def _zip_output(self, workspace: Path, out_dir: Path, dest_zip: Path) -> None:
238
+ dest_zip.parent.mkdir(parents=True, exist_ok=True)
239
+ with zipfile.ZipFile(dest_zip, "w", zipfile.ZIP_DEFLATED) as zf:
240
+ if out_dir.exists():
241
+ for p in sorted(out_dir.rglob("*")):
242
+ if p.is_file():
243
+ zf.write(p, p.relative_to(workspace).as_posix())
244
+
245
+
246
+ def _default_sqlite_path() -> Path:
247
+ import tempfile
248
+
249
+ return Path(tempfile.gettempdir()) / "mdengine-codeflow-jobs" / "jobs.sqlite"
@@ -0,0 +1,185 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from contextlib import asynccontextmanager
5
+ from pathlib import Path
6
+
7
+ from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from fastapi.responses import FileResponse, Response, StreamingResponse
10
+
11
+ from md_generator.codeflow.api.job_manager import CodeflowJobManager
12
+ from md_generator.codeflow.api.schemas import merge_upload_options_json, options_to_scan_config
13
+ from md_generator.codeflow.api.settings import CodeflowApiSettings, cors_list, sqlite_path_resolved
14
+ from md_generator.codeflow.core.extractor import build_output_zip
15
+ from md_generator.codeflow.core.run_config import ScanConfig
16
+
17
+ try:
18
+ from md_generator.codeflow.mcp.server import build_mcp_stack
19
+
20
+ _mcp, _mcp_http = build_mcp_stack(mount_under_fastapi=True)
21
+ except Exception:
22
+ _mcp = None # type: ignore[misc]
23
+ _mcp_http = None
24
+
25
+
26
+ @asynccontextmanager
27
+ async def lifespan(app: FastAPI):
28
+ settings = CodeflowApiSettings()
29
+ ws = Path(settings.job_workspace_root) if settings.job_workspace_root else None
30
+ jobs = CodeflowJobManager(sqlite_path=sqlite_path_resolved(settings), workspace_root=ws)
31
+ app.state.settings = settings
32
+ app.state.jobs = jobs
33
+ if _mcp is not None:
34
+ async with _mcp.session_manager.run():
35
+ yield
36
+ else:
37
+ yield
38
+ jobs.close()
39
+
40
+
41
+ app = FastAPI(title="codeflow-to-md", lifespan=lifespan)
42
+ if _mcp_http is not None:
43
+ app.mount("/mcp", _mcp_http)
44
+
45
+ _bootstrap = CodeflowApiSettings()
46
+ app.add_middleware(
47
+ CORSMiddleware,
48
+ allow_origins=cors_list(_bootstrap),
49
+ allow_credentials="*" not in cors_list(_bootstrap),
50
+ allow_methods=["*"],
51
+ allow_headers=["*"],
52
+ )
53
+
54
+
55
+ @app.get("/health")
56
+ async def health() -> dict[str, str]:
57
+ return {"status": "ok"}
58
+
59
+
60
+ @app.post("/analyze")
61
+ async def analyze_upload(
62
+ request: Request,
63
+ file: UploadFile = File(...),
64
+ options_json: str | None = Form(default=None),
65
+ ) -> dict[str, str]:
66
+ """Upload a ZIP of source code; returns ``job_id`` for async processing."""
67
+ settings: CodeflowApiSettings = request.app.state.settings
68
+ jobs: CodeflowJobManager = request.app.state.jobs
69
+ raw = await file.read()
70
+ max_b = max(1, settings.max_upload_zip_mb) * 1024 * 1024
71
+ if len(raw) > max_b:
72
+ raise HTTPException(status_code=413, detail=f"ZIP exceeds CODEFLOW_MAX_UPLOAD_ZIP_MB ({settings.max_upload_zip_mb})")
73
+ try:
74
+ opts = merge_upload_options_json(options_json)
75
+ except Exception as e:
76
+ raise HTTPException(status_code=400, detail=str(e)) from e
77
+ # template: output path overwritten in job manager
78
+ tmp_src = Path(".")
79
+ cfg_tpl = options_to_scan_config(tmp_src, "out", opts)
80
+ try:
81
+ rec = jobs.create_zip_job(raw, cfg_tpl)
82
+ jobs.run_job_thread(rec.job_id)
83
+ except Exception as e:
84
+ raise HTTPException(status_code=400, detail=str(e)) from e
85
+ return {"job_id": rec.job_id}
86
+
87
+
88
+ @app.get("/status/{job_id}")
89
+ async def job_status(request: Request, job_id: str) -> dict:
90
+ jobs: CodeflowJobManager = request.app.state.jobs
91
+ rec = jobs.get(job_id)
92
+ if not rec:
93
+ raise HTTPException(404, detail="Unknown job_id")
94
+ return rec.to_api_dict()
95
+
96
+
97
+ @app.get("/result/{job_id}")
98
+ async def job_result(request: Request, job_id: str) -> FileResponse:
99
+ jobs: CodeflowJobManager = request.app.state.jobs
100
+ rec = jobs.get(job_id)
101
+ if not rec:
102
+ raise HTTPException(404, detail="Unknown job_id")
103
+ if rec.status != "COMPLETED" or not rec.zip_path:
104
+ raise HTTPException(400, detail="Job is not ready")
105
+ path = Path(rec.zip_path)
106
+ if not path.is_file():
107
+ raise HTTPException(400, detail="ZIP missing on disk")
108
+ return FileResponse(path, media_type="application/zip", filename="codeflow-output.zip")
109
+
110
+
111
+ @app.post("/analyze/sync")
112
+ async def analyze_sync(
113
+ request: Request,
114
+ file: UploadFile = File(...),
115
+ options_json: str | None = Form(default=None),
116
+ ) -> Response:
117
+ """Small ZIP → immediate ZIP response (bounded by ``CODEFLOW_MAX_SYNC_ZIP_MB``)."""
118
+ settings: CodeflowApiSettings = request.app.state.settings
119
+ raw = await file.read()
120
+ max_up = max(1, settings.max_upload_zip_mb) * 1024 * 1024
121
+ if len(raw) > max_up:
122
+ raise HTTPException(status_code=413, detail="ZIP too large")
123
+ try:
124
+ opts = merge_upload_options_json(options_json)
125
+ except Exception as e:
126
+ raise HTTPException(status_code=400, detail=str(e)) from e
127
+ import tempfile
128
+
129
+ td = Path(tempfile.mkdtemp(prefix="codeflow-sync-"))
130
+ src = td / "src"
131
+ src.mkdir(parents=True, exist_ok=True)
132
+ zin = td / "in.zip"
133
+ zin.write_bytes(raw)
134
+ import zipfile
135
+
136
+ with zipfile.ZipFile(zin, "r") as zf:
137
+ zf.extractall(src)
138
+ cfg = options_to_scan_config(src, "out", opts)
139
+ try:
140
+ data = build_output_zip(cfg, workspace_root=src)
141
+ except Exception as e:
142
+ raise HTTPException(status_code=400, detail=str(e)) from e
143
+ max_b = settings.max_sync_zip_mb * 1024 * 1024
144
+ if len(data) > max_b:
145
+ raise HTTPException(status_code=413, detail=f"Output exceeds CODEFLOW_MAX_SYNC_ZIP_MB ({settings.max_sync_zip_mb}); use POST /analyze")
146
+ return Response(
147
+ content=data,
148
+ media_type="application/zip",
149
+ headers={"Content-Disposition": 'attachment; filename="codeflow-output.zip"'},
150
+ )
151
+
152
+
153
+ @app.get("/analyze/job/{job_id}/events")
154
+ async def job_events(request: Request, job_id: str) -> StreamingResponse:
155
+ from md_generator.codeflow.api.sse import format_sse
156
+
157
+ jobs: CodeflowJobManager = request.app.state.jobs
158
+
159
+ async def gen():
160
+ rec = jobs.get(job_id)
161
+ if not rec:
162
+ yield format_sse("job_failed", {"job_id": job_id, "error": "unknown job_id"}).encode("utf-8")
163
+ return
164
+ yield format_sse("job_started", {"job_id": job_id}).encode("utf-8")
165
+ last_progress = -1
166
+ while True:
167
+ rec = jobs.get(job_id)
168
+ if not rec:
169
+ yield format_sse("job_failed", {"job_id": job_id, "error": "job disappeared"}).encode("utf-8")
170
+ return
171
+ if rec.progress != last_progress or rec.current:
172
+ last_progress = rec.progress
173
+ yield format_sse(
174
+ "progress_update",
175
+ {"progress": rec.progress, "current": rec.current},
176
+ ).encode("utf-8")
177
+ if rec.status == "COMPLETED":
178
+ yield format_sse("job_completed", {"job_id": job_id, "zip_path": rec.zip_path}).encode("utf-8")
179
+ return
180
+ if rec.status == "FAILED":
181
+ yield format_sse("job_failed", {"job_id": job_id, "error": rec.error or "unknown"}).encode("utf-8")
182
+ return
183
+ await asyncio.sleep(0.4)
184
+
185
+ return StreamingResponse(gen(), media_type="text/event-stream")
@@ -0,0 +1,16 @@
1
+ """Standalone MCP server for codeflow (stdio)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+
7
+
8
+ def main() -> None:
9
+ from md_generator.codeflow.mcp.server import build_mcp_stack
10
+
11
+ mcp, _ = build_mcp_stack(mount_under_fastapi=False)
12
+ asyncio.run(mcp.run_stdio_async())
13
+
14
+
15
+ if __name__ == "__main__":
16
+ main()
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+
6
+ def main() -> None:
7
+ import uvicorn
8
+
9
+ host = os.environ.get("CODEFLOW_TO_MD_HOST", "127.0.0.1")
10
+ port = int(os.environ.get("CODEFLOW_TO_MD_PORT", "8016"))
11
+ uvicorn.run("md_generator.codeflow.api.main:app", host=host, port=port, reload=False)
12
+
13
+
14
+ if __name__ == "__main__":
15
+ main()