leyline-code 0.8.1__tar.gz → 0.9.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 (192) hide show
  1. leyline_code-0.9.1/PKG-INFO +389 -0
  2. leyline_code-0.9.1/README.md +356 -0
  3. leyline_code-0.9.1/docs/changes.md +372 -0
  4. leyline_code-0.9.1/docs/indexing.md +227 -0
  5. leyline_code-0.9.1/docs/mcp-tools.md +56 -0
  6. {leyline_code-0.8.1 → leyline_code-0.9.1}/docs/quick-reference.md +8 -4
  7. leyline_code-0.9.1/docs/understanding.md +327 -0
  8. {leyline_code-0.8.1 → leyline_code-0.9.1}/pyproject.toml +1 -1
  9. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-adversarial-review/SKILL.md +1 -1
  10. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-ask/SKILL.md +4 -3
  11. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-explain-flow/SKILL.md +1 -1
  12. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-explore-module/SKILL.md +2 -2
  13. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-pr-review/SKILL.md +1 -1
  14. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-quick-change/SKILL.md +1 -1
  15. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-spec/SKILL.md +1 -1
  16. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/__init__.py +1 -1
  17. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/cli.py +281 -25
  18. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/cluster.py +17 -7
  19. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/coupling.py +2 -0
  20. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/explain.py +35 -3
  21. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/export.py +3 -0
  22. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/loop.py +21 -8
  23. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/outline.py +17 -2
  24. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/query.py +81 -16
  25. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/server.py +48 -34
  26. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/store.py +28 -0
  27. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/tours.py +23 -14
  28. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/viewer/viewer.html +18 -6
  29. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_flows_explain.py +2 -2
  30. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_index.py +4 -2
  31. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_resolve.py +1 -1
  32. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_server.py +7 -4
  33. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_surface.py +73 -2
  34. leyline_code-0.8.1/PKG-INFO +0 -1325
  35. leyline_code-0.8.1/README.md +0 -1292
  36. {leyline_code-0.8.1 → leyline_code-0.9.1}/.github/workflows/publish.yml +0 -0
  37. {leyline_code-0.8.1 → leyline_code-0.9.1}/.github/workflows/test.yml +0 -0
  38. {leyline_code-0.8.1 → leyline_code-0.9.1}/.gitignore +0 -0
  39. {leyline_code-0.8.1 → leyline_code-0.9.1}/LICENSE +0 -0
  40. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-change-impact/SKILL.md +0 -0
  41. {leyline_code-0.8.1 → leyline_code-0.9.1}/skills/leyline-tour/SKILL.md +0 -0
  42. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/adapters/__init__.py +0 -0
  43. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/adapters/csharp.py +0 -0
  44. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/adapters/generic.py +0 -0
  45. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/adapters/python.py +0 -0
  46. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/adapters/typescript.py +0 -0
  47. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/affected.py +0 -0
  48. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/agent_skills.py +0 -0
  49. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/change.py +0 -0
  50. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/channels.py +0 -0
  51. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/context.py +0 -0
  52. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/coverage.py +0 -0
  53. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/diagrams.py +0 -0
  54. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/diff.py +0 -0
  55. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/drift.py +0 -0
  56. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/exact.py +0 -0
  57. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/fncoupling.py +0 -0
  58. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/formats.py +0 -0
  59. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/grade.py +0 -0
  60. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/incremental.py +0 -0
  61. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/indexer.py +0 -0
  62. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/learnings.py +0 -0
  63. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/model.py +0 -0
  64. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/packing.py +0 -0
  65. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/patterns.py +0 -0
  66. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/pr.py +0 -0
  67. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/props.py +0 -0
  68. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/quick.py +0 -0
  69. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/related.py +0 -0
  70. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/removal.py +0 -0
  71. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/renames.py +0 -0
  72. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/rereview.py +0 -0
  73. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/roslyn_refs/Program.cs +0 -0
  74. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/roslyn_refs/RoslynRefs.csproj +0 -0
  75. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/rules.py +0 -0
  76. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/schema.sql +0 -0
  77. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/scip_pb2.py +0 -0
  78. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/spec.py +0 -0
  79. {leyline_code-0.8.1 → leyline_code-0.9.1}/src/leyline/verdicts.py +0 -0
  80. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/conftest.py +0 -0
  81. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/App/App.csproj +0 -0
  82. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/App/Checks.cs +0 -0
  83. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/App/Program.cs +0 -0
  84. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/Lib/Bus.cs +0 -0
  85. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/Lib/Lib.csproj +0 -0
  86. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/Lib/Program.cs +0 -0
  87. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/Lib/Shapes.cs +0 -0
  88. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/scripts/launch.py +0 -0
  89. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/scripts/run.py +0 -0
  90. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture/scripts/util.py +0 -0
  91. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Boxes.cs +0 -0
  92. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Fluent.cs +0 -0
  93. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Hard.cs +0 -0
  94. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Made.cs +0 -0
  95. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Mod.csproj +0 -0
  96. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Reports.cs +0 -0
  97. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Runner.cs +0 -0
  98. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/Shapes.cs +0 -0
  99. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/cs/Mod/State.cs +0 -0
  100. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/py/src/pkg/__init__.py +0 -0
  101. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/py/src/pkg/core.py +0 -0
  102. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/py/tests/conftest.py +0 -0
  103. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/py/tests/test_engine.py +0 -0
  104. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/py/web/app.py +0 -0
  105. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture2/py/web/client.py +0 -0
  106. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/package.json +0 -0
  107. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/app/package.json +0 -0
  108. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/app/src/main.test.ts +0 -0
  109. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/app/src/main.tsx +0 -0
  110. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/app/src/server.ts +0 -0
  111. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/core/package.json +0 -0
  112. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/core/src/graph.ts +0 -0
  113. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/core/src/ids.ts +0 -0
  114. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture3/pkg/core/src/index.ts +0 -0
  115. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/go/go.mod +0 -0
  116. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/go/main.go +0 -0
  117. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/go/main_test.go +0 -0
  118. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/go/shapes/shape.go +0 -0
  119. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/java/com/acme/App.java +0 -0
  120. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/java/com/acme/Counter.java +0 -0
  121. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/rs/Cargo.toml +0 -0
  122. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/rs/src/geo.rs +0 -0
  123. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture4/rs/src/main.rs +0 -0
  124. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/app.py +0 -0
  125. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/audit.py +0 -0
  126. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/client.py +0 -0
  127. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/consumer.py +0 -0
  128. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/container.py +0 -0
  129. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/db.py +0 -0
  130. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/migrations/0001_audit.py +0 -0
  131. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/models.py +0 -0
  132. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/tasks.py +0 -0
  133. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/py/tests/test_models.py +0 -0
  134. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/rpc/py/client.py +0 -0
  135. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/rpc/py/server.py +0 -0
  136. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/shop/Billing.cs +0 -0
  137. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/shop/OrderService.cs +0 -0
  138. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/shop/Program.cs +0 -0
  139. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/shop/Shop.csproj +0 -0
  140. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/shop/Store.cs +0 -0
  141. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/ts/events.ts +0 -0
  142. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/ts/nest.ts +0 -0
  143. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/ts/repo.ts +0 -0
  144. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture5/ts/subs.ts +0 -0
  145. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture6/tests/test_web.py +0 -0
  146. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture6/web/__init__.py +0 -0
  147. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture6/web/app.py +0 -0
  148. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture6/web/config.py +0 -0
  149. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture6/web/globals.py +0 -0
  150. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_props/fastcheck_vitest_tap.txt +0 -0
  151. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_props/fscheck_dotnet_test.txt +0 -0
  152. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_props/hypothesis_6.100.txt +0 -0
  153. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_props/hypothesis_6.168.txt +0 -0
  154. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_ws/wsapp/pyproject.toml +0 -0
  155. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_ws/wsapp/src/apppkg/__init__.py +0 -0
  156. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_ws/wsapp/src/apppkg/main.py +0 -0
  157. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_ws/wsapp/tests/test_main.py +0 -0
  158. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_ws/wslib/pyproject.toml +0 -0
  159. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_ws/wslib/src/libpkg/__init__.py +0 -0
  160. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/fixture_ws/wslib/src/libpkg/core.py +0 -0
  161. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/store_identity.py +0 -0
  162. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_adapters.py +0 -0
  163. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_before.py +0 -0
  164. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_channels.py +0 -0
  165. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_context.py +0 -0
  166. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_coupling.py +0 -0
  167. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_diagrams.py +0 -0
  168. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_drift.py +0 -0
  169. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_fixes.py +0 -0
  170. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_fncoupling.py +0 -0
  171. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_formats.py +0 -0
  172. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_handlers.py +0 -0
  173. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_incremental.py +0 -0
  174. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_learnings.py +0 -0
  175. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_learnings_synonyms.py +0 -0
  176. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_learnings_workspace.py +0 -0
  177. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_memory.py +0 -0
  178. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_outline.py +0 -0
  179. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_pr.py +0 -0
  180. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_props.py +0 -0
  181. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_quick.py +0 -0
  182. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_rereview.py +0 -0
  183. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_robust.py +0 -0
  184. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_scenario_coverage.py +0 -0
  185. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_security.py +0 -0
  186. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_shapes.py +0 -0
  187. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_signalfix.py +0 -0
  188. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_skills.py +0 -0
  189. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_state.py +0 -0
  190. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_trust.py +0 -0
  191. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_verdicts.py +0 -0
  192. {leyline_code-0.8.1 → leyline_code-0.9.1}/tests/test_viewer.py +0 -0
@@ -0,0 +1,389 @@
1
+ Metadata-Version: 2.5
2
+ Name: leyline-code
3
+ Version: 0.9.1
4
+ Summary: Map a codebase so a person can plan a change, have an agent write the code, and check that it was done as agreed.
5
+ Project-URL: Homepage, https://github.com/Orbitope/leyline
6
+ Project-URL: Issues, https://github.com/Orbitope/leyline/issues
7
+ License-Expression: GPL-3.0-only
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: mcp>=1.0
11
+ Requires-Dist: networkx>=3.0
12
+ Requires-Dist: tree-sitter-c-sharp>=0.23
13
+ Requires-Dist: tree-sitter-python>=0.23
14
+ Requires-Dist: tree-sitter-typescript>=0.23
15
+ Requires-Dist: tree-sitter>=0.23
16
+ Provides-Extra: languages
17
+ Requires-Dist: tree-sitter-bash; extra == 'languages'
18
+ Requires-Dist: tree-sitter-c; extra == 'languages'
19
+ Requires-Dist: tree-sitter-cpp; extra == 'languages'
20
+ Requires-Dist: tree-sitter-gdscript; extra == 'languages'
21
+ Requires-Dist: tree-sitter-go; extra == 'languages'
22
+ Requires-Dist: tree-sitter-java; extra == 'languages'
23
+ Requires-Dist: tree-sitter-kotlin; extra == 'languages'
24
+ Requires-Dist: tree-sitter-lua; extra == 'languages'
25
+ Requires-Dist: tree-sitter-php; extra == 'languages'
26
+ Requires-Dist: tree-sitter-ruby; extra == 'languages'
27
+ Requires-Dist: tree-sitter-rust; extra == 'languages'
28
+ Requires-Dist: tree-sitter-scala; extra == 'languages'
29
+ Requires-Dist: tree-sitter-swift; extra == 'languages'
30
+ Provides-Extra: scip
31
+ Requires-Dist: protobuf>=7.35; extra == 'scip'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Leyline
35
+
36
+ ## Working through an agent
37
+
38
+ Leyline is meant to be driven by a coding agent: you ask in plain words, the agent uses Leyline's map and follows
39
+ one of its skills, and you read the answer or the page it writes. Set it up once, in the repository you will work on:
40
+
41
+ ```bash
42
+ pip install leyline-code # or, from a checkout: pip install -e .
43
+ claude mcp add leyline -- leyline serve # connect Claude Code to Leyline's MCP server
44
+ leyline skills install # copy the skills into .claude/skills (and .agents/skills if you have one)
45
+ ```
46
+
47
+ Start a new agent session, then ask. Nothing needs mapping first: the agent maps the code when it needs to.
48
+
49
+ | Skill | What it does | Ask, for example |
50
+ | --- | --- | --- |
51
+ | `leyline-ask` | Answers a question about the code, citing it, and says how sure it is | "Who calls `saveOne`, and is that link certain?" |
52
+ | `leyline-explain-flow` | Explains how something runs, step by step, from the map | "What happens when a user saves a dialogue?" |
53
+ | `leyline-explore-module` | Says what one part of the code holds and where to start reading | "What is in `editor/host`, and where do I start?" |
54
+ | `leyline-change-impact` | Says what a described change would touch, break and need tested | "What would changing the validator's output format affect?" |
55
+ | `leyline-spec` | Plans a change as an OpenSpec folder, checked against the code, then checks it after | "Plan adding an export button, with tests." |
56
+ | `leyline-quick-change` | Makes a small fix with no spec and says whether it was done; says when it needs a spec | "Make the retry count 3." |
57
+ | `leyline-pr-review` | Reviews a pull request: what changed, what it breaks, findings, what to fix first | "Review pull request 123." |
58
+ | `leyline-adversarial-review` | Attacks a plan or a pull request as a logic or a performance reviewer | "Stress-test the plan for loud-engine." |
59
+ | `leyline-tour` | Writes a guided reading order through the code, saved on the map | "Write a tour of the payment code for a new hire." |
60
+
61
+ `leyline skills list` describes them, `leyline skills show <name>` prints one, and running `leyline skills install`
62
+ again updates them, leaving alone any you edited (`--force` replaces those). An agent connected to `leyline serve`
63
+ can also load each skill as an MCP prompt of the same name, or through the `skills` tool, without installing them.
64
+ The rest of this page is what the agent does underneath, and the commands for doing it yourself.
65
+
66
+ ## Start here
67
+
68
+ To understand a codebase, map it and look around. Names work wherever an id does (`Engine.start`, a file path):
69
+
70
+ ```bash
71
+ leyline map path/to/repo # once; writes .leyline/map.html, which opens on a guided tour
72
+ leyline tour # what this repository is, and where to start reading
73
+ leyline show Engine.start # one thing: what it is, who uses it, what it uses, the code around it
74
+ leyline show "what happens when a user saves" # where a behavior described in words starts
75
+ leyline outline editor/core # a large module split into parts, with what each holds
76
+ leyline explain-path DialogueCanvas.save # what runs from there, step by step, with a diagram
77
+ ```
78
+
79
+ Or ask your agent in plain words (see [Working through an agent](#working-through-an-agent)). One page of what to
80
+ ask and the command or tool behind it: [docs/quick-reference.md](docs/quick-reference.md).
81
+
82
+ Leyline also lets a person design a change to a codebase while a coding agent writes it. Before any code is
83
+ written it shows, on one page, what code will be written, what it will affect and how you will know it
84
+ was done; afterwards it says on the same page whether the change was done as agreed. Everything on that
85
+ page comes from the code and the test results, not from the agent's account of its own work.
86
+
87
+ ```bash
88
+ pip install -e . # Python 3.10 or later; installs the `leyline` command
89
+ ```
90
+
91
+ Three commands, in this order. Each one ends with a `Next:` line saying what to do after it.
92
+
93
+ | Command | When | What you read |
94
+ | --- | --- | --- |
95
+ | `leyline map [repo ...]` | Once, before the first change | Ten lines on what was found, and a map page to browse |
96
+ | `leyline plan <change>` | After the change is written as a spec, and after every edit to it | The page, `leyline.md`, and what is still needed before implementation |
97
+ | `leyline check <change> --tests FILE` | After the code is written | The verdict, added to the same page |
98
+
99
+ A change is an [OpenSpec](https://openspec.dev) folder, `openspec/changes/<id>/`: a proposal, the
100
+ requirements with scenarios, and a task list. You describe the change in words and your agent writes the
101
+ folder; `skills/leyline-spec/SKILL.md` tells it how. `<change>` is the folder or just its id.
102
+
103
+ A change someone else already wrote (a branch, a pull request) has no spec. Check it out and run
104
+ `leyline pr [base]` instead: see [Reviewing a pull request](docs/changes.md#reviewing-a-pull-request).
105
+
106
+ A one-line fix needs no spec either: `leyline quick "make the retry count 3" --about RETRIES` before, and
107
+ `leyline quick --done quick-make-the-retry-count-3 --tests -` after. See [A quick change](docs/changes.md#a-quick-change).
108
+
109
+ ### A worked example
110
+
111
+ The repository `tests/fixture2` has a small Python package with an `Engine` class and its tests.
112
+
113
+ ```bash
114
+ cp -r tests/fixture2 /tmp/engine-repo && cd /tmp/engine-repo
115
+ leyline map .
116
+ ```
117
+ ```
118
+ Mapped engine-repo in 0.8 s: 14 files, 354 lines, 4 modules.
119
+ Found 34 types, 73 functions, 5 tests and 1 entry point (where a program starts).
120
+ Modules: cs/Mod (8 files), py/src/pkg (2 files), py/tests (2 files), py/web (2 files)
121
+ Design patterns found: builder, composite, decorator, factory 2, singleton, strategy, template method
122
+ Store: .leyline/leyline.db
123
+ Map page: .leyline/map.html (open it in a browser)
124
+ Next: write the change you want as an OpenSpec folder, openspec/changes/<id>/ (ask your agent; ...
125
+ ```
126
+
127
+ You tell your agent: "engine names should come back in upper case, and add a `shout`". It writes
128
+ `openspec/changes/loud-engine/` with this `tasks.md`
129
+
130
+ ```
131
+ - [ ] 1.1 Change `Engine.start` to return the name in upper case
132
+ - [ ] 1.2 Add `Engine.shout`, the name with an exclamation mark
133
+ - [ ] 1.3 Add the test "Shout"
134
+ ```
135
+
136
+ and two scenarios, `Start` and `Shout`, in `specs/engine/spec.md`. Then you plan it, passing the tests'
137
+ output from before any code changes so that `check` can tell a test the change breaks from one that
138
+ already failed (TAP from vitest, node --test or tap, `pytest -rA`, `go test -v`, `jest --verbose`,
139
+ `dotnet test --logger "console;verbosity=detailed"`, or any runner
140
+ that prints one PASS or FAIL line per test):
141
+
142
+ ```bash
143
+ (cd py && PYTHONPATH=src pytest -rA tests) | leyline plan loud-engine --tests -
144
+ ```
145
+ ```
146
+ # Loud engine
147
+ ...
148
+ **State: ready to implement.** It has not been reviewed.
149
+
150
+ Names are hard to read in logs. The plan has 3 tasks: it changes Engine.start and adds Engine.shout.
151
+ Nothing else must be edited with it; 4 places in 1 module run into the changed code and may behave
152
+ differently; 3 existing tests already run through it. It is done when 2 scenarios pass: 1 already has a
153
+ test, 1 needs a test written.
154
+ ...
155
+ **Shares a caller or a field with the change, and no task names it.** Each line is either right to
156
+ leave alone or a missing task:
157
+ - Engine.name (used by Engine.start) is also used by Engine.child
158
+ ...
159
+ Next: have the plan reviewed (logic and performance) before code is written (ask your agent to run the
160
+ leyline-adversarial-review skill), then run `leyline plan loud-engine` again. Or, if you accept the plan
161
+ as it is, implement it.
162
+ ```
163
+
164
+ The agent implements the tasks. Then:
165
+
166
+ ```bash
167
+ (cd py && PYTHONPATH=src pytest -rA tests) | leyline check loud-engine --tests -
168
+ ```
169
+ ```
170
+ ## 4. Was it done as agreed
171
+ **Yes.** Every task is done, every scenario is proven, and nothing outside the spec changed.
172
+
173
+ | Task | Result | Verdict | Missing |
174
+ | 1.1 Change `Engine.start` to return the name in upper case | done | proven | |
175
+ ...
176
+ | Scenario | Result | Verdict | Evidence |
177
+ | Start | passes | proven | its test reaches the changed code on the map |
178
+ | Shout | passes | proven | its test reaches the changed code on the map |
179
+
180
+ Verdicts: 5 proven. Blocking: partial, contradicted, inconclusive. Not blocking: needs you. (The default.)
181
+
182
+ Tests: 5 of 5 passed before, 6 of 6 after.
183
+ Next: nothing left to check; the change was done as agreed. Review the diff and commit it.
184
+ ```
185
+
186
+ `check` exits 0 only when the change was done as agreed. Had the agent also edited `Engine.child`, the
187
+ verdict would read "Not yet: 1 edit is outside the spec", and `Next:` would ask you to add a task for it or
188
+ undo it.
189
+
190
+ ### What you read
191
+
192
+ `openspec/changes/<id>/leyline.md` is the one page for a change. It opens with its state (not ready,
193
+ ready, done as agreed or not) and a paragraph in plain words, then:
194
+
195
+ 1. **What code will be written**: each task and the code it touches.
196
+ 2. **What it will affect**: what must be edited with it, what runs into it, the risks, and code that
197
+ shares a caller or a field with the change that no task names.
198
+ 3. **How you will know it was done**: each scenario and the test that proves it.
199
+ 4. **Review findings** and **Before implementation**: what blocks implementation, and what is left to
200
+ decide. Only you resolve a finding.
201
+ 5. **Was it done as agreed**, added by `check`.
202
+
203
+ ### With an agent
204
+
205
+ The agent uses the same loop through Leyline's MCP server. To connect it to Claude Code, run this in
206
+ the root of the repository you will work on:
207
+
208
+ ```bash
209
+ claude mcp add leyline -- leyline serve # this repository only
210
+ claude mcp add -s user leyline -- leyline serve # or: every project you open
211
+ ```
212
+
213
+ and `leyline skills install` to give Claude Code the skills (see [Working through an agent](#working-through-an-agent)).
214
+
215
+ Claude Code starts the server in the directory it was opened in, and the server reads the store there,
216
+ `.leyline/leyline.db`, or, from a folder inside the repository, the nearest such store above it. Nothing needs to be mapped first: the agent's first call is `map`. If `leyline` is
217
+ installed in a virtual environment, give its full path (`-- /path/to/venv/bin/leyline serve`); for a
218
+ store elsewhere, such as a workspace mapped from another directory, add `-e LEYLINE_DB=/full/path/leyline.db`
219
+ before the `--`. `claude mcp list` should then show `leyline` as connected.
220
+
221
+ The agent's path is the same three tools, `map`, `plan` and `check`, each returning `next`, with the
222
+ review tools between plan and implementation; the server's instructions give the agent that order.
223
+ The skills in `skills/` say how to write the spec (`leyline-spec`), review it
224
+ (`leyline-adversarial-review`), assess any change (`leyline-change-impact`), explain the code
225
+ (`leyline-tour`) and answer how something works (`leyline-explain-flow`). Answers are kept short enough for an agent's context: long lists are cut, and the
226
+ answer says what was cut (`cut`) and how to see the rest (`more`).
227
+
228
+ Everything below is the detail: the other commands, how the map is built, and how far to trust it.
229
+ `leyline --help` lists the other commands under "advanced".
230
+
231
+ ## What Leyline is
232
+
233
+ Leyline maps a codebase into one SQLite graph that people and coding agents can both query.
234
+ Every record belongs to one of three layers:
235
+
236
+ - **fact**: extracted deterministically from the source, rebuilt on every index run
237
+ - **inferred**: written by an LLM, with evidence and a confidence
238
+ - **intent**: written by you, such as boundaries and rules
239
+
240
+ This version builds the fact layer for C# and Python, proposes systems by clustering, labels design
241
+ patterns by their shape, generates an orientation tour, assesses a described change before it is made,
242
+ reviews it after, and checks architecture rules.
243
+
244
+ ## Use
245
+
246
+ ```bash
247
+ pip install -e .
248
+ leyline map path/to/repo # writes path/to/repo/.leyline/leyline.db and .leyline/map.html
249
+ leyline index path/to/repo # the same index, printing the full statistics instead
250
+ leyline --db ... coverage .coverage # import measured test coverage
251
+ leyline --db path/to/repo/.leyline/leyline.db overview
252
+ leyline --db ... search "Simulation Step"
253
+ leyline --db ... show "Simulation.Step" # one thing in detail (expand --json for every field)
254
+ leyline --db ... tour # a guided walk through the repository
255
+ leyline --db ... patterns # design patterns found by their shape
256
+ leyline --db ... state # fields assigned from outside their own type
257
+ leyline --db ... serve # MCP server over stdio
258
+ leyline --db ... view # the map, served on http://127.0.0.1:8765
259
+ leyline --db ... export -o map.html # the map as one self-contained page
260
+ ```
261
+
262
+ To use it from an MCP client, register the command `leyline --db <path> serve`.
263
+
264
+ ### Several repositories
265
+
266
+ ```bash
267
+ leyline map flask/ werkzeug/ # one workspace; writes ./.leyline/leyline.db
268
+ leyline --db .leyline/leyline.db index werkzeug/ # re-indexes flask too
269
+ ```
270
+
271
+ Repositories named together are indexed in one run, so a name in one resolves to its declaration in
272
+ the other: `import werkzeug` in Flask reaches Werkzeug's source (a package under `src/` by its package
273
+ name), a Flask class extends Werkzeug's, and a Werkzeug method that calls `self.open()` dispatches
274
+ into Flask's override. Ids keep their own repo's prefix, so a store indexed one repository at a time
275
+ reads the same. The store remembers its members: indexing any one of them later indexes all of them
276
+ again, which keeps the links between them. A member whose directory has gone is left as stored, and `map` says so.
277
+ Name its new place and it is the same member, under its old id, not a second copy, when Leyline can tell it is the
278
+ same repository: both are git repositories with the same first commit, and the same `origin` where both have one.
279
+ Anything less sure (not under git, or a store mapped before Leyline recorded first commits) is mapped as a new
280
+ member; drop the old one with `leyline map --forget <id>`. That drops a member's facts, where it was and its place
281
+ in the workspace, then maps the others again in full so no link into it is left; notes and review decisions about
282
+ its code are kept, as when code is deleted.
283
+
284
+ `overview` lists the links between repositories; the `cross_repo` tool adds the functions most called
285
+ across and the flows that cross and come back. An edge between repositories carries `to_repo`;
286
+ `impact` and a change assessment count what they reach per repo and flag a change that reaches into
287
+ another one. Not yet: the C# compiler pass (SCIP indexes are matched to their repository by the
288
+ directory they were made in), and values handed across that the syntax pass cannot follow, such as
289
+ the WSGI app a test client calls.
290
+
291
+ ### Someone else's repository
292
+
293
+ Leyline is made to be run on code you did not write: a pull request under review, a project you just
294
+ cloned. What such a repository holds is treated as its author's, not as Leyline's or yours:
295
+
296
+ - **`.leyline/` is Leyline's own.** Its files are local and rebuilt by `leyline map`, so a repository
297
+ that tracks files there (a pull request can commit a store with annotations, resolved findings and
298
+ rules of its author's making, a snapshot or a review page, and checking it out writes over your
299
+ ignored copy) is refused, with the files named. Stop tracking them (`git rm -r --cached .leyline`)
300
+ or keep the store elsewhere (`--db /elsewhere/leyline.db`, or `LEYLINE_DB`). A `.leyline` that is
301
+ a link to a folder outside the repository is refused too, so nothing is written there.
302
+ `LEYLINE_TRUST_STORE=1` uses the folder anyway, for a store you committed on purpose.
303
+ - **Nothing is written outside the repository through a link.** A file Leyline writes (the map page,
304
+ a change's `leyline.md`, the anchors and learnings files) replaces a link at its name rather than
305
+ writing through it; a change folder or `openspec/` that is a link to a folder outside the
306
+ repository is read but not written to, and the plan or check says so ("Not written: ...").
307
+ - **A pull request does not vouch for itself.** A learnings file records what reviewers decided
308
+ before, so one a pull request commits ("f may return anything, do not flag it") would steer its own
309
+ review. Learnings the pull request's commits add or change (compared with its base) are not applied
310
+ to it: not in `learnings_that_apply`, not matched against its findings. Its page lists them under
311
+ "Learnings added by this pull request, not applied", for the person to judge.
312
+ - **An agent's paths stay inside the map.** The MCP tools that take a path (`map`'s `paths`, the change
313
+ folder of `plan`, `check` and the `spec_*` tools, `affected_tests`, `drift`'s `path`, `review_pr`'s
314
+ `path`, a coverage file for `check`, `quick` or `coverage`) accept only paths inside the
315
+ repositories the store maps, or, before anything is mapped, the directory the server was started
316
+ in. An agent can be told which path to pass by text it read; the error says the person can run the
317
+ `leyline` command themselves, which takes any path.
318
+
319
+ ## How the pieces fit
320
+
321
+ Everything lives in one file: `.leyline/leyline.db` inside the repository you indexed. Three things
322
+ read and write it, and none of them runs unless you start it. Beside it, `leyline.cache.db` holds what the
323
+ next index needs to do again only what an edit changed: each file's parse output under its content hash,
324
+ and fingerprints of the last run. Deleting it only makes the next index a full one, and so does damage to it: a
325
+ cache that is not a database is started again.
326
+
327
+ | Piece | Started by | Reads | Writes |
328
+ | --- | --- | --- | --- |
329
+ | `leyline map` or `index` | You, a git hook or CI; `plan` and `check` when the code changed | The working tree | Facts, flows and system proposals. Replaces the previous facts: after the first run only what changed is redone, and the store comes out as a full run would leave it (`--full` forces one). |
330
+ | `leyline serve` (MCP) | Your coding agent, when it starts | The store | Annotations, change proposals and saved views |
331
+ | `leyline view` or `export` | You | The store | Nothing |
332
+
333
+ One index runs at a time per store. A second one that starts while another is running (a `map` while a `plan`
334
+ re-maps) says so on stderr, naming the other process and how long it has been going, waits for it to finish,
335
+ and then maps what changed since. It waits up to an hour (`LEYLINE_WAIT`, in seconds), and past that stops with
336
+ the same message. A run that dies lets go of the store with its process.
337
+
338
+ Annotations, proposals and views are not facts, so re-indexing keeps them. An annotation is flagged
339
+ stale when the code behind its evidence changes.
340
+
341
+ ## Further reading
342
+
343
+ The rest of the documentation is in `docs/`:
344
+
345
+ - [quick-reference.md](docs/quick-reference.md): one page of what to ask, and the command or tool behind it.
346
+ - [changes.md](docs/changes.md): planning, reviewing and checking a change: the spec loop, spec drift, assessing a
347
+ change, reviewing a pull request, a quick change, learning from rejected findings, sequence diagrams, rules.
348
+ - [understanding.md](docs/understanding.md): what the map finds and how to ask it: exact references, measured
349
+ coverage, field reads and writes, design patterns, tours, change coupling, outlines, asking how something works,
350
+ large modules, the map page.
351
+ - [mcp-tools.md](docs/mcp-tools.md): every MCP tool and its arguments.
352
+ - [indexing.md](docs/indexing.md): which files and languages are read, how calls and channels are resolved, systems,
353
+ annotations, flows, and how well it holds up.
354
+
355
+ ## Identity
356
+
357
+ Node ids are stable across file moves within a project:
358
+
359
+ - C#: `repo:csharp:Project::Namespace.Type.Member(ParamTypes)`. The project is part of the id
360
+ because two projects may declare the same type name.
361
+ - Python: `repo:python:package.module.Class.method`
362
+ - TypeScript, JavaScript and the generic languages: `repo:language:dir.file.Name`, the file named without its
363
+ extension. Two files of one language in one directory that differ only in their extension (`a.js` beside
364
+ `a.ts`, `m.c` beside `m.h`) would name their contents alike: the first by path keeps that id, and the others
365
+ keep their extension in it (`repo:typescript:dir.a.ts.Name`). Adding or removing such a neighbour can change
366
+ the ids of the files after it.
367
+ - Files and modules: `repo:file:path` and `repo:module:path`
368
+
369
+ A name defined twice in one file (`def load()` in both branches of an `if` or a `try`, a property's getter and
370
+ setter) is one node: which definition runs is not known from the text. Its span runs from the first definition
371
+ to the last, its `definitions` attribute lists each one's lines, its text hash reads every body, and its calls
372
+ come from all of them.
373
+
374
+ ## Tests
375
+
376
+ ```bash
377
+ pytest
378
+ ```
379
+
380
+ A new skill is a folder under `skills/` with a `SKILL.md`. That folder is the only copy: the wheel carries it as
381
+ `leyline/skills/`, and `leyline skills`, the MCP prompts and the `skills` tool pick it up with no other edit.
382
+ `tests/test_skills.py` checks each skill's frontmatter and that the wheel's copy matches.
383
+
384
+ ## License
385
+
386
+ Copyright (C) 2026 Matthew Burke.
387
+
388
+ Leyline is free software, licensed under the GNU General Public License, version 3.
389
+ See [LICENSE](LICENSE) for the full text. It comes with no warranty.