kingmadoc 0.3.0.dev7__tar.gz → 0.3.0.dev8__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 (168) hide show
  1. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/CHANGELOG.md +8 -0
  2. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/PKG-INFO +1 -1
  3. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/explaining-code/SKILL.md +15 -4
  4. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/explaining-code/reference/arc42.md +1 -1
  5. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/explaining-code/reference/c4-model.md +10 -5
  6. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/explaining-code/reference/c4.md +1 -1
  7. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/explaining-code/reference/models.md +2 -0
  8. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/explaining-code/reference/split.md +1 -1
  9. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/cli.py +6 -5
  10. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/render.py +61 -3
  11. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_render.py +43 -0
  12. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_skill_explaining_code.py +21 -1
  13. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_skill_models.py +17 -0
  14. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/.featuredoc.yml +0 -0
  15. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  16. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  17. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/.github/workflows/ci.yml +0 -0
  18. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/.github/workflows/release.yml +0 -0
  19. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/.gitignore +0 -0
  20. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/CLAUDE.md +0 -0
  21. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/CONTRIBUTING.md +0 -0
  22. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/LICENSE +0 -0
  23. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/README.md +0 -0
  24. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/docs/conventions.md +0 -0
  25. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/docs/index.md +0 -0
  26. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/docs/releasing.md +0 -0
  27. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/docs/roadmap.md +0 -0
  28. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/docs/test-plan.md +0 -0
  29. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/README.md +0 -0
  30. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/manage.py +0 -0
  31. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/requirements.txt +0 -0
  32. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/shop/__init__.py +0 -0
  33. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/shop/models.py +0 -0
  34. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/shop/services.py +0 -0
  35. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/shop/settings.py +0 -0
  36. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/shop/urls.py +0 -0
  37. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop/shop/views.py +0 -0
  38. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop-discount/shop/discounts.py +0 -0
  39. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/fixtures/shop-discount/shop/services.py +0 -0
  40. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/results/2026-09-27T111837Z.json +0 -0
  41. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/results/2026-09-27T112600Z.json +0 -0
  42. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/results/2026-09-27T162715Z.json +0 -0
  43. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/scenarios/explain-branch.yml +0 -0
  44. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/scenarios/explain-feature.yml +0 -0
  45. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/evals/scenarios/plan-feature.yml +0 -0
  46. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/examples/verify-mode-plan.md +0 -0
  47. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/pyproject.toml +0 -0
  48. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/scripts/build_skill_variants.py +0 -0
  49. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/scripts/run_evals.py +0 -0
  50. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/SKILL.md +0 -0
  51. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/codex.md +0 -0
  52. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/copilot.md +0 -0
  53. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/cursor.md +0 -0
  54. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/reference/diagram-rules.md +0 -0
  55. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/skill/reference/formats.md +0 -0
  56. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/__init__.py +0 -0
  57. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/about.py +0 -0
  58. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/adr.py +0 -0
  59. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/config.py +0 -0
  60. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/d2_binary.py +0 -0
  61. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/diagrams/__init__.py +0 -0
  62. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/diagrams/base.py +0 -0
  63. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/diagrams/d2.py +0 -0
  64. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/diagrams/mermaid.py +0 -0
  65. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/diagrams/plantuml.py +0 -0
  66. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/documents.py +0 -0
  67. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/exceptions.py +0 -0
  68. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/explain.py +0 -0
  69. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/__init__.py +0 -0
  70. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/branch.py +0 -0
  71. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/collect.py +0 -0
  72. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/data_model.py +0 -0
  73. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/dominators.py +0 -0
  74. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/js_modules.py +0 -0
  75. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/projects.py +0 -0
  76. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/routes.py +0 -0
  77. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/facts/services.py +0 -0
  78. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/git.py +0 -0
  79. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/naming.py +0 -0
  80. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/plan/__init__.py +0 -0
  81. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/plan/analyzer.py +0 -0
  82. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/plan/dependencies.py +0 -0
  83. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/plan/generator.py +0 -0
  84. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/plan/models.py +0 -0
  85. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/plandoc.py +0 -0
  86. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/skills.py +0 -0
  87. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/templates/adr.md.j2 +0 -0
  88. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/templates/domain_design.md.j2 +0 -0
  89. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/templates/functional_design.md.j2 +0 -0
  90. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/templates/plan_default.md.j2 +0 -0
  91. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/templates/security_design.md.j2 +0 -0
  92. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/templates/technical_design.md.j2 +0 -0
  93. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/templating.py +0 -0
  94. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/verify/__init__.py +0 -0
  95. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/verify/changes.py +0 -0
  96. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/verify/commands.py +0 -0
  97. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/verify/deviations.py +0 -0
  98. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/verify/locate.py +0 -0
  99. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/verify/report.py +0 -0
  100. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/src/kingmadoc/vscode.py +0 -0
  101. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/d2/class.d2 +0 -0
  102. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/d2/component.d2 +0 -0
  103. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/d2/container.d2 +0 -0
  104. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/d2/context.d2 +0 -0
  105. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/d2/sequence.d2 +0 -0
  106. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/mermaid/class.mmd +0 -0
  107. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/mermaid/component.mmd +0 -0
  108. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/mermaid/container.mmd +0 -0
  109. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/mermaid/context.mmd +0 -0
  110. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/mermaid/sequence.mmd +0 -0
  111. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/plantuml/class.puml +0 -0
  112. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/plantuml/component.puml +0 -0
  113. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/plantuml/container.puml +0 -0
  114. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/plantuml/context.puml +0 -0
  115. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/backends/plantuml/sequence.puml +0 -0
  116. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/mermaid/component.mmd +0 -0
  117. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/mermaid/container.mmd +0 -0
  118. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/fixtures/mermaid/context.mmd +0 -0
  119. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_adr.py +0 -0
  120. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_adr_numbering.py +0 -0
  121. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_analyzer.py +0 -0
  122. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_cli.py +0 -0
  123. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_cli_encoding.py +0 -0
  124. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_cli_init.py +0 -0
  125. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_cli_plan_custom_template.py +0 -0
  126. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_cli_sigint.py +0 -0
  127. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_cli_verify_config.py +0 -0
  128. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_config.py +0 -0
  129. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_config_poetry.py +0 -0
  130. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_config_shape.py +0 -0
  131. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_d2_download.py +0 -0
  132. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_dependencies.py +0 -0
  133. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_dependency_graph_model.py +0 -0
  134. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_design_models.py +0 -0
  135. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_diagram_backends.py +0 -0
  136. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_diagrams_mermaid.py +0 -0
  137. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_documents.py +0 -0
  138. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_duplicate_names.py +0 -0
  139. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_evals.py +0 -0
  140. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_explain.py +0 -0
  141. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_explain_config.py +0 -0
  142. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_explain_status.py +0 -0
  143. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_extra_designs_coverage.py +0 -0
  144. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_facts.py +0 -0
  145. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_facts_code.py +0 -0
  146. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_facts_data_model.py +0 -0
  147. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_facts_dominators.py +0 -0
  148. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_functional_design.py +0 -0
  149. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_generator.py +0 -0
  150. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_grep_performance.py +0 -0
  151. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_manifests.py +0 -0
  152. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_max_lines_per_file.py +0 -0
  153. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_output_dir.py +0 -0
  154. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_plan_e2e.py +0 -0
  155. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_plandoc.py +0 -0
  156. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_properties.py +0 -0
  157. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_security_domain_designs.py +0 -0
  158. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_skill.py +0 -0
  159. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_skills_install.py +0 -0
  160. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_source_dirs.py +0 -0
  161. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_summary_slug_diagram_defaults.py +0 -0
  162. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_technical_design.py +0 -0
  163. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_templating_security.py +0 -0
  164. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_verify.py +0 -0
  165. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_verify_locate.py +0 -0
  166. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_version.py +0 -0
  167. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/tests/test_vscode_preview.py +0 -0
  168. {kingmadoc-0.3.0.dev7 → kingmadoc-0.3.0.dev8}/uv.lock +0 -0
@@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Changed
11
11
 
12
+ - Tidier diagrams: `kingmadoc render` lays figures out with ELK (straight,
13
+ right-angled arrows with fewer crossings; a diagram that sets its own
14
+ `layout-engine` keeps it) and warns about more than 12 arrows or two arrows between
15
+ the same shapes. `explaining-code` 5.7: one flow direction, one arrow per pair, at
16
+ most 12 arrows, labels of at most six words; C4 boundary labels sit top-left in a
17
+ small font so arrows do not cross them. At hand-over the agent asks whether VS Code
18
+ should open explainers as a preview (the pictures only show there) and sets it up on
19
+ yes.
12
20
  - Beta channel: every commit on `main` that passes CI is published to PyPI as a
13
21
  development version (`0.3.0.devN`); follow it with
14
22
  `pipx install --pip-args=--pre kingmadoc` and `pipx upgrade kingmadoc`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: kingmadoc
3
- Version: 0.3.0.dev7
3
+ Version: 0.3.0.dev8
4
4
  Summary: Feature docs for AI coding agents: a plan with C4 diagrams before the code, a verification doc after.
5
5
  Project-URL: Homepage, https://github.com/ATkingma/KingmaDoc
6
6
  Project-URL: Issues, https://github.com/ATkingma/KingmaDoc/issues
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: explaining-code
3
3
  description: "Explains existing code with rendered diagrams (C4 in Simon Brown's notation; UML sequence, state, class, activity, use case; ER; data flow) and short tables, as an arc42 or compact C4 document, one file or split into functional and technical. Fixes code blindness, e.g. after an agent wrote the code. Scope: a feature, a branch or PR, a whole project, or a folder, service or module. Use when the user asks to explain, describe, document, map, diagram, draw, visualise or give an overview of existing code or architecture; asks how something works, what it does, how the parts fit together, where something happens, or what a branch, PR, commit or task changed; wants onboarding, a walkthrough, a codebase tour, an architecture or design document, arc42, C4, UML, sequence, ER or deployment diagrams of existing code; or no longer understands the code. The request may be in any language. Not for features that are not built yet."
4
- version: 5.6.0
4
+ version: 5.7.0
5
5
  allowed-tools: [Read, Write, Glob, Grep, Bash]
6
6
  ---
7
7
 
@@ -163,6 +163,12 @@ Write every diagram in **D2** (Step 5 turns them into images).
163
163
  Never set a `font-color` without a fill (titles, labels: the theme picks the colour),
164
164
  and never fill white: boundaries and nodes are `fill: transparent`. Put
165
165
  `shape: sequence_diagram` at the top level, or give its container the label `""`.
166
+ 5. **Tidy arrows:** set `direction: down` (people on top, data stores at the bottom;
167
+ `right` only for timelines and swimlanes), draw one arrow per pair of shapes with a
168
+ combined label, keep at most 12 arrows per figure (split it otherwise, e.g. one
169
+ figure per container), label each arrow in at most six words plus `[protocol]`, and
170
+ point arrows at the shapes inside a boundary, not at the boundary. `kingmadoc render`
171
+ lays figures out with straight, right-angled arrows (ELK) and warns about crowded ones.
166
172
 
167
173
  For a **branch**, mark changes by border, so the C4 colours stay meaningful, and add
168
174
  both to the legend:
@@ -174,6 +180,7 @@ classes: {
174
180
  changed: {style: {stroke: "#ef6c00"; stroke-width: 4}}
175
181
  }
176
182
  title: "[Container] Webshop - branch feature/invoices" {shape: text; near: top-center; style: {font-size: 24; bold: true}}
183
+ direction: down
177
184
  vars: {
178
185
  d2-legend: {
179
186
  n: New in this branch {class: [container; new]}
@@ -236,6 +243,10 @@ Go through the checklist of each figure's model (end of
236
243
  and fix what fails; check that the document embeds every image. Then hand over in at most five lines:
237
244
  the path, one or two sentences on what the system is, the number of figures, and the
238
245
  "Couldn't work out" questions (at most three), which you then answer into the explainer.
239
- Do not paste the explainer or its figures into the chat. If VS Code opens it as text,
240
- say once that Ctrl+Shift+V shows the pictures, or `kingmadoc skills install --vscode`
241
- always does; never run that without the user's consent.
246
+ Do not paste the explainer or its figures into the chat.
247
+
248
+ VS Code shows the pictures only in its Markdown preview. If `.vscode/settings.json` does
249
+ not yet open `docs/explain/` as a preview, Ask once: "Should VS Code open explainers
250
+ directly as a preview, with the pictures?" and, on yes, run
251
+ `kingmadoc skills install --vscode` (it adds one setting). Never run it without that yes;
252
+ without it, mention that Ctrl+Shift+V shows the pictures.
@@ -63,7 +63,7 @@ Text in `<angle brackets>` is filled in; leave out subsections marked optional.
63
63
  | **Scope** | <feature / branch `<branch>` vs `<base>` / project / part `<path>`> |
64
64
  | **Stack** | <languages, frameworks, data stores> |
65
65
  | **Entry points** | <`path`, …> |
66
- | **Based on** | <commit hash (branch)> · <ISO date> · KingmaDoc skill explaining-code 5.6.0 (arc42) |
66
+ | **Based on** | <commit hash (branch)> · <ISO date> · KingmaDoc skill explaining-code 5.7.0 (arc42) |
67
67
 
68
68
  ## What changed (branch only)
69
69
 
@@ -106,10 +106,11 @@ classes: {
106
106
  container: {shape: rectangle; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
107
107
  database: {shape: cylinder; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
108
108
  component: {shape: rectangle; style: {fill: "#85bbf0"; stroke: "#5d82a8"; font-color: "#000000"}}
109
- boundary: {style: {fill: transparent; stroke: "#888888"; stroke-dash: 4}}
110
- node: {style: {fill: transparent; stroke: "#888888"}}
109
+ boundary: {label.near: top-left; style: {fill: transparent; stroke: "#888888"; stroke-dash: 4; font-size: 15}}
110
+ node: {label.near: top-left; style: {fill: transparent; stroke: "#888888"; font-size: 15}}
111
111
  }
112
112
  title: "[System Context] Webshop" {
113
+ direction: down
113
114
  shape: text
114
115
  near: top-center
115
116
  style: {font-size: 24; bold: true}
@@ -155,9 +156,10 @@ classes: {
155
156
  external: {shape: rectangle; style: {fill: "#999999"; stroke: "#6b6b6b"; font-color: "#ffffff"}}
156
157
  container: {shape: rectangle; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
157
158
  database: {shape: cylinder; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
158
- boundary: {style: {fill: transparent; stroke: "#888888"; stroke-dash: 4}}
159
+ boundary: {label.near: top-left; style: {fill: transparent; stroke: "#888888"; stroke-dash: 4; font-size: 15}}
159
160
  }
160
161
  title: "[Container] Webshop" {shape: text; near: top-center; style: {font-size: 24; bold: true}}
162
+ direction: down
161
163
  vars: {
162
164
  d2-legend: {
163
165
  p: Person {class: person}
@@ -213,9 +215,10 @@ classes: {
213
215
  container: {shape: rectangle; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
214
216
  database: {shape: cylinder; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
215
217
  component: {shape: rectangle; style: {fill: "#85bbf0"; stroke: "#5d82a8"; font-color: "#000000"}}
216
- boundary: {style: {fill: transparent; stroke: "#888888"; stroke-dash: 4}}
218
+ boundary: {label.near: top-left; style: {fill: transparent; stroke: "#888888"; stroke-dash: 4; font-size: 15}}
217
219
  }
218
220
  title: "[Component] Webshop - API" {shape: text; near: top-center; style: {font-size: 24; bold: true}}
221
+ direction: down
219
222
  vars: {
220
223
  d2-legend: {
221
224
  c: Container {class: container}
@@ -262,9 +265,10 @@ instances they run; infrastructure (proxy, DNS) only when the code configures it
262
265
  classes: {
263
266
  container: {shape: rectangle; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
264
267
  database: {shape: cylinder; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
265
- node: {style: {fill: transparent; stroke: "#888888"}}
268
+ node: {label.near: top-left; style: {fill: transparent; stroke: "#888888"; font-size: 15}}
266
269
  }
267
270
  title: "[Deployment] Webshop - production" {shape: text; near: top-center; style: {font-size: 24; bold: true}}
271
+ direction: down
268
272
  vars: {
269
273
  d2-legend: {
270
274
  n: Deployment node {class: node}
@@ -298,6 +302,7 @@ classes: {
298
302
  database: {shape: cylinder; style: {fill: "#438dd5"; stroke: "#3c7fc0"; font-color: "#ffffff"}}
299
303
  }
300
304
  title: "[Dynamic] Webshop - placing an order" {shape: text; near: top-center; style: {font-size: 24; bold: true}}
305
+ direction: down
301
306
  vars: {
302
307
  d2-legend: {
303
308
  p: Person {class: person}
@@ -22,7 +22,7 @@ Text in `<angle brackets>` is filled in.
22
22
  | **Scope** | <feature / branch `<branch>` vs `<base>` / project / part `<path>`> |
23
23
  | **Stack** | <languages, frameworks, data stores> |
24
24
  | **Entry points** | <`path`, …> |
25
- | **Based on** | <commit hash (branch)> · <ISO date> · KingmaDoc skill explaining-code 5.6.0 |
25
+ | **Based on** | <commit hash (branch)> · <ISO date> · KingmaDoc skill explaining-code 5.7.0 |
26
26
 
27
27
  ## In short
28
28
 
@@ -140,6 +140,7 @@ no methods, no types, multiplicities kept.
140
140
 
141
141
  ```d2
142
142
  title: "[Class] Diagram backends" {shape: text; near: top-center; style: {font-size: 24; bold: true}}
143
+ direction: down
143
144
  backend: "«interface» DiagramBackend" {
144
145
  shape: class
145
146
  "+render_context(diagram)": str
@@ -171,6 +172,7 @@ inside it.
171
172
 
172
173
  ```d2
173
174
  title: "[Package] kingmadoc" {shape: text; near: top-center; style: {font-size: 24; bold: true}}
175
+ direction: down
174
176
  cli: cli {shape: package}
175
177
  plan: plan {shape: package}
176
178
  diagrams: diagrams {shape: package}
@@ -43,7 +43,7 @@ in its first line. `kingmadoc render` takes all three files at once.
43
43
  | **Scope** | <feature / branch `<branch>` vs `<base>` / project / part `<path>`> |
44
44
  | **Stack** | <languages, frameworks, data stores> |
45
45
  | **Entry points** | <`path`, …> |
46
- | **Based on** | <commit hash (branch)> · <ISO date> · KingmaDoc skill explaining-code 5.6.0 (split) |
46
+ | **Based on** | <commit hash (branch)> · <ISO date> · KingmaDoc skill explaining-code 5.7.0 (split) |
47
47
 
48
48
  <at most three plain sentences: what it is, for whom, what it does>
49
49
 
@@ -49,7 +49,7 @@ from kingmadoc.plan.generator import (
49
49
  render_plan,
50
50
  )
51
51
  from kingmadoc.plandoc import check_plan, generated_at, parse_plan, set_status
52
- from kingmadoc.render import dark_mode_warnings, render_file
52
+ from kingmadoc.render import diagram_warnings, render_file
53
53
  from kingmadoc.skills import AGENT_DIRS, install_skills
54
54
  from kingmadoc.verify.changes import detect_changes
55
55
  from kingmadoc.verify.commands import MARKER_FILES, detect_commands, run_check
@@ -474,10 +474,11 @@ def render_command(documents: tuple[Path, ...], light: bool, verbose: bool) -> N
474
474
  noun = "image" if len(images) == 1 else "images"
475
475
  click.echo(f"{document}: {len(images)} {noun} ({span})")
476
476
  for image in images:
477
- if not light:
478
- source = image.with_suffix(".d2")
479
- for warning in dark_mode_warnings(source.read_text(encoding="utf-8")):
480
- click.echo(f"{source}: {warning} (dark mode)", err=True)
477
+ source = image.with_suffix(".d2")
478
+ for warning in diagram_warnings(source.read_text(encoding="utf-8")):
479
+ if light and warning.endswith("(dark mode)"):
480
+ continue
481
+ click.echo(f"{source}: {warning}", err=True)
481
482
  except KingmaDocError as exc:
482
483
  raise click.ClickException(str(exc)) from exc
483
484
 
@@ -35,6 +35,12 @@ D2_PAD = 20
35
35
  IMAGE_MODE = 0o644
36
36
  # D2's own dark theme; the SVG switches to it when the viewer uses dark mode.
37
37
  D2_DARK_THEME = 200
38
+ # Straight, right-angled arrows (see _layout_args); bundled with D2 like dagre.
39
+ LAYOUT_ENGINE = "elk"
40
+ # Above this many arrows a figure gets hard to follow (render warns).
41
+ MAX_ARROWS = 12
42
+ _ARROW = re.compile(r"^\s*([\w.]+)\s*(<->|->|<-|--)\s*([\w.]+)", re.M)
43
+ _LEGEND = re.compile(r"vars:\s*\{\s*d2-legend:\s*\{.*?^\s*\}\s*^\}", re.S | re.M)
38
44
 
39
45
  # A diagram is either a fenced d2 block, or a reference written by an earlier render.
40
46
  # References may only point into img/ next to the document (no other paths).
@@ -82,7 +88,7 @@ def render_file(path: Path, d2: Sequence[str], dark: bool = True) -> list[Path]:
82
88
  # across filesystems (EXDEV when /tmp is another disk).
83
89
  with tempfile.TemporaryDirectory(dir=path.parent, prefix=".kingmadoc-render-") as tmp:
84
90
  rendered = [
85
- _render(source, Path(tmp), n, path, [*d2, *_theme_args(dark)])
91
+ _render(source, Path(tmp), n, path, [*d2, *_theme_args(dark), *_layout_args(source)])
86
92
  for n, source in enumerate(sources, start=1)
87
93
  ]
88
94
  image_dir.mkdir(parents=True, exist_ok=True)
@@ -101,6 +107,49 @@ def render_file(path: Path, d2: Sequence[str], dark: bool = True) -> list[Path]:
101
107
  return [image_dir / f"{name}.svg" for name in names]
102
108
 
103
109
 
110
+ def diagram_warnings(source: str) -> list[str]:
111
+ """Everything worth fixing in a D2 diagram: dark-mode styles and crowded arrows.
112
+
113
+ Args:
114
+ source: The D2 source.
115
+
116
+ Returns:
117
+ One message per problem (empty when there is none).
118
+ """
119
+ return dark_mode_warnings(source) + crowding_warnings(source)
120
+
121
+
122
+ def crowding_warnings(source: str) -> list[str]:
123
+ """Warn about figures whose arrows get hard to follow.
124
+
125
+ More than :data:`MAX_ARROWS` arrows, or two arrows between the same two shapes (one
126
+ arrow with a combined label reads better).
127
+
128
+ Args:
129
+ source: The D2 source.
130
+
131
+ Returns:
132
+ One message per problem.
133
+ """
134
+ body = _LEGEND.sub("", source)
135
+ if "sequence_diagram" in body:
136
+ return [] # a sequence diagram's arrows are its messages, in order
137
+ pairs = [(a, b) for a, _, b in _ARROW.findall(body)]
138
+ warnings = []
139
+ if len(pairs) > MAX_ARROWS:
140
+ warnings.append(f"{len(pairs)} arrows (more than {MAX_ARROWS}) are hard to follow; "
141
+ "split the figure or combine arrows")
142
+ seen: dict[frozenset[str], int] = {}
143
+ for a, b in pairs:
144
+ seen[frozenset((a, b))] = seen.get(frozenset((a, b)), 0) + 1
145
+ for pair, count in seen.items():
146
+ if count > 1 and len(pair) == 2:
147
+ a, b = sorted(pair)
148
+ warnings.append(f"{count} arrows between `{a}` and `{b}`; draw one with a "
149
+ "combined label")
150
+ return warnings
151
+
152
+
104
153
  def dark_mode_warnings(source: str) -> list[str]:
105
154
  """Find styles in a D2 diagram that break when the image shows in dark mode.
106
155
 
@@ -121,10 +170,10 @@ def dark_mode_warnings(source: str) -> list[str]:
121
170
  fill = re.search(r"\bfill: *\"?([^;}\n\"]+)", block)
122
171
  if not fill or fill.group(1).strip() == "transparent":
123
172
  warnings.append(f"{_key(source, match.start())}: font-color without a fill stays "
124
- "dark on the dark background; remove the font-color")
173
+ "dark on the dark background; remove the font-color (dark mode)")
125
174
  for match in re.finditer(r"\bfill: *\"?(?:#fff\b|#ffffff|white)\"?", source, re.I):
126
175
  warnings.append(f"{_key(source, match.start())}: a white fill stays white while the "
127
- "text on it turns light; use fill: transparent")
176
+ "text on it turns light; use fill: transparent (dark mode)")
128
177
  for match in re.finditer(r"^( +)shape: *sequence_diagram", source, re.M):
129
178
  opening = source[: match.start()].rstrip().rsplit("\n", 1)[-1]
130
179
  if not re.search(r':\s*""\s*\{$', opening):
@@ -202,6 +251,15 @@ def _render(source: str, tmp: Path, n: int, doc: Path, d2: Sequence[str]) -> Pat
202
251
  return out
203
252
 
204
253
 
254
+ def _layout_args(source: str) -> list[str]:
255
+ """ELK unless the diagram picks its own engine (``vars: {d2-config: {layout-engine}}``).
256
+
257
+ ELK routes arrows orthogonally, with fewer crossings than D2's default (dagre, curved
258
+ splines that run over each other in bigger diagrams).
259
+ """
260
+ return [] if "layout-engine" in source else ["--layout", LAYOUT_ENGINE]
261
+
262
+
205
263
  def _theme_args(dark: bool) -> list[str]:
206
264
  return ["--dark-theme", str(D2_DARK_THEME)] if dark else []
207
265
 
@@ -426,3 +426,46 @@ def test_images_are_readable_by_everyone(tmp_path: Path, d2: list[str]) -> None:
426
426
  images = render_file(_doc(tmp_path), d2)
427
427
 
428
428
  assert all(p.stat().st_mode & 0o777 == 0o644 for p in images)
429
+
430
+
431
+ def test_images_use_the_elk_layout(
432
+ tmp_path: Path, d2: list[str], monkeypatch: pytest.MonkeyPatch
433
+ ) -> None:
434
+ """ELK routes arrows straight and at right angles (dagre curves them over each other)."""
435
+ commands = _commands(tmp_path, d2, monkeypatch)
436
+
437
+ assert commands and all(c[c.index("--layout") + 1] == "elk" for c in commands)
438
+
439
+
440
+ def test_a_diagram_that_picks_its_layout_keeps_it(
441
+ tmp_path: Path, d2: list[str], monkeypatch: pytest.MonkeyPatch
442
+ ) -> None:
443
+ """vars.d2-config.layout-engine in the source wins over the default."""
444
+ import subprocess
445
+
446
+ from kingmadoc import render
447
+
448
+ seen: list[list[str]] = []
449
+ real_run = subprocess.run
450
+
451
+ def run(command: list[str], **kwargs: object): # type: ignore[no-untyped-def]
452
+ seen.append(command)
453
+ return real_run(command, **kwargs) # type: ignore[call-overload]
454
+
455
+ monkeypatch.setattr(render.subprocess, "run", run)
456
+ source = "vars: {d2-config: {layout-engine: dagre}}\na -> b\n"
457
+ render_file(_doc(tmp_path, f"# T\n\n```d2\n{source}```\n"), d2)
458
+
459
+ assert seen and "--layout" not in seen[0]
460
+
461
+
462
+ def test_crowded_diagrams_are_reported() -> None:
463
+ """More than 12 arrows, or two arrows between the same pair, make a figure hard to read."""
464
+ from kingmadoc.render import diagram_warnings
465
+
466
+ crowded = "\n".join(f"a{i} -> b{i}: x" for i in range(13))
467
+ twice = "api -> db: reads\ndb -> api: rows\nweb -> api: calls\n"
468
+
469
+ assert any("13 arrows" in w for w in diagram_warnings(crowded))
470
+ assert any("`api` and `db`" in w for w in diagram_warnings(twice))
471
+ assert diagram_warnings("a -> b: x\nb -> c: y\n") == []
@@ -211,8 +211,12 @@ def test_d2_examples_compile(tmp_path: Path, index: int) -> None:
211
211
  source = re.sub(r"<([A-Za-z][^<>\n]*)>", r"\1", examples[index])
212
212
  (tmp_path / "x.d2").write_text(source, encoding="utf-8")
213
213
 
214
+ # The same options as `kingmadoc render`: the ELK layout and the dark theme.
215
+ from kingmadoc.render import D2_DARK_THEME, _layout_args
216
+
214
217
  result = subprocess.run(
215
- [D2 or "d2", str(tmp_path / "x.d2"), str(tmp_path / "x.svg")],
218
+ [D2 or "d2", *_layout_args(source), "--dark-theme", str(D2_DARK_THEME),
219
+ str(tmp_path / "x.d2"), str(tmp_path / "x.svg")],
216
220
  capture_output=True, text=True, timeout=60, check=False,
217
221
  )
218
222
 
@@ -262,3 +266,19 @@ def test_long_references_start_with_their_contents() -> None:
262
266
  assert "Contents:" in head, path.name
263
267
  for section in re.findall(r"^## (.+)$", _read(path).split("````", 1)[0], re.M):
264
268
  assert section.split(".")[0] in head or section in head, f"{path.name}: {section}"
269
+
270
+
271
+ def test_arrows_stay_tidy() -> None:
272
+ """One flow direction, one arrow per pair, at most 12 arrows, short labels."""
273
+ step = re.sub(r"\s+", " ", _read(SKILL).split("## Step 3.", 1)[1].split("## Step 4.", 1)[0])
274
+
275
+ assert "direction: down" in step and "one arrow per pair" in step
276
+ assert "at most 12 arrows" in step and "six words" in step
277
+
278
+
279
+ def test_hand_over_offers_the_vs_code_preview() -> None:
280
+ """The pictures only show in a preview: ask once, then set it up on yes."""
281
+ hand_over = re.sub(r"\s+", " ", _read(SKILL).split("## Step 6.", 1)[1])
282
+
283
+ assert "kingmadoc skills install --vscode" in hand_over
284
+ assert "Ask" in hand_over and "on yes" in hand_over
@@ -331,3 +331,20 @@ def test_algorithm_model_explains_with_formula_trace_and_complexity() -> None:
331
331
  assert "```text" in body and "Invariant" in body
332
332
  assert re.search(r"\bO\(.+\) time", body)
333
333
  assert re.search(r"^\| Step \|", body, re.M), "a trace table, one row per step"
334
+
335
+
336
+ def test_c4_boundaries_keep_their_label_out_of_the_arrows() -> None:
337
+ """Boundary and node labels sit top-left in a small font, so arrows do not cross them."""
338
+ for line in re.findall(r"^\s*(?:boundary|node): \{.*$", _read(C4_MODEL), re.M):
339
+ assert "label.near: top-left" in line and "font-size: 15" in line, line
340
+
341
+
342
+ @pytest.mark.parametrize("path", ALL_FILES, ids=lambda p: p.name)
343
+ def test_diagrams_flow_down(path: Path) -> None:
344
+ """Every example with arrows (except sequence diagrams) sets one flow direction:
345
+ down, or right for timelines and swimlanes."""
346
+ for source in _examples(_read(path)):
347
+ if _edges(source) and "sequence_diagram" not in source and "grid-" not in source:
348
+ assert re.search(r"^direction: (down|right)$", source, re.M), (
349
+ f"{path.name}:\n{source[:120]}"
350
+ )
File without changes
File without changes
File without changes
File without changes