ai-codeindex 0.26.0__tar.gz → 0.26.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 (418) hide show
  1. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.gitignore +1 -0
  2. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/CHANGELOG.md +11 -0
  3. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/PKG-INFO +1 -1
  4. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/pyproject.toml +1 -1
  5. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/README_AI.md +3 -3
  6. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_hooks.py +54 -2
  7. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_scan.py +15 -0
  8. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/enricher.py +40 -0
  9. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/init_wizard.py +16 -3
  10. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/README_AI.md +2 -2
  11. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_hooks.py +116 -0
  12. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_enricher.py +73 -0
  13. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_detection.py +65 -0
  14. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.editorconfig +0 -0
  15. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.github/ISSUE_TEMPLATE/bug.md +0 -0
  16. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  17. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.github/ISSUE_TEMPLATE/enhancement.md +0 -0
  18. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.github/ISSUE_TEMPLATE/epic.md +0 -0
  19. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.github/ISSUE_TEMPLATE/feature.md +0 -0
  20. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.github/workflows/ci.yml +0 -0
  21. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.github/workflows/publish.yml +0 -0
  22. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.serena/.gitignore +0 -0
  23. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.serena/memories/design_philosophy.md +0 -0
  24. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.serena/memories/development_workflow.md +0 -0
  25. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.serena/memories/project_overview.md +0 -0
  26. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.serena/memories/release_automation.md +0 -0
  27. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/.serena/memories/suggested_commands.md +0 -0
  28. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/CLAUDE.md +0 -0
  29. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/CODE_OF_CONDUCT.md +0 -0
  30. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/CONTRIBUTING.md +0 -0
  31. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/FOR_LOOMGRAPH.md +0 -0
  32. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/LICENSE +0 -0
  33. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/Makefile +0 -0
  34. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/PROJECT_SYMBOLS.md +0 -0
  35. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/README.md +0 -0
  36. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/README_AI.md +0 -0
  37. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/README_zh.md +0 -0
  38. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/SECURITY.md +0 -0
  39. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/.gitignore +0 -0
  40. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/Makefile +0 -0
  41. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/README.md +0 -0
  42. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/grade.py +0 -0
  43. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/questions.yaml.example +0 -0
  44. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/report.py +0 -0
  45. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/run_bench.py +0 -0
  46. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/bench/targets.yaml.example +0 -0
  47. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/README.md +0 -0
  48. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/adr/001-use-tree-sitter-for-parsing.md +0 -0
  49. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/adr/002-external-ai-cli-integration.md +0 -0
  50. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/adr/003-add-swift-objc-support.md +0 -0
  51. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/adr/004-automatic-claude-md-update.md +0 -0
  52. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/adr/005-navigation-disclaimer-and-readme-size-cap.md +0 -0
  53. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/adr/006-distribution-architecture-split.md +0 -0
  54. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/design/document-aggregation.md +0 -0
  55. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/design/initial-design.md +0 -0
  56. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/design/kiss-universal-description.md +0 -0
  57. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/design/parallel-strategy.md +0 -0
  58. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/architecture/design-philosophy.md +0 -0
  59. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/benchmark/2026-05-readme-impact.md +0 -0
  60. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/QUICK_START_RELEASE.md +0 -0
  61. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/claude-code-adoption-guide.md +0 -0
  62. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/claude-code-adoption-guide.zh.md +0 -0
  63. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/gitflow-workflow.md +0 -0
  64. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/github-issue-quick-reference.md +0 -0
  65. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/package-naming.md +0 -0
  66. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/pre-release-checklist.md +0 -0
  67. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/setup.md +0 -0
  68. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/team-workflow-guide.md +0 -0
  69. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/development/test-architecture.md +0 -0
  70. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/evaluation/before-after/README.md +0 -0
  71. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/evaluation/case-studies/php-payment-project.md +0 -0
  72. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/evaluation/loomgraph-efficiency-comparison.md +0 -0
  73. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/advanced-usage.md +0 -0
  74. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/claude-code-integration.md +0 -0
  75. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/configuration.md +0 -0
  76. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/contributing.md +0 -0
  77. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/getting-started.md +0 -0
  78. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/git-hooks-integration.md +0 -0
  79. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/json-output-integration.md +0 -0
  80. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/guides/loomgraph-integration.md +0 -0
  81. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/planning/README.md +0 -0
  82. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/planning/ROADMAP.md +0 -0
  83. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/planning/executive-summary.md +0 -0
  84. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/README.md +0 -0
  85. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.10.0.md +0 -0
  86. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.12.0.md +0 -0
  87. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.12.1.md +0 -0
  88. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.14.0.md +0 -0
  89. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.15.0.md +0 -0
  90. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.17.0.md +0 -0
  91. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.17.2.md +0 -0
  92. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.17.3.md +0 -0
  93. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.18.0.md +0 -0
  94. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.19.0.md +0 -0
  95. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.2.0.md +0 -0
  96. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.20.0.md +0 -0
  97. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.23.1.md +0 -0
  98. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.23.2.md +0 -0
  99. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.24.0.md +0 -0
  100. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.25.0.md +0 -0
  101. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.3.0.md +0 -0
  102. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.3.1.md +0 -0
  103. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.3.2.md +0 -0
  104. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.4.0.md +0 -0
  105. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.5.0-beta1.md +0 -0
  106. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.7.0.md +0 -0
  107. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.8.0.md +0 -0
  108. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/docs/releases/RELEASE_NOTES_v0.9.0.md +0 -0
  109. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/CLAUDE.md.php-project +0 -0
  110. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/CLAUDE.md.template +0 -0
  111. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/README.md +0 -0
  112. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/README_AI.md +0 -0
  113. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/ai-integration-guide.md +0 -0
  114. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/frameworks/README_AI.md +0 -0
  115. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/frameworks/template/README.md +0 -0
  116. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/frameworks/template/README_AI.md +0 -0
  117. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/frameworks/template/test_template_extractor.py +0 -0
  118. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/frameworks/template/yourframework_extractor.py +0 -0
  119. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/loomgraph_output.json +0 -0
  120. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/loomgraph_php_output.json +0 -0
  121. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/loomgraph_sample.php +0 -0
  122. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/loomgraph_sample.py +0 -0
  123. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/parse_integration_example.sh +0 -0
  124. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/examples/print_env.py +0 -0
  125. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/hooks/README.md +0 -0
  126. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/hooks/templates/post-commit-update-logic.sh +0 -0
  127. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/hooks/templates/post-commit-v4 +0 -0
  128. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/reports/swift-objc-support-analysis.md +0 -0
  129. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/reports/swift-poc-summary.md +0 -0
  130. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/reports/tech-debt-codeindex-v2.md +0 -0
  131. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/reports/tech-debt-codeindex.md +0 -0
  132. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/reports/tech-debt-loomgraph-v2.md +0 -0
  133. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/reports/tech-debt-loomgraph.md +0 -0
  134. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/reports/workflow-comparison.md +0 -0
  135. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/README.md +0 -0
  136. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/README_AI.md +0 -0
  137. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/bump_version.sh +0 -0
  138. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/check_docs_release.py +0 -0
  139. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/check_version_consistency.py +0 -0
  140. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/close-epic9-issues.sh +0 -0
  141. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/diagnose_ai_failures.py +0 -0
  142. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/hooks/hook-common.sh +0 -0
  143. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/hooks/pre-push +0 -0
  144. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/legacy/PROJECT_INDEX.json +0 -0
  145. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/legacy/README.md +0 -0
  146. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/legacy/hierarchical_strategy.py +0 -0
  147. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/pre_release_check.sh +0 -0
  148. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/release.sh +0 -0
  149. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/test_hook_e2e.sh +0 -0
  150. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/scripts/validate_php_project.py +0 -0
  151. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/skills/README.md +0 -0
  152. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/skills/create.sh +0 -0
  153. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/skills/install.sh +0 -0
  154. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/skills/src/mo-arch/SKILL.md +0 -0
  155. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/skills/src/mo-index/SKILL.md +0 -0
  156. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/README_AI.md +0 -0
  157. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/__init__.py +0 -0
  158. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/adaptive_config.py +0 -0
  159. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/adaptive_selector.py +0 -0
  160. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/ai_helper.py +0 -0
  161. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/claude_md.py +0 -0
  162. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli.py +0 -0
  163. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_claude_md.py +0 -0
  164. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_common.py +0 -0
  165. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_config.py +0 -0
  166. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_config_commands.py +0 -0
  167. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_docs.py +0 -0
  168. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_parse.py +0 -0
  169. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_symbols.py +0 -0
  170. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/cli_tech_debt.py +0 -0
  171. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/config.py +0 -0
  172. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/config_help.py +0 -0
  173. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/directory_tree.py +0 -0
  174. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/docstring_processor.py +0 -0
  175. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/doctor.py +0 -0
  176. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/errors.py +0 -0
  177. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/extractors/README_AI.md +0 -0
  178. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/extractors/__init__.py +0 -0
  179. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/extractors/spring.py +0 -0
  180. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/extractors/thinkphp.py +0 -0
  181. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/file_classifier.py +0 -0
  182. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/framework_detect.py +0 -0
  183. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/hierarchical.py +0 -0
  184. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/hooks.py +0 -0
  185. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/incremental.py +0 -0
  186. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/invoker.py +0 -0
  187. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/objc_association.py +0 -0
  188. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parallel.py +0 -0
  189. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parser.py +0 -0
  190. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/README_AI.md +0 -0
  191. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/__init__.py +0 -0
  192. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/base.py +0 -0
  193. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/java/README_AI.md +0 -0
  194. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/java/__init__.py +0 -0
  195. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/java/calls.py +0 -0
  196. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/java/imports.py +0 -0
  197. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/java/inheritance.py +0 -0
  198. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/java/symbols.py +0 -0
  199. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/java_parser.py +0 -0
  200. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/objc/README_AI.md +0 -0
  201. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/objc/__init__.py +0 -0
  202. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/objc/calls.py +0 -0
  203. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/objc/imports.py +0 -0
  204. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/objc/inheritance.py +0 -0
  205. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/objc/symbols.py +0 -0
  206. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/php/README_AI.md +0 -0
  207. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/php/__init__.py +0 -0
  208. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/php/calls.py +0 -0
  209. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/php/imports.py +0 -0
  210. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/php/inheritance.py +0 -0
  211. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/php/symbols.py +0 -0
  212. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/python/README_AI.md +0 -0
  213. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/python/__init__.py +0 -0
  214. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/python/calls.py +0 -0
  215. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/python/imports.py +0 -0
  216. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/python/inheritance.py +0 -0
  217. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/python/symbols.py +0 -0
  218. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/swift/README_AI.md +0 -0
  219. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/swift/__init__.py +0 -0
  220. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/swift/calls.py +0 -0
  221. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/swift/imports.py +0 -0
  222. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/swift/inheritance.py +0 -0
  223. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/swift/symbols.py +0 -0
  224. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/typescript/README_AI.md +0 -0
  225. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/typescript/__init__.py +0 -0
  226. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/typescript/calls.py +0 -0
  227. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/typescript/imports.py +0 -0
  228. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/typescript/inheritance.py +0 -0
  229. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/typescript/symbols.py +0 -0
  230. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/parsers/utils.py +0 -0
  231. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/route_extractor.py +0 -0
  232. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/route_registry.py +0 -0
  233. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/scanner.py +0 -0
  234. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/semantic_extractor.py +0 -0
  235. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/skill_helpers.py +0 -0
  236. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/smart_writer.py +0 -0
  237. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/symbol_index.py +0 -0
  238. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/symbol_scorer.py +0 -0
  239. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/tech_debt.py +0 -0
  240. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/tech_debt_formatters.py +0 -0
  241. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/templates/README_AI.md +0 -0
  242. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/templates/__init__.py +0 -0
  243. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/templates/claude_md_core.md +0 -0
  244. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/test_smells.py +0 -0
  245. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writer.py +0 -0
  246. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writers/README_AI.md +0 -0
  247. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writers/__init__.py +0 -0
  248. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writers/core.py +0 -0
  249. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writers/detailed_generator.py +0 -0
  250. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writers/navigation_generator.py +0 -0
  251. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writers/overview_generator.py +0 -0
  252. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/src/codeindex/writers/utils.py +0 -0
  253. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/generator.py +0 -0
  254. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/scripts/analyze_legacy_tests.py +0 -0
  255. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/scripts/compare_coverage.py +0 -0
  256. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/scripts/compare_test_results.py +0 -0
  257. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/specs/java.yaml +0 -0
  258. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/specs/php.yaml +0 -0
  259. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/specs/python.yaml +0 -0
  260. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/test_generator/templates/inheritance_test.py.j2 +0 -0
  261. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/__init__.py +0 -0
  262. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/conftest.py +0 -0
  263. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/extractors/README_AI.md +0 -0
  264. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/extractors/__init__.py +0 -0
  265. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/extractors/test_spring.py +0 -0
  266. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/extractors/test_thinkphp.py +0 -0
  267. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/extractors/test_thinkphp_description.py +0 -0
  268. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/features/README.md +0 -0
  269. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/features/cli_scan_defaults.feature +0 -0
  270. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/features/help_system.feature +0 -0
  271. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/features/init_wizard.feature +0 -0
  272. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/features/symbol_overload_detection.feature +0 -0
  273. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/features/tech_debt_detection.feature +0 -0
  274. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/features/tech_debt_reporting.feature +0 -0
  275. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/README_AI.md +0 -0
  276. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/Controller.php +0 -0
  277. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/README_AI.md +0 -0
  278. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/Service.java +0 -0
  279. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/Simple.java +0 -0
  280. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/broken.py +0 -0
  281. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/complete.py +0 -0
  282. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/service.ts +0 -0
  283. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/simple.php +0 -0
  284. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/simple.py +0 -0
  285. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/cli_parse/unsupported.txt +0 -0
  286. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/enum.java +0 -0
  287. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/generics.java +0 -0
  288. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/imports.java +0 -0
  289. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/interface.java +0 -0
  290. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/record.java +0 -0
  291. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/sealed_class.java +0 -0
  292. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/simple_class.java +0 -0
  293. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/spring/Application.java +0 -0
  294. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/spring/User.java +0 -0
  295. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/spring/UserController.java +0 -0
  296. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/spring/UserRepository.java +0 -0
  297. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/spring/UserService.java +0 -0
  298. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/java/spring_controller.java +0 -0
  299. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/typescript/app.js +0 -0
  300. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/typescript/component.tsx +0 -0
  301. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/fixtures/typescript/service.ts +0 -0
  302. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/legacy/README.md +0 -0
  303. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/legacy/README_AI.md +0 -0
  304. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/legacy/test_hierarchical_test/level1/file1.py +0 -0
  305. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/legacy/test_hierarchical_test/level1/level2a/file2.py +0 -0
  306. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/legacy/test_hierarchical_test/level1/level2a/level3/file4.py +0 -0
  307. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/legacy/test_hierarchical_test/level1/level2b/file3.py +0 -0
  308. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/legacy/test_hierarchy_simple.py +0 -0
  309. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_adaptive_config.py +0 -0
  310. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_adaptive_selector.py +0 -0
  311. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_ai_helper.py +0 -0
  312. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_backward_compatibility.py +0 -0
  313. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_call_integration.py +0 -0
  314. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_claude_md.py +0 -0
  315. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_claude_md_injection.py +0 -0
  316. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_config_gitignore.py +0 -0
  317. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_debt_scan.py +0 -0
  318. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_docstring_options.py +0 -0
  319. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_json.py +0 -0
  320. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_parse.py +0 -0
  321. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_scan_defaults_bdd.py +0 -0
  322. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_cli_tech_debt.py +0 -0
  323. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_config_adaptive.py +0 -0
  324. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_dataclass_structure.py +0 -0
  325. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_directory_tree.py +0 -0
  326. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_docstring_config.py +0 -0
  327. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_docstring_processor.py +0 -0
  328. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_doctor.py +0 -0
  329. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_enricher_integration.py +0 -0
  330. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_error_handling.py +0 -0
  331. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_file_classifier.py +0 -0
  332. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_help_system_bdd.py +0 -0
  333. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_hook_post_commit.py +0 -0
  334. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_hooks.py +0 -0
  335. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_hooks_config.py +0 -0
  336. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_hooks_integration.py +0 -0
  337. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_init_minimal_scope.py +0 -0
  338. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_init_wizard_bdd.py +0 -0
  339. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_integration_swift_objc.py +0 -0
  340. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_annotations.py +0 -0
  341. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_calls.py +0 -0
  342. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_edge_cases.py +0 -0
  343. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_error_recovery.py +0 -0
  344. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_generic_bounds.py +0 -0
  345. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_inheritance.py +0 -0
  346. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_lambda.py +0 -0
  347. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_lombok.py +0 -0
  348. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_module.py +0 -0
  349. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_parser.py +0 -0
  350. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_spring.py +0 -0
  351. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_java_throws.py +0 -0
  352. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_json_output.py +0 -0
  353. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_lazy_loading.py +0 -0
  354. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_list_dirs_diagnostic.py +0 -0
  355. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_loomgraph_integration.py +0 -0
  356. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_objc_association_utils.py +0 -0
  357. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parallel_scan.py +0 -0
  358. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser.py +0 -0
  359. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_objc_association.py +0 -0
  360. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_objc_basic.py +0 -0
  361. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_objc_bridging.py +0 -0
  362. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_objc_categories.py +0 -0
  363. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_docstrings.py +0 -0
  364. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_extensions.py +0 -0
  365. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_generics.py +0 -0
  366. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_inheritance.py +0 -0
  367. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_integration.py +0 -0
  368. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_poc.py +0 -0
  369. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_properties.py +0 -0
  370. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_property_wrappers.py +0 -0
  371. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_protocols.py +0 -0
  372. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_parser_swift_signatures.py +0 -0
  373. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_php_calls.py +0 -0
  374. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_php_comment_extraction.py +0 -0
  375. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_php_docstring_extraction.py +0 -0
  376. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_php_import_alias.py +0 -0
  377. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_php_inheritance.py +0 -0
  378. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_php_loomgraph_integration.py +0 -0
  379. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_project_index_semantic.py +0 -0
  380. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_python_calls.py +0 -0
  381. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_python_docstring_description.py +0 -0
  382. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_python_import_alias.py +0 -0
  383. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_python_inheritance.py +0 -0
  384. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_route_extractor.py +0 -0
  385. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_route_info.py +0 -0
  386. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_route_registry.py +0 -0
  387. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_route_table_description.py +0 -0
  388. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_route_table_display.py +0 -0
  389. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_scanall_auto_ai.py +0 -0
  390. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_scanner_passthrough.py +0 -0
  391. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_semantic_extractor.py +0 -0
  392. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_skill_helpers.py +0 -0
  393. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_smart_writer.py +0 -0
  394. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_smart_writer_adaptive.py +0 -0
  395. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_smart_writer_docstring.py +0 -0
  396. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_smart_writer_enriched.py +0 -0
  397. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_smart_writer_integration.py +0 -0
  398. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_smart_writer_semantic.py +0 -0
  399. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_story_4_4_integration.py +0 -0
  400. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_symbol_overload.py +0 -0
  401. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_symbol_scorer.py +0 -0
  402. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_tech_debt_bdd.py +0 -0
  403. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_tech_debt_detector.py +0 -0
  404. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_tech_debt_formatters.py +0 -0
  405. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_tech_debt_ios.py +0 -0
  406. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_tech_debt_java.py +0 -0
  407. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_tech_debt_reporter.py +0 -0
  408. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_test_smells.py +0 -0
  409. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_thinkphp_route_extractor.py +0 -0
  410. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_typescript_integration.py +0 -0
  411. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_typescript_parser.py +0 -0
  412. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_windows_path_optimization.py +0 -0
  413. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/test_writer_preserves_enrichment.py +0 -0
  414. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/writers/README_AI.md +0 -0
  415. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/writers/__init__.py +0 -0
  416. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/writers/test_generators.py +0 -0
  417. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/tests/writers/test_utils.py +0 -0
  418. {ai_codeindex-0.26.0 → ai_codeindex-0.26.1}/uv.lock +0 -0
@@ -36,3 +36,4 @@ htmlcov/
36
36
 
37
37
  # Validation reports (generated)
38
38
  scripts/validation/reports/*.md
39
+ *.code-workspace
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.26.1] - 2026-06-05
11
+
12
+ **Theme**: GitFlow + non-Python UX patch. Four bug fixes from continued fabricOS dogfood — all paths where `codeindex` silently no-op'd or warned about state that wasn't real. No contract changes.
13
+
14
+ ### Fixed
15
+
16
+ - **`codeindex hooks install post-commit` now warns when `.codeindex.yaml` disables the hook** (GH #87): `codeindex init` ships the yaml with `hooks.post_commit.enabled: false` by default, so the installed `.git/hooks/post-commit` wrapper would check the flag at runtime and silently no-op. The install command printed ✓, the user committed code, and `README_AI.md` never updated — classic "installed but not working" trap. New `_maybe_warn_post_commit_disabled` helper reads the yaml after install; when `post_commit.enabled` is false (or absent), prints an actionable reminder with the exact yaml snippet to flip. No behavior change to the install itself — patch-safe. (The doubly-opt-in default itself is a separate contract question deliberately deferred from this patch; the reminder is enough to unblock the first-time path.)
17
+ - **`codeindex init` no longer false-flags installed TS/JS parsers as missing + install hint matches the documented pipx path** (GH #86): `init_wizard.PARSER_PACKAGES` enumerated only python/php/java even though scanner gained TS/JS parsers (#73) and the parser modules exist under `src/codeindex/parsers/{typescript,javascript}/`. Result on a TS-only project: `init --yes` printed `Warning: Missing parsers for: javascript, typescript` despite `pipx inject ai-codeindex tree-sitter-{typescript,javascript}` confirming the packages were already installed. Worse, the hint said `Install with: pip install ai-codeindex[...]` — contradicts the entire documented install path (`pipx install ai-codeindex` everywhere else in CLAUDE.md, README, hooks/index SKILL) and doesn't even work cleanly for a pipx-managed env. Fix: extend `PARSER_PACKAGES` to include typescript/javascript; change the install hint to `pipx inject ai-codeindex tree-sitter-<lang> [tree-sitter-<lang2>...]` (matches the rest of the doc surface). New structural test `test_parser_package_map_covers_scanner_supported_set` locks the invariant — adding a language to scanner.py without exposing it via `PARSER_PACKAGES` will fail the test, preventing the GH #86 drift class from recurring (same pattern as the #73 structural guard).
18
+ - **`scan-all --ai` no longer caches AI refusal text as `enrichment: ok`** (GH #85): when the configured AI backend declined to answer for a directory (e.g. haiku returning "I don't see any file names or symbol names provided…"), Phase 2 wrote the refusal verbatim into the directory's `README_AI.md` blockquote AND stamped `<!-- enrichment: ok -->`. Subsequent `scan-all --ai` runs saw the `ok` marker, skipped re-enrichment, and the garbage description stuck in the cache forever. New `enricher.looks_like_refusal(text)` helper detects common refusal prefixes (case-insensitive: `I don't`, `I cannot`, `I'm unable`, `no file`, `insufficient context`, `as an AI`, etc.); when matched, the directory is marked `failed (reason: ai-refused)` so the next run retries automatically (idempotent re-enrichment policy already in place). Refusal-pattern detection happens on the cleaned/truncated AI output (~80 chars) — refusal phrasing always lives in the first ~30 chars so truncation doesn't blind the check.
19
+ - **Post-commit hook now fires on merge commits** (GH #84): the shell wrapper's loop guard used `git diff-tree --no-commit-id --name-only -r HEAD`, which returns **empty on merge commits** by default — `-m` is required to enumerate per-parent changes. Empty → `NON_DOC_FILES` empty → wrapper exits 0 without delegating to `codeindex hooks run post-commit`. Net effect on GitFlow projects: `README_AI.md` files never auto-updated on PR merges (the commits that actually bring new code to `main` / `develop`). Reported live on fabricOS HEAD `171702b` (Merge PR #7). Fix: add `-m` to the diff-tree command. Verified against a real merge commit in this repo (`e240ba0` — Merge develop for 0.26.0 release): old logic saw 0 files, new logic sees the 20 files actually changed. New regression test in `tests/test_cli_hooks.py::TestHookGeneration::test_post_commit_loop_guard_handles_merge_commits` locks both directions (rejects the broken pattern, requires either `-m` or `git show --name-only`).
20
+
10
21
  ## [0.26.0] - 2026-06-01
11
22
 
12
23
  **Theme**: dogfood-driven CLI bug sweep. Five user-reported issues from fabricOS dogfood — mostly UX cliffs in `init` and `list-dirs` on non-Python projects, plus a navigation-correctness bug in nav-level `README_AI.md`. One contract change in `init --yes` so first `scan-all --ai` works out of the box.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-codeindex
3
- Version: 0.26.0
3
+ Version: 0.26.1
4
4
  Summary: AI-native code indexing tool for large codebases
5
5
  Project-URL: Homepage, https://github.com/dreamlx/codeindex
6
6
  Project-URL: Documentation, https://github.com/dreamlx/codeindex
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ai-codeindex"
3
- version = "0.26.0"
3
+ version = "0.26.1"
4
4
  description = "AI-native code indexing tool for large codebases"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,12 +1,12 @@
1
1
  <!-- codeindex navigation index — agent: drill into source via Read/Grep for precise mechanism; do not treat this as final word. -->
2
- <!-- Generated by codeindex (detailed) at 2026-06-01T11:56:26.178371 -->
2
+ <!-- Generated by codeindex (detailed) at 2026-06-05T21:54:59.634399 -->
3
3
 
4
4
  # codeindex
5
5
 
6
6
  ## Overview
7
7
 
8
8
  - **Files**: 89
9
- - **Symbols**: 570
9
+ - **Symbols**: 572
10
10
 
11
11
  ## Files
12
12
 
@@ -162,7 +162,7 @@ This module provides:
162
162
  - `def install_hook(hook_name: str, repo_path: Optional[Path] = None) -> bool`
163
163
  - `def uninstall_hook(hook_name: str, repo_path: Optional[Path] = None) -> bool`
164
164
 
165
- _... and 6 more symbols_
165
+ _... and 7 more symbols_
166
166
 
167
167
  ### cli_parse.py
168
168
  _CLI parse command - Parse a single source file and output JSON.
@@ -316,8 +316,11 @@ exit 0
316
316
  # Post-commit hook for codeindex
317
317
  # Thin wrapper — all logic in Python (auto-updated via pip)
318
318
 
319
- # Avoid infinite loop: skip if last commit only contains README_AI.md
320
- LAST_COMMIT_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD)
319
+ # Avoid infinite loop: skip if last commit only contains README_AI.md.
320
+ # -m is required so merge commits enumerate per-parent changes — without it
321
+ # `git diff-tree -r HEAD` returns empty on every merge commit and the hook
322
+ # silently skips every PR merge in a GitFlow project (GH #84).
323
+ LAST_COMMIT_FILES=$(git diff-tree --no-commit-id --name-only -r -m HEAD)
321
324
  NON_DOC_FILES=$(echo "$LAST_COMMIT_FILES" | \\
322
325
  grep -v "README_AI.md" | grep -v "PROJECT_INDEX.md" || true)
323
326
  if [ -z "$NON_DOC_FILES" ]; then
@@ -539,6 +542,48 @@ def hooks():
539
542
  pass
540
543
 
541
544
 
545
+ def _maybe_warn_post_commit_disabled(project_dir: Path) -> None:
546
+ """Print a reminder if ``hooks.post_commit.enabled`` is false in
547
+ ``.codeindex.yaml``.
548
+
549
+ ``codeindex init`` ships the yaml with ``enabled: false`` by default,
550
+ so the installed ``.git/hooks/post-commit`` wrapper no-ops at runtime
551
+ even though the install command printed ✓. User commits, READMEs don't
552
+ update, "it doesn't work" — see GH #87.
553
+
554
+ We don't flip the flag automatically (contract change, see #75 for the
555
+ boundary). The reminder makes the contract visible at install time so
556
+ the user can decide.
557
+ """
558
+ yaml_path = project_dir / ".codeindex.yaml"
559
+ if not yaml_path.exists():
560
+ return
561
+
562
+ try:
563
+ import yaml
564
+
565
+ with open(yaml_path) as f:
566
+ data = yaml.safe_load(f) or {}
567
+ except Exception:
568
+ # Yaml unreadable or parse error — silent skip; the install itself
569
+ # succeeded, we don't want this advisory to mask the real result.
570
+ return
571
+
572
+ enabled = data.get("hooks", {}).get("post_commit", {}).get("enabled", False)
573
+ if enabled:
574
+ return
575
+
576
+ console.print(
577
+ "[yellow]⚠[/yellow] [bold]post_commit.enabled is false in "
578
+ ".codeindex.yaml[/bold] — the installed hook wrapper checks this "
579
+ "flag at runtime and will no-op until you flip it.\n"
580
+ " Edit [cyan].codeindex.yaml[/cyan]:\n"
581
+ " hooks:\n"
582
+ " post_commit:\n"
583
+ " [bold]enabled: true[/bold]\n"
584
+ )
585
+
586
+
542
587
  @hooks.command()
543
588
  @click.option(
544
589
  "--all",
@@ -625,6 +670,13 @@ def install(hook_name: Optional[str], install_all: bool, force: bool):
625
670
  f"[dim]→ Skipped {skipped_count} already installed hook(s)[/dim]\n"
626
671
  )
627
672
 
673
+ # GH #87: surface the runtime-disabled trap. If post-commit was just
674
+ # installed (or was already installed), the wrapper still no-ops when
675
+ # .codeindex.yaml has post_commit.enabled=false (the init default).
676
+ # Without this reminder, install prints ✓ but commits trigger nothing.
677
+ if "post-commit" in hooks_to_install:
678
+ _maybe_warn_post_commit_disabled(Path.cwd())
679
+
628
680
  except ValueError as e:
629
681
  console.print(f"[red]✗[/red] Error: {e}", style="red")
630
682
  raise click.Abort()
@@ -668,6 +668,7 @@ def _enrich_directories_with_ai(
668
668
  build_safe_subdir_context,
669
669
  extract_summary_from_readme,
670
670
  inject_blockquote,
671
+ looks_like_refusal,
671
672
  mark_enrichment_status,
672
673
  should_enrich,
673
674
  )
@@ -759,6 +760,20 @@ def _enrich_directories_with_ai(
759
760
  failed_dirs.append(dir_path.name)
760
761
  continue
761
762
 
763
+ # AI returned text, but is it an actual description or a refusal?
764
+ # Stamping refusal text with `enrichment: ok` poisons the cache forever
765
+ # because subsequent runs see the marker and skip re-enrichment (GH #85).
766
+ if looks_like_refusal(description):
767
+ if readme_path.exists():
768
+ mark_enrichment_status(readme_path, "failed", reason="ai-refused")
769
+ failed_dirs.append(dir_path.name)
770
+ if not quiet:
771
+ console.print(
772
+ f"[yellow]⚠[/yellow] {dir_path.name}: AI declined "
773
+ f"(refusal pattern) — will retry next `scan-all --ai`"
774
+ )
775
+ continue
776
+
762
777
  # Inject into README_AI.md
763
778
  if readme_path.exists():
764
779
  inject_blockquote(readme_path, description)
@@ -18,6 +18,46 @@ _MAX_SYMBOLS_PER_FILE = 5
18
18
  # Maximum total files to include in the prompt
19
19
  _MAX_FILES = 15
20
20
 
21
+ # Refusal prefixes used to detect AI responses that decline to summarise the
22
+ # directory (typically because the prompt context was thin enough that the
23
+ # model can't say anything specific). When the model returns "I don't see any
24
+ # file names..." we must NOT stamp `enrichment: ok` — that poisons the cache
25
+ # permanently (GH #85). Instead the response is treated as a failure with
26
+ # reason "ai-refused" so the next `scan-all --ai` retries automatically.
27
+ #
28
+ # Matched against the lowercased, stripped response. Keep prefixes short so
29
+ # the check is robust to upstream truncation (the cli_scan path truncates
30
+ # enrichment output to ~80 chars; refusal phrasing is always in the first 30).
31
+ _REFUSAL_PREFIXES = (
32
+ "i don't",
33
+ "i do not",
34
+ "i cannot",
35
+ "i can't",
36
+ "i'm unable",
37
+ "i am unable",
38
+ "i need more",
39
+ "no file",
40
+ "insufficient context",
41
+ "without more",
42
+ "sorry, i",
43
+ "as an ai",
44
+ )
45
+
46
+
47
+ def looks_like_refusal(text: str) -> bool:
48
+ """Return True if the cleaned AI response looks like a refusal / inability.
49
+
50
+ Used by ``scan-all --ai`` Phase 2 to distinguish a usable one-line
51
+ description from boilerplate refusal text. The refusal text is never
52
+ project-information and stamping it with ``<!-- enrichment: ok -->``
53
+ pins it in the cache; the next run sees the marker and skips
54
+ re-enrichment, so the garbage description sticks indefinitely (GH #85).
55
+ """
56
+ t = (text or "").strip().lower()
57
+ if not t:
58
+ return False
59
+ return any(t.startswith(p) for p in _REFUSAL_PREFIXES)
60
+
21
61
 
22
62
  def extract_symbol_summary(parse_results: list[ParseResult]) -> str:
23
63
  """Extract a compact summary of file names + symbol names for AI prompt.
@@ -94,11 +94,19 @@ FRAMEWORK_PATTERNS: Dict[str, Dict[str, str]] = {
94
94
 
95
95
 
96
96
 
97
- # Parser package mapping: language -> tree-sitter package name
97
+ # Parser package mapping: language -> tree-sitter Python import name.
98
+ #
99
+ # Must stay in sync with ``scanner.LANGUAGE_EXTENSIONS``. The structural
100
+ # test ``test_parser_package_map_covers_scanner_supported_set`` locks the
101
+ # invariant — adding a language to scanner.py without exposing it here
102
+ # silently re-introduces the GH #86 false-positive ("missing parsers" for
103
+ # parsers that are actually installed).
98
104
  PARSER_PACKAGES = {
99
105
  "python": "tree_sitter_python",
100
106
  "php": "tree_sitter_php",
101
107
  "java": "tree_sitter_java",
108
+ "typescript": "tree_sitter_typescript",
109
+ "javascript": "tree_sitter_javascript",
102
110
  }
103
111
 
104
112
 
@@ -146,8 +154,13 @@ def get_parser_install_guidance(languages: list[str]) -> dict:
146
154
  }
147
155
 
148
156
  if missing:
149
- extras = ",".join(missing)
150
- result["install_command"] = f"pip install ai-codeindex[{extras}]"
157
+ # Use `pipx inject ai-codeindex tree-sitter-<lang>` to match the
158
+ # documented install path (CLAUDE.md, README, hooks/index SKILL all
159
+ # use pipx). The previous `pip install ai-codeindex[<lang>]` hint
160
+ # contradicted the recommended path AND broke for pipx-managed
161
+ # envs — see GH #86 (4b).
162
+ pkgs = " ".join(f"tree-sitter-{lang}" for lang in missing)
163
+ result["install_command"] = f"pipx inject ai-codeindex {pkgs}"
151
164
 
152
165
  return result
153
166
 
@@ -1,12 +1,12 @@
1
1
  <!-- codeindex navigation index — agent: drill into source via Read/Grep for precise mechanism; do not treat this as final word. -->
2
- <!-- Generated by codeindex (detailed) at 2026-06-01T11:59:55.553144 -->
2
+ <!-- Generated by codeindex (detailed) at 2026-06-05T21:54:59.931951 -->
3
3
 
4
4
  # tests
5
5
 
6
6
  ## Overview
7
7
 
8
8
  - **Files**: 125
9
- - **Symbols**: 2148
9
+ - **Symbols**: 2164
10
10
 
11
11
  ## Files
12
12
 
@@ -223,6 +223,40 @@ class TestHookGeneration:
223
223
 
224
224
  assert "codeindex-managed hook" in script
225
225
 
226
+ def test_post_commit_loop_guard_handles_merge_commits(self):
227
+ """Regression for GH #84.
228
+
229
+ ``git diff-tree --no-commit-id --name-only -r HEAD`` returns empty
230
+ on merge commits by default — it needs ``-m`` to enumerate the
231
+ per-parent changes. Without the flag, the loop guard sees zero
232
+ files on every merge commit, hits the "all docs" early-exit, and
233
+ the wrapper never delegates to ``codeindex hooks run post-commit``.
234
+
235
+ Net effect on GitFlow projects: ``README_AI.md`` files never
236
+ auto-update on PR merges (the commits that bring new code to
237
+ ``main`` / ``develop``). Reported live on fabricOS HEAD ``171702b``.
238
+ """
239
+ script = generate_hook_script("post-commit")
240
+
241
+ # The broken pattern: -r HEAD without -m. Asserted as a literal
242
+ # substring so future whitespace/format tweaks don't accidentally
243
+ # let it back in.
244
+ assert "diff-tree --no-commit-id --name-only -r HEAD" not in script, (
245
+ "Post-commit loop guard uses `git diff-tree -r HEAD` without -m. "
246
+ "On merge commits this returns empty and the hook silently skips "
247
+ "(GH #84). Use `-m` or `git show --name-only`."
248
+ )
249
+
250
+ # The fix: either -m on diff-tree OR git show --name-only. Both
251
+ # produce non-empty output on merge commits.
252
+ has_dash_m = "diff-tree" in script and " -m " in script
253
+ has_git_show = "git show --name-only" in script
254
+ assert has_dash_m or has_git_show, (
255
+ "Post-commit hook must enumerate files on merge commits. "
256
+ "Expected either `git diff-tree ... -m HEAD` or "
257
+ "`git show --name-only ... HEAD` in the loop guard (GH #84)."
258
+ )
259
+
226
260
 
227
261
  class TestBackupAndRestore:
228
262
  """Test backup and restore functionality."""
@@ -297,3 +331,85 @@ class TestCLIIntegration:
297
331
  """Should provide hooks status CLI command."""
298
332
  # This will be implemented with Click
299
333
  pass
334
+
335
+
336
+ class TestPostCommitEnabledWarning:
337
+ """GH #87 — ``codeindex hooks install post-commit`` must surface the
338
+ runtime-disabled trap.
339
+
340
+ ``codeindex init`` writes ``.codeindex.yaml`` with
341
+ ``hooks.post_commit.enabled: false`` by default. Without a reminder at
342
+ install time, users see the install ✓, make commits, and nothing
343
+ happens because the wrapper checks the flag at runtime. These tests
344
+ cover the three meaningful states (disabled / enabled / no yaml)."""
345
+
346
+ def _yaml(self, enabled: bool) -> str:
347
+ return (
348
+ "version: 1\n"
349
+ "hooks:\n"
350
+ " post_commit:\n"
351
+ f" enabled: {str(enabled).lower()}\n"
352
+ )
353
+
354
+ def test_install_warns_when_yaml_disables_post_commit(self, tmp_path):
355
+ from click.testing import CliRunner
356
+
357
+ from codeindex.cli import main
358
+
359
+ (tmp_path / ".git" / "hooks").mkdir(parents=True)
360
+ (tmp_path / ".codeindex.yaml").write_text(self._yaml(enabled=False))
361
+
362
+ runner = CliRunner()
363
+ original_cwd = os.getcwd()
364
+ try:
365
+ os.chdir(tmp_path)
366
+ result = runner.invoke(main, ["hooks", "install", "post-commit"])
367
+ finally:
368
+ os.chdir(original_cwd)
369
+
370
+ assert result.exit_code == 0, result.output
371
+ assert "post_commit.enabled is false" in result.output, result.output
372
+ # The user-visible fix (the yaml snippet) must be present.
373
+ assert "enabled: true" in result.output
374
+
375
+ def test_install_silent_when_yaml_enables_post_commit(self, tmp_path):
376
+ from click.testing import CliRunner
377
+
378
+ from codeindex.cli import main
379
+
380
+ (tmp_path / ".git" / "hooks").mkdir(parents=True)
381
+ (tmp_path / ".codeindex.yaml").write_text(self._yaml(enabled=True))
382
+
383
+ runner = CliRunner()
384
+ original_cwd = os.getcwd()
385
+ try:
386
+ os.chdir(tmp_path)
387
+ result = runner.invoke(main, ["hooks", "install", "post-commit"])
388
+ finally:
389
+ os.chdir(original_cwd)
390
+
391
+ assert result.exit_code == 0, result.output
392
+ # No reminder when the user has explicitly enabled it.
393
+ assert "post_commit.enabled is false" not in result.output
394
+
395
+ def test_install_silent_when_no_yaml(self, tmp_path):
396
+ """Without ``.codeindex.yaml`` we don't know the project's intent;
397
+ the install itself succeeds, advisory stays silent — matches the
398
+ rule "don't mask the real result of a non-yaml-driven command"."""
399
+ from click.testing import CliRunner
400
+
401
+ from codeindex.cli import main
402
+
403
+ (tmp_path / ".git" / "hooks").mkdir(parents=True)
404
+ # No .codeindex.yaml.
405
+
406
+ runner = CliRunner()
407
+ original_cwd = os.getcwd()
408
+ try:
409
+ os.chdir(tmp_path)
410
+ result = runner.invoke(main, ["hooks", "install", "post-commit"])
411
+ finally:
412
+ os.chdir(original_cwd)
413
+
414
+ assert result.exit_code == 0, result.output
415
+ assert "post_commit.enabled is false" not in result.output
@@ -14,6 +14,7 @@ from codeindex.enricher import (
14
14
  extract_symbol_summary,
15
15
  has_successful_enrichment,
16
16
  inject_blockquote,
17
+ looks_like_refusal,
17
18
  mark_enrichment_status,
18
19
  should_enrich,
19
20
  )
@@ -417,3 +418,75 @@ class TestMarkEnrichmentStatus:
417
418
  line for line in content.split("\n") if line.startswith("<!-- enrichment:")
418
419
  ][0]
419
420
  assert "line2" not in enrichment_line or "\n" not in enrichment_line
421
+
422
+
423
+ class TestLooksLikeRefusal:
424
+ """Tests for the GH #85 refusal detector."""
425
+
426
+ def test_classic_refusal_patterns(self):
427
+ """Phrases observed on fabricOS haiku enrichment failures."""
428
+ # The exact phrase that prompted the bug report:
429
+ assert looks_like_refusal(
430
+ "I don't see any file names or symbol names provided in the directory listing"
431
+ )
432
+ assert looks_like_refusal("I cannot provide a description without more context")
433
+ assert looks_like_refusal("I'm unable to summarise this module")
434
+ assert looks_like_refusal("I am unable to identify the purpose")
435
+ assert looks_like_refusal("I need more information to describe this")
436
+ assert looks_like_refusal("No file names were provided")
437
+ assert looks_like_refusal("Insufficient context to summarise")
438
+ assert looks_like_refusal("Sorry, I can't help with that")
439
+ assert looks_like_refusal("As an AI language model, I can't...")
440
+
441
+ def test_case_insensitive_and_whitespace_tolerant(self):
442
+ assert looks_like_refusal(" I DON'T see any files ")
443
+ assert looks_like_refusal("\n\ni cannot help\n")
444
+ assert looks_like_refusal("I Don't Have Enough Info")
445
+
446
+ def test_normal_descriptions_are_not_refusals(self):
447
+ """Real one-line descriptions must pass through unmolested."""
448
+ assert not looks_like_refusal("会员等级管理、积分兑换、权益卡券")
449
+ assert not looks_like_refusal("Identity and authentication layer")
450
+ assert not looks_like_refusal("HTTP request handlers and middleware")
451
+ assert not looks_like_refusal("Data access layer (repositories + ORM)")
452
+ # English description that mentions "I" but isn't a refusal:
453
+ assert not looks_like_refusal("Implementation of OAuth2 flows")
454
+
455
+ def test_empty_and_whitespace_only(self):
456
+ # Empty / whitespace-only is handled by the existing "empty AI
457
+ # response" branch in cli_scan; the refusal check should NOT also
458
+ # claim it (avoid double-classification).
459
+ assert not looks_like_refusal("")
460
+ assert not looks_like_refusal(" ")
461
+ assert not looks_like_refusal("\n\n\t")
462
+ assert not looks_like_refusal(None) # type: ignore[arg-type]
463
+
464
+
465
+ class TestRefusalEndToEndMarker:
466
+ """GH #85 — refusal text must end up as `failed (ai-refused)`, never `ok`.
467
+
468
+ Mirrors the cli_scan Phase 2 decision branch: after cleaning the AI
469
+ response, the loop chooses between (empty / refused / ok). This test
470
+ drives the same conditional with a refusal sample and asserts the
471
+ on-disk marker + blockquote state.
472
+ """
473
+
474
+ def test_refusal_text_marks_failed_not_ok(self, tmp_path):
475
+ readme = tmp_path / "README_AI.md"
476
+ readme.write_text("# ui\n\n## Overview\n")
477
+ description = "I don't see any file names or symbol names provided"
478
+
479
+ # Same branch order as cli_scan._enrich_directories_with_ai:
480
+ if not description:
481
+ mark_enrichment_status(readme, "failed", reason="empty AI response")
482
+ elif looks_like_refusal(description):
483
+ mark_enrichment_status(readme, "failed", reason="ai-refused")
484
+ else:
485
+ inject_blockquote(readme, description)
486
+ mark_enrichment_status(readme, "ok")
487
+
488
+ content = readme.read_text()
489
+ assert "<!-- enrichment: failed (reason: ai-refused) -->" in content
490
+ assert "<!-- enrichment: ok -->" not in content
491
+ # Blockquote MUST NOT be injected — refusal text is not a description.
492
+ assert "I don't see" not in content
@@ -56,6 +56,71 @@ class TestParserInstallGuidance:
56
56
  assert guidance["missing"] == []
57
57
  assert guidance["installed"] == []
58
58
 
59
+ def test_typescript_javascript_are_known_to_parser_map(self):
60
+ """Regression for GH #86 (4a).
61
+
62
+ ``PARSER_PACKAGES`` used to enumerate only python/php/java, even
63
+ though scanner gained TS/JS parsers (#73) and the parser modules
64
+ exist under ``src/codeindex/parsers/typescript/`` and
65
+ ``.../javascript/``. Result: ``init --yes`` on a TS project
66
+ reported ``Warning: Missing parsers for: javascript, typescript``
67
+ even though ``pipx inject ai-codeindex tree-sitter-{typescript,
68
+ javascript}`` confirmed the packages were already installed.
69
+
70
+ With the fix, TS/JS must be detected as installed in dev/CI
71
+ (where ``[all]`` extra is installed via ``pip install -e .[dev,all]``).
72
+ """
73
+ assert check_parser_installed("typescript") is True
74
+ assert check_parser_installed("javascript") is True
75
+
76
+ guidance = get_parser_install_guidance(["typescript", "javascript"])
77
+ assert "typescript" in guidance["installed"]
78
+ assert "javascript" in guidance["installed"]
79
+ assert "typescript" not in guidance["missing"]
80
+ assert "javascript" not in guidance["missing"]
81
+
82
+ def test_install_command_uses_pipx_inject_not_pip_install(self):
83
+ """Regression for GH #86 (4b).
84
+
85
+ The hint used to be ``pip install ai-codeindex[<langs>]`` — contradicts
86
+ the entire documented install path (``pipx install ai-codeindex`` is the
87
+ recommended path per CLAUDE.md / README / both hooks & index SKILL).
88
+ Worse: ``pip install`` into a pipx-managed env doesn't work cleanly,
89
+ so the hint actively misleads.
90
+
91
+ Force a missing parser by asking for an unknown language; assert the
92
+ hint now uses ``pipx inject``.
93
+ """
94
+ guidance = get_parser_install_guidance(["fortran"])
95
+ assert "install_command" in guidance
96
+ cmd = guidance["install_command"]
97
+ assert cmd.startswith("pipx inject ai-codeindex"), (
98
+ f"Install hint must point to pipx (not pip install); got: {cmd!r}"
99
+ )
100
+ assert "pip install" not in cmd, (
101
+ f"Install hint still mentions `pip install` — contradicts pipx-based "
102
+ f"recommended path (GH #86 4b). Got: {cmd!r}"
103
+ )
104
+
105
+ def test_parser_package_map_covers_scanner_supported_set(self):
106
+ """Structural drift guard. ``PARSER_PACKAGES`` (used to validate
107
+ installed parsers + build install hints) must cover every language
108
+ ``scanner.LANGUAGE_EXTENSIONS`` knows how to scan — otherwise a
109
+ language can be detected by ``init`` (#73) and scanned by the
110
+ runtime, while the parser-presence check silently fails for it,
111
+ re-introducing the GH #86 false-positive warning class."""
112
+ from codeindex.init_wizard import PARSER_PACKAGES
113
+ from codeindex.scanner import LANGUAGE_EXTENSIONS as SCAN_EXT
114
+
115
+ missing = set(SCAN_EXT.keys()) - set(PARSER_PACKAGES.keys())
116
+ assert not missing, (
117
+ f"init_wizard.PARSER_PACKAGES is missing languages that "
118
+ f"scanner.py knows how to scan: {sorted(missing)}. This is the "
119
+ f"drift class that caused GH #86 — the parser-installed check "
120
+ f"silently returns False for these and `init` warns about "
121
+ f"\"missing parsers\" even when they're installed."
122
+ )
123
+
59
124
 
60
125
  class TestInitWizardPostMessage:
61
126
  """Tests for updated post-init messages (Story 19.2)."""
File without changes
File without changes
File without changes
File without changes
File without changes