kodit 0.3.7__tar.gz → 0.3.9__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.

Potentially problematic release.


This version of kodit might be problematic. Click here for more details.

Files changed (241) hide show
  1. {kodit-0.3.7 → kodit-0.3.9}/.github/workflows/docker.yaml +2 -2
  2. {kodit-0.3.7 → kodit-0.3.9}/Dockerfile +4 -0
  3. {kodit-0.3.7 → kodit-0.3.9}/PKG-INFO +1 -1
  4. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/deployment/docker-compose.yaml +12 -3
  5. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/mcp/index.md +4 -2
  6. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/_version.py +2 -2
  7. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/config.py +3 -2
  8. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/cloning/git/working_copy.py +23 -2
  9. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/slicing/slicer.py +15 -6
  10. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/cloning/git_cloning/working_copy_test.py +137 -1
  11. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/slicer_test.py +60 -3
  12. {kodit-0.3.7 → kodit-0.3.9}/tests/smoke.sh +3 -0
  13. kodit-0.3.7/docs/MIGRATION_TO_INDEX_AGGREGATE.md +0 -222
  14. {kodit-0.3.7 → kodit-0.3.9}/.claude/commands/debug.md +0 -0
  15. {kodit-0.3.7 → kodit-0.3.9}/.claude/commands/new-requirement.md +0 -0
  16. {kodit-0.3.7 → kodit-0.3.9}/.claude/commands/refactor.md +0 -0
  17. {kodit-0.3.7 → kodit-0.3.9}/.claude/commands/update-docs.md +0 -0
  18. {kodit-0.3.7 → kodit-0.3.9}/.claude/settings.json +0 -0
  19. {kodit-0.3.7 → kodit-0.3.9}/.cursor/rules/kodit.mdc +0 -0
  20. {kodit-0.3.7 → kodit-0.3.9}/.cursor/rules/style.mdc +0 -0
  21. {kodit-0.3.7 → kodit-0.3.9}/.dockerignore +0 -0
  22. {kodit-0.3.7 → kodit-0.3.9}/.github/CODE_OF_CONDUCT.md +0 -0
  23. {kodit-0.3.7 → kodit-0.3.9}/.github/CONTRIBUTING.md +0 -0
  24. {kodit-0.3.7 → kodit-0.3.9}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {kodit-0.3.7 → kodit-0.3.9}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  26. {kodit-0.3.7 → kodit-0.3.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  27. {kodit-0.3.7 → kodit-0.3.9}/.github/dependabot.yml +0 -0
  28. {kodit-0.3.7 → kodit-0.3.9}/.github/workflows/docs.yaml +0 -0
  29. {kodit-0.3.7 → kodit-0.3.9}/.github/workflows/pull_request.yaml +0 -0
  30. {kodit-0.3.7 → kodit-0.3.9}/.github/workflows/pypi-test.yaml +0 -0
  31. {kodit-0.3.7 → kodit-0.3.9}/.github/workflows/pypi.yaml +0 -0
  32. {kodit-0.3.7 → kodit-0.3.9}/.github/workflows/test.yaml +0 -0
  33. {kodit-0.3.7 → kodit-0.3.9}/.gitignore +0 -0
  34. {kodit-0.3.7 → kodit-0.3.9}/.python-version +0 -0
  35. {kodit-0.3.7 → kodit-0.3.9}/.vscode/launch.json +0 -0
  36. {kodit-0.3.7 → kodit-0.3.9}/.vscode/settings.json +0 -0
  37. {kodit-0.3.7 → kodit-0.3.9}/CLAUDE.md +0 -0
  38. {kodit-0.3.7 → kodit-0.3.9}/LICENSE +0 -0
  39. {kodit-0.3.7 → kodit-0.3.9}/README.md +0 -0
  40. {kodit-0.3.7 → kodit-0.3.9}/alembic.ini +0 -0
  41. {kodit-0.3.7 → kodit-0.3.9}/docs/_index.md +0 -0
  42. {kodit-0.3.7 → kodit-0.3.9}/docs/demos/_index.md +0 -0
  43. {kodit-0.3.7 → kodit-0.3.9}/docs/demos/go-simple-microservice/index.md +0 -0
  44. {kodit-0.3.7 → kodit-0.3.9}/docs/demos/knock-knock-auth/index.md +0 -0
  45. {kodit-0.3.7 → kodit-0.3.9}/docs/developer/index.md +0 -0
  46. {kodit-0.3.7 → kodit-0.3.9}/docs/getting-started/_index.md +0 -0
  47. {kodit-0.3.7 → kodit-0.3.9}/docs/getting-started/installation/index.md +0 -0
  48. {kodit-0.3.7 → kodit-0.3.9}/docs/getting-started/integration/index.md +0 -0
  49. {kodit-0.3.7 → kodit-0.3.9}/docs/getting-started/quick-start/index.md +0 -0
  50. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/_index.md +0 -0
  51. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/configuration/index.md +0 -0
  52. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/deployment/index.md +0 -0
  53. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/deployment/kubernetes.yaml +0 -0
  54. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/indexing/index.md +0 -0
  55. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/sync/index.md +0 -0
  56. {kodit-0.3.7 → kodit-0.3.9}/docs/reference/telemetry/index.md +0 -0
  57. {kodit-0.3.7 → kodit-0.3.9}/pyproject.toml +0 -0
  58. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/.gitignore +0 -0
  59. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/__init__.py +0 -0
  60. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/app.py +0 -0
  61. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/application/__init__.py +0 -0
  62. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/application/factories/__init__.py +0 -0
  63. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/application/factories/code_indexing_factory.py +0 -0
  64. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/application/services/__init__.py +0 -0
  65. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/application/services/code_indexing_application_service.py +0 -0
  66. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/application/services/sync_scheduler.py +0 -0
  67. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/cli.py +0 -0
  68. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/database.py +0 -0
  69. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/__init__.py +0 -0
  70. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/entities.py +0 -0
  71. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/errors.py +0 -0
  72. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/interfaces.py +0 -0
  73. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/protocols.py +0 -0
  74. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/services/__init__.py +0 -0
  75. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/services/bm25_service.py +0 -0
  76. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/services/embedding_service.py +0 -0
  77. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/services/enrichment_service.py +0 -0
  78. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/services/index_query_service.py +0 -0
  79. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/services/index_service.py +0 -0
  80. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/domain/value_objects.py +0 -0
  81. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/__init__.py +0 -0
  82. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/bm25/__init__.py +0 -0
  83. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/bm25/bm25_factory.py +0 -0
  84. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/bm25/local_bm25_repository.py +0 -0
  85. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/bm25/vectorchord_bm25_repository.py +0 -0
  86. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/cloning/__init__.py +0 -0
  87. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/cloning/git/__init__.py +0 -0
  88. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/cloning/metadata.py +0 -0
  89. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/__init__.py +0 -0
  90. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/embedding_factory.py +0 -0
  91. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/embedding_providers/__init__.py +0 -0
  92. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/embedding_providers/batching.py +0 -0
  93. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/embedding_providers/hash_embedding_provider.py +0 -0
  94. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/embedding_providers/local_embedding_provider.py +0 -0
  95. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/embedding_providers/openai_embedding_provider.py +0 -0
  96. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/local_vector_search_repository.py +0 -0
  97. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/embedding/vectorchord_vector_search_repository.py +0 -0
  98. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/enrichment/__init__.py +0 -0
  99. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/enrichment/enrichment_factory.py +0 -0
  100. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/enrichment/local_enrichment_provider.py +0 -0
  101. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/enrichment/null_enrichment_provider.py +0 -0
  102. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/enrichment/openai_enrichment_provider.py +0 -0
  103. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/git/__init__.py +0 -0
  104. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/git/git_utils.py +0 -0
  105. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/ignore/__init__.py +0 -0
  106. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/ignore/ignore_pattern_provider.py +0 -0
  107. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/indexing/__init__.py +0 -0
  108. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/indexing/auto_indexing_service.py +0 -0
  109. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/indexing/fusion_service.py +0 -0
  110. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/mappers/__init__.py +0 -0
  111. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/mappers/index_mapper.py +0 -0
  112. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/slicing/__init__.py +0 -0
  113. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/slicing/language_detection_service.py +0 -0
  114. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/sqlalchemy/__init__.py +0 -0
  115. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/sqlalchemy/embedding_repository.py +0 -0
  116. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/sqlalchemy/entities.py +0 -0
  117. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/sqlalchemy/index_repository.py +0 -0
  118. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/ui/__init__.py +0 -0
  119. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/ui/progress.py +0 -0
  120. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/infrastructure/ui/spinner.py +0 -0
  121. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/log.py +0 -0
  122. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/mcp.py +0 -0
  123. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/middleware.py +0 -0
  124. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/README +0 -0
  125. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/__init__.py +0 -0
  126. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/env.py +0 -0
  127. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/script.py.mako +0 -0
  128. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/versions/4073b33f9436_add_file_processing_flag.py +0 -0
  129. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/versions/4552eb3f23ce_add_summary.py +0 -0
  130. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/versions/7c3bbc2ab32b_add_embeddings_table.py +0 -0
  131. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/versions/85155663351e_initial.py +0 -0
  132. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/versions/9e53ea8bb3b0_add_authors.py +0 -0
  133. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/versions/__init__.py +0 -0
  134. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/migrations/versions/c3f5137d30f5_index_all_the_things.py +0 -0
  135. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/reporting.py +0 -0
  136. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/utils/__init__.py +0 -0
  137. {kodit-0.3.7 → kodit-0.3.9}/src/kodit/utils/path_utils.py +0 -0
  138. {kodit-0.3.7 → kodit-0.3.9}/tests/__init__.py +0 -0
  139. {kodit-0.3.7 → kodit-0.3.9}/tests/conftest.py +0 -0
  140. {kodit-0.3.7 → kodit-0.3.9}/tests/docker-smoke.sh +0 -0
  141. {kodit-0.3.7 → kodit-0.3.9}/tests/experiments/__init__.py +0 -0
  142. {kodit-0.3.7 → kodit-0.3.9}/tests/experiments/cline_prompt_tests/__init__.py +0 -0
  143. {kodit-0.3.7 → kodit-0.3.9}/tests/experiments/cline_prompt_tests/cline_prompt.txt +0 -0
  144. {kodit-0.3.7 → kodit-0.3.9}/tests/experiments/cline_prompt_tests/cline_prompt_test.py +0 -0
  145. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/__init__.py +0 -0
  146. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/application/__init__.py +0 -0
  147. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/application/services/__init__.py +0 -0
  148. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/application/services/test_sync_scheduler.py +0 -0
  149. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/application/test_code_indexing_application_service.py +0 -0
  150. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/cli_test.py +0 -0
  151. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/config_test.py +0 -0
  152. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/__init__.py +0 -0
  153. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/bm25_domain_service_test.py +0 -0
  154. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/enrichment_domain_service_test.py +0 -0
  155. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/entities_test.py +0 -0
  156. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/services/__init__.py +0 -0
  157. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/services/index_service_test.py +0 -0
  158. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/test_embedding_service.py +0 -0
  159. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/test_language_mapping.py +0 -0
  160. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/domain/test_multi_search_result.py +0 -0
  161. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/e2e.py +0 -0
  162. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/__init__.py +0 -0
  163. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/bm25/__init__.py +0 -0
  164. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/bm25/local_bm25_repository_test.py +0 -0
  165. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/bm25/vectorchord_bm25_repository_test.py +0 -0
  166. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/cloning/git_cloning/__init__.py +0 -0
  167. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/__init__.py +0 -0
  168. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/embedding_factory_test.py +0 -0
  169. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/embedding_provider/__init__.py +0 -0
  170. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/embedding_provider/test_hash_embedding_provider.py +0 -0
  171. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/embedding_provider/test_local_embedding_provider.py +0 -0
  172. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/embedding_provider/test_openai_embedding_provider.py +0 -0
  173. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/test_batching.py +0 -0
  174. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/test_embedding_integration.py +0 -0
  175. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/test_local_vector_search_repository.py +0 -0
  176. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/embedding/test_vectorchord_vector_search_repository.py +0 -0
  177. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/enrichment/__init__.py +0 -0
  178. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/enrichment/enrichment_provider/__init__.py +0 -0
  179. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/enrichment/enrichment_provider/test_local_enrichment_provider.py +0 -0
  180. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/enrichment/enrichment_provider/test_null_enrichment_provider.py +0 -0
  181. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/enrichment/enrichment_provider/test_openai_enrichment_provider.py +0 -0
  182. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/enrichment/test_enrichment_factory.py +0 -0
  183. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/indexing/__init__.py +0 -0
  184. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/indexing/test_auto_indexing_service.py +0 -0
  185. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/mappers/__init__.py +0 -0
  186. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/mappers/test_index_mapper.py +0 -0
  187. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/__init__.py +0 -0
  188. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/__init__.py +0 -0
  189. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/c/main.c +0 -0
  190. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/c/models.c +0 -0
  191. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/c/models.h +0 -0
  192. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/c/utils.c +0 -0
  193. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/c/utils.h +0 -0
  194. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/cpp/main.cpp +0 -0
  195. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/cpp/models.cpp +0 -0
  196. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/cpp/models.hpp +0 -0
  197. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/cpp/utils.cpp +0 -0
  198. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/cpp/utils.hpp +0 -0
  199. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/csharp/Main.cs +0 -0
  200. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/csharp/Models.cs +0 -0
  201. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/csharp/Utils.cs +0 -0
  202. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/css/components.css +0 -0
  203. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/css/main.css +0 -0
  204. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/css/utilities.css +0 -0
  205. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/go/main.go +0 -0
  206. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/go/models.go +0 -0
  207. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/go/utils.go +0 -0
  208. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/html/components.html +0 -0
  209. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/html/forms.html +0 -0
  210. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/html/main.html +0 -0
  211. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/java/Main.java +0 -0
  212. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/java/Models.java +0 -0
  213. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/java/Utils.java +0 -0
  214. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/javascript/main.js +0 -0
  215. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/javascript/models.js +0 -0
  216. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/javascript/utils.js +0 -0
  217. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/python/__init__.py +0 -0
  218. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/python/main.py +0 -0
  219. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/python/models.py +0 -0
  220. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/python/utils.py +0 -0
  221. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/rust/main.rs +0 -0
  222. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/rust/models.rs +0 -0
  223. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/slicing/data/rust/utils.rs +0 -0
  224. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/snippets/__init__.py +0 -0
  225. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/snippets/csharp.cs +0 -0
  226. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/snippets/golang.go +0 -0
  227. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/snippets/javascript.js +0 -0
  228. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/snippets/knock_knock_server.py +0 -0
  229. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/snippets/python.py +0 -0
  230. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/snippets/typescript.tsx +0 -0
  231. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/sqlalchemy/__init__.py +0 -0
  232. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/infrastructure/sqlalchemy/test_embedding_repository.py +0 -0
  233. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/log_test.py +0 -0
  234. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/mcp_stdio_test.py +0 -0
  235. {kodit-0.3.7 → kodit-0.3.9}/tests/kodit/mcp_test.py +0 -0
  236. {kodit-0.3.7 → kodit-0.3.9}/tests/performance/__init__.py +0 -0
  237. {kodit-0.3.7 → kodit-0.3.9}/tests/performance/similarity.py +0 -0
  238. {kodit-0.3.7 → kodit-0.3.9}/tests/utils/__init__.py +0 -0
  239. {kodit-0.3.7 → kodit-0.3.9}/tests/utils/test_path_utils.py +0 -0
  240. {kodit-0.3.7 → kodit-0.3.9}/tests/vectorchord-smoke.sh +0 -0
  241. {kodit-0.3.7 → kodit-0.3.9}/uv.lock +0 -0
@@ -1,9 +1,9 @@
1
1
  name: Publish Docker image
2
2
 
3
3
  on:
4
+ workflow_dispatch:
4
5
  push:
5
- pull_request_target:
6
- types: [opened, synchronize, reopened]
6
+ workflow_call:
7
7
 
8
8
  env:
9
9
  TEST_TAG: user/app:test
@@ -86,6 +86,10 @@ EOT
86
86
 
87
87
  STOPSIGNAL SIGINT
88
88
 
89
+ # Configure a default data directory so the app can write to it and volumes can be mounted to it
90
+ RUN mkdir -p /data && chown -R app:app /data
91
+ ENV DATA_DIR=/data
92
+
89
93
  USER app
90
94
  WORKDIR /app
91
95
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kodit
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Code indexing for better AI code generation
5
5
  Project-URL: Homepage, https://docs.helixml.tech/kodit/
6
6
  Project-URL: Documentation, https://docs.helixml.tech/kodit/
@@ -2,9 +2,9 @@ version: "3.9"
2
2
 
3
3
  services:
4
4
  kodit:
5
- image: registry.helix.ml/helix/kodit:latest # Replace with a version
5
+ image: test:latest
6
6
  ports:
7
- - "8080:8080" # Expose the MCP server
7
+ - "8080:8080" # You may wish to pick a less common port
8
8
  # Start the Kodit MCP server and bind to all interfaces
9
9
  command: ["serve", "--host", "0.0.0.0", "--port", "8080"]
10
10
  restart: unless-stopped
@@ -39,11 +39,20 @@ services:
39
39
  SYNC_PERIODIC_INTERVAL_SECONDS: 1800 # 30 minutes
40
40
  SYNC_PERIODIC_RETRY_ATTEMPTS: 3
41
41
 
42
+ # Logging configuration
43
+ LOG_LEVEL: INFO # Set to DEBUG for more detailed logging
44
+
45
+ volumes:
46
+ - kodit-data:/data
47
+
42
48
  vectorchord:
43
49
  image: tensorchord/vchord-suite:pg17-20250601
44
50
  environment:
45
51
  - POSTGRES_DB=kodit
46
52
  - POSTGRES_PASSWORD=mysecretpassword
47
53
  ports:
48
- - "5432:5432"
54
+ - "5432"
49
55
  restart: unless-stopped
56
+
57
+ volumes:
58
+ kodit-data:
@@ -95,7 +95,8 @@ claude mcp add kodit -- kodit stdio
95
95
 
96
96
  #### Cursor Streaming HTTP Mode (recommended)
97
97
 
98
- [![Install MCP Server](cursor://anysphere.cursor-deeplink/mcp/install?name=kodit&config=eyJ1cmwiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvbWNwIn0%3D)
98
+ ![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)
99
+ {class="h-8 inline-block" href="cursor://anysphere.cursor-deeplink/mcp/install?name=kodit&config=eyJ1cmwiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvbWNwIn0%3D"}
99
100
 
100
101
  Add the following to `$HOME/.cursor/mcp.json`:
101
102
 
@@ -114,7 +115,8 @@ where Kodit is hosted.
114
115
 
115
116
  #### Cursor STDIO
116
117
 
117
- [![Install MCP Server](cursor://anysphere.cursor-deeplink/mcp/install?name=kodit&config=eyJjb21tYW5kIjoicGlweCBydW4ga29kaXQgc3RkaW8ifQ%3D%3D)
118
+ ![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)
119
+ {class="h-8 inline-block" href="cursor://anysphere.cursor-deeplink/mcp/install?name=kodit&config=eyJjb21tYW5kIjoicGlweCBydW4ga29kaXQgc3RkaW8ifQ%3D%3D"}
118
120
 
119
121
  Add the following to `$HOME/.cursor/mcp.json`:
120
122
 
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.3.7'
21
- __version_tuple__ = version_tuple = (0, 3, 7)
20
+ __version__ = version = '0.3.9'
21
+ __version_tuple__ = version_tuple = (0, 3, 9)
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
23
23
  from kodit.database import Database
24
24
 
25
25
  DEFAULT_BASE_DIR = Path.home() / ".kodit"
26
- DEFAULT_DB_URL = f"sqlite+aiosqlite:///{DEFAULT_BASE_DIR}/kodit.db"
27
26
  DEFAULT_LOG_LEVEL = "INFO"
28
27
  DEFAULT_LOG_FORMAT = "pretty"
29
28
  DEFAULT_DISABLE_TELEMETRY = False
@@ -160,7 +159,9 @@ class AppContext(BaseSettings):
160
159
  )
161
160
 
162
161
  data_dir: Path = Field(default=DEFAULT_BASE_DIR)
163
- db_url: str = Field(default=DEFAULT_DB_URL)
162
+ db_url: str = Field(
163
+ default_factory=lambda data: f"sqlite+aiosqlite:///{data['data_dir']}/kodit.db"
164
+ )
164
165
  log_level: str = Field(default=DEFAULT_LOG_LEVEL)
165
166
  log_format: str = Field(default=DEFAULT_LOG_FORMAT)
166
167
  disable_telemetry: bool = Field(default=DEFAULT_DISABLE_TELEMETRY)
@@ -1,6 +1,7 @@
1
1
  """Working copy provider for git-based sources."""
2
2
 
3
3
  import hashlib
4
+ import shutil
4
5
  from pathlib import Path
5
6
 
6
7
  import git
@@ -47,6 +48,26 @@ class GitWorkingCopyProvider:
47
48
  async def sync(self, uri: str) -> Path:
48
49
  """Refresh a Git working copy."""
49
50
  clone_path = self.get_clone_path(uri)
50
- repo = git.Repo(clone_path)
51
- repo.remotes.origin.pull()
51
+
52
+ # Check if the clone directory exists and is a valid Git repository
53
+ if not clone_path.exists() or not (clone_path / ".git").exists():
54
+ self.log.info(
55
+ "Clone directory does not exist or is not a Git repository, "
56
+ "preparing...",
57
+ uri=uri, clone_path=str(clone_path)
58
+ )
59
+ return await self.prepare(uri)
60
+
61
+ try:
62
+ repo = git.Repo(clone_path)
63
+ repo.remotes.origin.pull()
64
+ except git.InvalidGitRepositoryError:
65
+ self.log.warning(
66
+ "Invalid Git repository found, re-cloning...",
67
+ uri=uri, clone_path=str(clone_path)
68
+ )
69
+ # Remove the invalid directory and re-clone
70
+ shutil.rmtree(clone_path)
71
+ return await self.prepare(uri)
72
+
52
73
  return clone_path
@@ -247,9 +247,9 @@ class Slicer:
247
247
  "typescript": "typescript",
248
248
  "js": "javascript",
249
249
  "ts": "typescript",
250
- "csharp": "c_sharp",
251
- "c#": "c_sharp",
252
- "cs": "c_sharp",
250
+ "csharp": "csharp",
251
+ "c#": "csharp",
252
+ "cs": "csharp",
253
253
  "html": "html",
254
254
  "css": "css",
255
255
  }
@@ -399,7 +399,10 @@ class Slicer:
399
399
  """Get tag name from start_tag node."""
400
400
  for child in start_tag.children:
401
401
  if child.type == "tag_name" and child.text:
402
- return child.text.decode("utf-8")
402
+ try:
403
+ return child.text.decode("utf-8")
404
+ except UnicodeDecodeError:
405
+ return None
403
406
  return None
404
407
 
405
408
  def _get_element_id(self, start_tag: Node) -> str | None:
@@ -424,7 +427,10 @@ class Slicer:
424
427
  """Get attribute name."""
425
428
  for child in attr_node.children:
426
429
  if child.type == "attribute_name" and child.text:
427
- return child.text.decode("utf-8")
430
+ try:
431
+ return child.text.decode("utf-8")
432
+ except UnicodeDecodeError:
433
+ return None
428
434
  return None
429
435
 
430
436
  def _get_attr_value(self, attr_node: Node) -> str | None:
@@ -433,7 +439,10 @@ class Slicer:
433
439
  if child.type == "quoted_attribute_value":
434
440
  for val_child in child.children:
435
441
  if val_child.type == "attribute_value" and val_child.text:
436
- return val_child.text.decode("utf-8")
442
+ try:
443
+ return val_child.text.decode("utf-8")
444
+ except UnicodeDecodeError:
445
+ return None
437
446
  return None
438
447
 
439
448
  def _extract_css_rule_name(self, node: Node) -> str | None:
@@ -2,7 +2,7 @@
2
2
 
3
3
  import hashlib
4
4
  from pathlib import Path
5
- from unittest.mock import patch
5
+ from unittest.mock import MagicMock, patch
6
6
 
7
7
  import git
8
8
  import pytest
@@ -199,3 +199,139 @@ async def test_prepare_handles_already_exists_error(
199
199
  # Verify that the directory was created
200
200
  assert result_path.exists()
201
201
  assert result_path.is_dir()
202
+
203
+
204
+ @pytest.mark.asyncio
205
+ async def test_sync_directory_does_not_exist_should_call_prepare(
206
+ working_copy: GitWorkingCopyProvider,
207
+ ) -> None:
208
+ """Test that sync() calls prepare() when directory doesn't exist."""
209
+ url = "https://github.com/username/repo.git"
210
+
211
+ # Mock prepare method to avoid actual cloning
212
+ with patch.object(working_copy, "prepare") as mock_prepare:
213
+ mock_prepare.return_value = Path("/fake/path")
214
+
215
+ # Call sync method
216
+ result_path = await working_copy.sync(url)
217
+
218
+ # Verify prepare was called
219
+ mock_prepare.assert_called_once_with(url)
220
+ assert result_path == Path("/fake/path")
221
+
222
+
223
+ @pytest.mark.asyncio
224
+ async def test_sync_directory_exists_but_no_git_should_call_prepare(
225
+ working_copy: GitWorkingCopyProvider,
226
+ ) -> None:
227
+ """Test that sync() calls prepare() when directory exists but is not a Git repo."""
228
+ url = "https://github.com/username/repo.git"
229
+
230
+ # Create a directory that exists but is not a git repo
231
+ clone_path = working_copy.get_clone_path(url)
232
+ clone_path.mkdir(parents=True, exist_ok=True)
233
+
234
+ # Mock prepare method to avoid actual cloning
235
+ with patch.object(working_copy, "prepare") as mock_prepare:
236
+ mock_prepare.return_value = clone_path
237
+
238
+ # Call sync method
239
+ result_path = await working_copy.sync(url)
240
+
241
+ # Verify prepare was called
242
+ mock_prepare.assert_called_once_with(url)
243
+ assert result_path == clone_path
244
+
245
+
246
+ @pytest.mark.asyncio
247
+ async def test_sync_valid_git_repository_should_pull(
248
+ working_copy: GitWorkingCopyProvider,
249
+ ) -> None:
250
+ """Test that sync() pulls from origin when repository is valid."""
251
+ url = "https://github.com/username/repo.git"
252
+
253
+ # Create a directory that exists with .git subdirectory
254
+ clone_path = working_copy.get_clone_path(url)
255
+ clone_path.mkdir(parents=True, exist_ok=True)
256
+ (clone_path / ".git").mkdir(parents=True, exist_ok=True)
257
+
258
+ # Mock git.Repo and origin.pull
259
+ mock_repo = MagicMock()
260
+ mock_origin = MagicMock()
261
+ mock_repo.remotes.origin = mock_origin
262
+
263
+ with patch("git.Repo") as mock_git_repo:
264
+ mock_git_repo.return_value = mock_repo
265
+
266
+ # Call sync method
267
+ result_path = await working_copy.sync(url)
268
+
269
+ # Verify git.Repo was called with the correct path
270
+ mock_git_repo.assert_called_once_with(clone_path)
271
+
272
+ # Verify origin.pull was called
273
+ mock_origin.pull.assert_called_once()
274
+
275
+ # Verify the correct path was returned
276
+ assert result_path == clone_path
277
+
278
+
279
+ @pytest.mark.asyncio
280
+ async def test_sync_invalid_git_repository_should_reclone(
281
+ working_copy: GitWorkingCopyProvider,
282
+ ) -> None:
283
+ """Test that sync() re-clones when repository is invalid."""
284
+ url = "https://github.com/username/repo.git"
285
+
286
+ # Create a directory that exists with .git subdirectory
287
+ clone_path = working_copy.get_clone_path(url)
288
+ clone_path.mkdir(parents=True, exist_ok=True)
289
+ (clone_path / ".git").mkdir(parents=True, exist_ok=True)
290
+
291
+ # Mock git.Repo to raise InvalidGitRepositoryError
292
+ with patch("git.Repo") as mock_git_repo:
293
+ mock_git_repo.side_effect = git.InvalidGitRepositoryError("Invalid repo")
294
+
295
+ # Mock shutil.rmtree to track directory removal
296
+ with (
297
+ patch("shutil.rmtree") as mock_rmtree,
298
+ patch.object(working_copy, "prepare") as mock_prepare,
299
+ ):
300
+ mock_prepare.return_value = clone_path
301
+
302
+ # Call sync method
303
+ result_path = await working_copy.sync(url)
304
+
305
+ # Verify git.Repo was called with the correct path
306
+ mock_git_repo.assert_called_once_with(clone_path)
307
+
308
+ # Verify the invalid directory was removed
309
+ mock_rmtree.assert_called_once_with(clone_path)
310
+
311
+ # Verify prepare was called to re-clone
312
+ mock_prepare.assert_called_once_with(url)
313
+
314
+ # Verify the correct path was returned
315
+ assert result_path == clone_path
316
+
317
+
318
+ @pytest.mark.asyncio
319
+ async def test_sync_get_clone_path_should_match_prepare(
320
+ working_copy: GitWorkingCopyProvider,
321
+ ) -> None:
322
+ """Test that sync() and prepare() use the same clone path."""
323
+ url = "https://github.com/username/repo.git"
324
+
325
+ # Get clone paths from both methods
326
+ sync_path = working_copy.get_clone_path(url)
327
+
328
+ # Mock prepare to return the expected path
329
+ with patch.object(working_copy, "prepare") as mock_prepare:
330
+ mock_prepare.return_value = sync_path
331
+
332
+ # Call sync method (will call prepare since directory doesn't exist)
333
+ result_path = await working_copy.sync(url)
334
+
335
+ # Verify paths match
336
+ assert result_path == sync_path
337
+ mock_prepare.assert_called_once_with(url)
@@ -2,10 +2,12 @@
2
2
 
3
3
  import tempfile
4
4
  from pathlib import Path
5
+ from typing import get_args
5
6
  from unittest.mock import Mock
6
7
 
7
8
  import pytest
8
9
  from pydantic import AnyUrl
10
+ from tree_sitter_language_pack import SupportedLanguage
9
11
 
10
12
  from kodit.domain.entities import File, Snippet
11
13
  from kodit.domain.value_objects import FileProcessingStatus
@@ -154,12 +156,43 @@ class TestSlicer:
154
156
  assert slicer._get_tree_sitter_language_name("c++") == "cpp" # noqa: SLF001
155
157
  assert slicer._get_tree_sitter_language_name("typescript") == "typescript" # noqa: SLF001
156
158
  assert slicer._get_tree_sitter_language_name("js") == "javascript" # noqa: SLF001
157
- assert slicer._get_tree_sitter_language_name("csharp") == "c_sharp" # noqa: SLF001
158
- assert slicer._get_tree_sitter_language_name("c#") == "c_sharp" # noqa: SLF001
159
- assert slicer._get_tree_sitter_language_name("cs") == "c_sharp" # noqa: SLF001
159
+ assert slicer._get_tree_sitter_language_name("csharp") == "csharp" # noqa: SLF001
160
+ assert slicer._get_tree_sitter_language_name("c#") == "csharp" # noqa: SLF001
161
+ assert slicer._get_tree_sitter_language_name("cs") == "csharp" # noqa: SLF001
160
162
  assert slicer._get_tree_sitter_language_name("html") == "html" # noqa: SLF001
161
163
  assert slicer._get_tree_sitter_language_name("css") == "css" # noqa: SLF001
162
164
 
165
+ def test_tree_sitter_language_names_are_valid(self) -> None:
166
+ """Test that all tree-sitter language mappings resolve to valid libraries."""
167
+ slicer = Slicer()
168
+
169
+ # Test all mappings in _get_tree_sitter_language_name
170
+ test_languages = [
171
+ "python",
172
+ "c++",
173
+ "c",
174
+ "cpp",
175
+ "java",
176
+ "rust",
177
+ "go",
178
+ "javascript",
179
+ "typescript",
180
+ "js",
181
+ "ts",
182
+ "csharp",
183
+ "c#",
184
+ "cs",
185
+ "html",
186
+ "css",
187
+ ]
188
+
189
+ for lang in test_languages:
190
+ ts_name = slicer._get_tree_sitter_language_name(lang) # noqa: SLF001
191
+
192
+ assert ts_name in get_args(SupportedLanguage), (
193
+ f"Language '{ts_name}' not in SupportedLanguage"
194
+ )
195
+
163
196
  def test_language_config_access(self) -> None:
164
197
  """Test that language config is correctly accessed."""
165
198
  # Just test that configs exist and are accessible
@@ -828,3 +861,27 @@ class TestErrorHandling:
828
861
 
829
862
  error_msg = str(exc_info.value)
830
863
  assert "File not found" in error_msg
864
+
865
+ def test_binary_file_handling_in_html_parser(self) -> None:
866
+ """Test that binary files don't crash HTML parser with UnicodeDecodeError."""
867
+ with tempfile.TemporaryDirectory() as tmp_dir:
868
+ # Create a binary file that looks like an image
869
+ binary_file = Path(tmp_dir, "test.html")
870
+ # Write binary data that would cause UnicodeDecodeError if decoded as UTF-8
871
+ binary_data = b"\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
872
+ binary_file.write_bytes(binary_data)
873
+
874
+ file_obj = create_file_from_path(binary_file)
875
+
876
+ slicer = Slicer()
877
+
878
+ # This should not raise UnicodeDecodeError
879
+ try:
880
+ snippets = slicer.extract_snippets([file_obj], "html")
881
+ # Should return empty list or handle gracefully
882
+ assert isinstance(snippets, list)
883
+ except UnicodeDecodeError:
884
+ pytest.fail("UnicodeDecodeError should not be raised for binary files")
885
+ except RuntimeError:
886
+ # Tree-sitter setup issues are acceptable
887
+ pytest.skip("Tree-sitter setup not available")
@@ -9,6 +9,9 @@ if [ -n "$CI" ]; then
9
9
  prefix=""
10
10
  fi
11
11
 
12
+ # Disable telemetry
13
+ export DISABLE_TELEMETRY=true
14
+
12
15
  # Check that the kodit data_dir does not exist
13
16
  if [ -d "$HOME/.kodit" ]; then
14
17
  echo "Kodit data_dir is not empty, please rm -rf $HOME/.kodit"
@@ -1,222 +0,0 @@
1
- # Migration to Index Aggregate Architecture
2
-
3
- This document outlines the strategy for migrating from the current multiple-service architecture to the new Index aggregate root design.
4
-
5
- ## Current Architecture Issues
6
-
7
- ### Code Analysis of `CodeIndexingApplicationService`
8
-
9
- The current application service has several problems:
10
-
11
- 1. **Service Proliferation**: 7+ domain services injected
12
- 2. **Manual Orchestration**: Application layer contains complex business logic
13
- 3. **Leaky Abstractions**: SQLAlchemy session management at application level
14
- 4. **Scattered State**: Snippet state tracked across multiple services
15
-
16
- ### Current Workflow Complexity
17
-
18
- ```python
19
- # Current approach - complex orchestration
20
- async def run_index(self, index_id: int) -> None:
21
- # 1. Get index from indexing service
22
- index = await self.indexing_domain_service.get_index(index_id)
23
-
24
- # 2. Delete old snippets via snippet service
25
- await self.snippet_domain_service.delete_snippets_for_index(index.id)
26
-
27
- # 3. Extract snippets via snippet service
28
- snippets = await self.snippet_domain_service.extract_and_create_snippets(...)
29
-
30
- # 4. Manual transaction management
31
- await self.session.commit()
32
-
33
- # 5. Create BM25 index via separate service
34
- await self._create_bm25_index(snippets, progress_callback)
35
-
36
- # 6. Create embeddings via separate service
37
- await self._create_code_embeddings(snippets, progress_callback)
38
-
39
- # 7. Enrich snippets via separate service
40
- await self._enrich_snippets(snippets, progress_callback)
41
-
42
- # 8. More embeddings via separate service
43
- await self._create_text_embeddings(snippets, progress_callback)
44
-
45
- # 9. Update timestamp via indexing service
46
- await self.indexing_domain_service.update_index_timestamp(index.id)
47
-
48
- # 10. Final commit
49
- await self.session.commit()
50
- ```
51
-
52
- ## New Architecture Benefits
53
-
54
- ### Simplified Application Service
55
-
56
- ```python
57
- # New approach - aggregate root handles complexity
58
- async def run_complete_indexing_workflow(
59
- self, uri: AnyUrl, local_path: Path
60
- ) -> domain_entities.Index:
61
- # 1. Create index (aggregate root)
62
- index = await self._index_domain_service.create_index(uri)
63
-
64
- # 2. Populate working copy (aggregate method)
65
- index = await self._index_domain_service.clone_and_populate_working_copy(
66
- index, local_path, SourceType.GIT
67
- )
68
-
69
- # 3. Extract snippets (aggregate method)
70
- index = await self._index_domain_service.extract_snippets(index)
71
-
72
- # 4. Simple transaction management
73
- await self._session.commit()
74
-
75
- return index
76
- ```
77
-
78
- ## Migration Strategy
79
-
80
- ### Phase 1: Parallel Implementation ✅
81
-
82
- - [x] Create new domain entities (`domain/models/entities.py`)
83
- - [x] Create repository protocol (`domain/models/protocols.py`)
84
- - [x] Create mapping layer (`infrastructure/mappers/index_mapper.py`)
85
- - [x] Create repository implementation (`infrastructure/sqlalchemy/index_repository.py`)
86
- - [x] Create domain service (`domain/services/index_service.py`)
87
- - [x] Create simplified application service (`application/services/simplified_indexing_service.py`)
88
-
89
- ### Phase 2: Feature Parity (Next Steps)
90
-
91
- #### 2.1 Complete Index Domain Service
92
- - [ ] Implement actual cloning logic in `clone_and_populate_working_copy`
93
- - [ ] Complete snippet enrichment in `enrich_snippets_with_summaries`
94
- - [ ] Add snippet search capabilities to Index aggregate
95
- - [ ] Add BM25/embedding integration
96
-
97
- #### 2.2 Application Service Integration
98
- - [ ] Update application factories to create new services
99
- - [ ] Add legacy compatibility methods
100
- - [ ] Implement search functionality migration
101
-
102
- #### 2.3 CLI Integration
103
- - [ ] Update CLI commands to use new application service
104
- - [ ] Maintain backward compatibility for existing commands
105
-
106
- ### Phase 3: Gradual Migration
107
-
108
- #### 3.1 New Endpoints First
109
- - [ ] Create new CLI commands using Index aggregate
110
- - [ ] Add new MCP tools using simplified service
111
- - [ ] Implement new features with aggregate root
112
-
113
- #### 3.2 Legacy Adaptation
114
- - [ ] Wrap old API calls to use new domain service
115
- - [ ] Provide compatibility layer for existing integrations
116
- - [ ] Migrate tests gradually
117
-
118
- #### 3.3 Search Migration
119
- - [ ] Move search logic into Index aggregate
120
- - [ ] Create search value objects in domain
121
- - [ ] Simplify search application service
122
-
123
- ### Phase 4: Complete Migration
124
-
125
- #### 4.1 Remove Old Services
126
- - [ ] Remove `IndexingDomainService`
127
- - [ ] Remove `SnippetDomainService`
128
- - [ ] Remove `SourceService`
129
- - [ ] Clean up old value objects
130
-
131
- #### 4.2 Final Cleanup
132
- - [ ] Remove legacy compatibility methods
133
- - [ ] Update all tests to use new architecture
134
- - [ ] Remove old application service
135
-
136
- ## Code Examples
137
-
138
- ### Before: Current Complexity
139
-
140
- ```python
141
- class CodeIndexingApplicationService:
142
- def __init__(self,
143
- indexing_domain_service: IndexingDomainService,
144
- snippet_domain_service: SnippetDomainService,
145
- source_service: SourceService,
146
- bm25_service: BM25DomainService,
147
- code_search_service: EmbeddingDomainService,
148
- text_search_service: EmbeddingDomainService,
149
- enrichment_service: EnrichmentDomainService,
150
- session: AsyncSession, # Leaky abstraction!
151
- ):
152
- # 7+ services to coordinate
153
- ```
154
-
155
- ### After: Aggregate Root Simplicity
156
-
157
- ```python
158
- class SimplifiedIndexingApplicationService:
159
- def __init__(self,
160
- index_domain_service: IndexDomainService,
161
- session: AsyncSession,
162
- ):
163
- # Single domain service + session
164
- # All business logic in domain
165
- ```
166
-
167
- ## Benefits of Migration
168
-
169
- ### 1. **Reduced Complexity**
170
- - Single domain service instead of 7+
171
- - Business logic moves to domain layer
172
- - Application layer focuses on coordination
173
-
174
- ### 2. **Better Domain Modeling**
175
- - Index as true aggregate root
176
- - Rich domain objects with behavior
177
- - Proper encapsulation of business rules
178
-
179
- ### 3. **Improved Testability**
180
- - Domain service can be tested in isolation
181
- - No SQLAlchemy dependencies in domain tests
182
- - Cleaner mocking for application tests
183
-
184
- ### 4. **Enhanced Maintainability**
185
- - Clear boundaries between layers
186
- - Easier to add new features
187
- - Reduced coupling between services
188
-
189
- ### 5. **Better Performance**
190
- - Fewer repository round trips
191
- - Optimized aggregate loading
192
- - Reduced object mapping overhead
193
-
194
- ## Risks and Mitigation
195
-
196
- ### Risk: Breaking Changes
197
- **Mitigation**: Implement compatibility layer during transition
198
-
199
- ### Risk: Feature Regression
200
- **Mitigation**: Comprehensive test coverage for both old and new
201
-
202
- ### Risk: Performance Impact
203
- **Mitigation**: Benchmark and optimize aggregate loading
204
-
205
- ### Risk: Complex Migration
206
- **Mitigation**: Gradual, phase-by-phase approach
207
-
208
- ## Success Metrics
209
-
210
- - [ ] Reduced lines of code in application service (target: 50% reduction)
211
- - [ ] Improved test coverage for domain logic
212
- - [ ] Faster indexing workflow execution
213
- - [ ] Fewer bugs related to state management
214
- - [ ] Easier onboarding for new developers
215
-
216
- ## Next Immediate Steps
217
-
218
- 1. **Complete Domain Service**: Finish implementing cloning and enrichment
219
- 2. **Factory Integration**: Update application factories
220
- 3. **Simple CLI Command**: Create one new command using aggregate
221
- 4. **Performance Test**: Benchmark against current implementation
222
- 5. **Migration Plan**: Detail specific steps for first legacy endpoint
File without changes
File without changes