codexa 0.4.0__py3-none-any.whl

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 (189) hide show
  1. codexa-0.4.0.dist-info/METADATA +650 -0
  2. codexa-0.4.0.dist-info/RECORD +189 -0
  3. codexa-0.4.0.dist-info/WHEEL +5 -0
  4. codexa-0.4.0.dist-info/entry_points.txt +2 -0
  5. codexa-0.4.0.dist-info/licenses/LICENSE +21 -0
  6. codexa-0.4.0.dist-info/top_level.txt +1 -0
  7. semantic_code_intelligence/__init__.py +5 -0
  8. semantic_code_intelligence/analysis/__init__.py +21 -0
  9. semantic_code_intelligence/analysis/ai_features.py +351 -0
  10. semantic_code_intelligence/bridge/__init__.py +28 -0
  11. semantic_code_intelligence/bridge/context_provider.py +245 -0
  12. semantic_code_intelligence/bridge/protocol.py +167 -0
  13. semantic_code_intelligence/bridge/server.py +348 -0
  14. semantic_code_intelligence/bridge/vscode.py +271 -0
  15. semantic_code_intelligence/ci/__init__.py +13 -0
  16. semantic_code_intelligence/ci/hooks.py +98 -0
  17. semantic_code_intelligence/ci/hotspots.py +272 -0
  18. semantic_code_intelligence/ci/impact.py +246 -0
  19. semantic_code_intelligence/ci/metrics.py +591 -0
  20. semantic_code_intelligence/ci/pr.py +412 -0
  21. semantic_code_intelligence/ci/quality.py +557 -0
  22. semantic_code_intelligence/ci/templates.py +164 -0
  23. semantic_code_intelligence/ci/trace.py +224 -0
  24. semantic_code_intelligence/cli/__init__.py +0 -0
  25. semantic_code_intelligence/cli/commands/__init__.py +0 -0
  26. semantic_code_intelligence/cli/commands/ask_cmd.py +153 -0
  27. semantic_code_intelligence/cli/commands/benchmark_cmd.py +303 -0
  28. semantic_code_intelligence/cli/commands/chat_cmd.py +252 -0
  29. semantic_code_intelligence/cli/commands/ci_gen_cmd.py +74 -0
  30. semantic_code_intelligence/cli/commands/context_cmd.py +120 -0
  31. semantic_code_intelligence/cli/commands/cross_refactor_cmd.py +113 -0
  32. semantic_code_intelligence/cli/commands/deps_cmd.py +91 -0
  33. semantic_code_intelligence/cli/commands/docs_cmd.py +101 -0
  34. semantic_code_intelligence/cli/commands/doctor_cmd.py +147 -0
  35. semantic_code_intelligence/cli/commands/evolve_cmd.py +171 -0
  36. semantic_code_intelligence/cli/commands/explain_cmd.py +112 -0
  37. semantic_code_intelligence/cli/commands/gate_cmd.py +135 -0
  38. semantic_code_intelligence/cli/commands/grep_cmd.py +234 -0
  39. semantic_code_intelligence/cli/commands/hotspots_cmd.py +119 -0
  40. semantic_code_intelligence/cli/commands/impact_cmd.py +131 -0
  41. semantic_code_intelligence/cli/commands/index_cmd.py +138 -0
  42. semantic_code_intelligence/cli/commands/init_cmd.py +152 -0
  43. semantic_code_intelligence/cli/commands/investigate_cmd.py +163 -0
  44. semantic_code_intelligence/cli/commands/languages_cmd.py +101 -0
  45. semantic_code_intelligence/cli/commands/lsp_cmd.py +49 -0
  46. semantic_code_intelligence/cli/commands/mcp_cmd.py +50 -0
  47. semantic_code_intelligence/cli/commands/metrics_cmd.py +264 -0
  48. semantic_code_intelligence/cli/commands/models_cmd.py +157 -0
  49. semantic_code_intelligence/cli/commands/plugin_cmd.py +275 -0
  50. semantic_code_intelligence/cli/commands/pr_summary_cmd.py +178 -0
  51. semantic_code_intelligence/cli/commands/quality_cmd.py +208 -0
  52. semantic_code_intelligence/cli/commands/refactor_cmd.py +103 -0
  53. semantic_code_intelligence/cli/commands/review_cmd.py +88 -0
  54. semantic_code_intelligence/cli/commands/search_cmd.py +236 -0
  55. semantic_code_intelligence/cli/commands/serve_cmd.py +117 -0
  56. semantic_code_intelligence/cli/commands/suggest_cmd.py +100 -0
  57. semantic_code_intelligence/cli/commands/summary_cmd.py +78 -0
  58. semantic_code_intelligence/cli/commands/tool_cmd.py +282 -0
  59. semantic_code_intelligence/cli/commands/trace_cmd.py +123 -0
  60. semantic_code_intelligence/cli/commands/tui_cmd.py +58 -0
  61. semantic_code_intelligence/cli/commands/viz_cmd.py +127 -0
  62. semantic_code_intelligence/cli/commands/watch_cmd.py +72 -0
  63. semantic_code_intelligence/cli/commands/web_cmd.py +61 -0
  64. semantic_code_intelligence/cli/commands/workspace_cmd.py +250 -0
  65. semantic_code_intelligence/cli/main.py +65 -0
  66. semantic_code_intelligence/cli/router.py +92 -0
  67. semantic_code_intelligence/config/__init__.py +0 -0
  68. semantic_code_intelligence/config/settings.py +260 -0
  69. semantic_code_intelligence/context/__init__.py +19 -0
  70. semantic_code_intelligence/context/engine.py +429 -0
  71. semantic_code_intelligence/context/memory.py +253 -0
  72. semantic_code_intelligence/daemon/__init__.py +1 -0
  73. semantic_code_intelligence/daemon/watcher.py +515 -0
  74. semantic_code_intelligence/docs/__init__.py +1080 -0
  75. semantic_code_intelligence/embeddings/__init__.py +0 -0
  76. semantic_code_intelligence/embeddings/enhanced.py +131 -0
  77. semantic_code_intelligence/embeddings/generator.py +149 -0
  78. semantic_code_intelligence/embeddings/model_registry.py +100 -0
  79. semantic_code_intelligence/evolution/__init__.py +1 -0
  80. semantic_code_intelligence/evolution/budget_guard.py +111 -0
  81. semantic_code_intelligence/evolution/commit_manager.py +88 -0
  82. semantic_code_intelligence/evolution/context_builder.py +131 -0
  83. semantic_code_intelligence/evolution/engine.py +249 -0
  84. semantic_code_intelligence/evolution/patch_generator.py +229 -0
  85. semantic_code_intelligence/evolution/task_selector.py +214 -0
  86. semantic_code_intelligence/evolution/test_runner.py +111 -0
  87. semantic_code_intelligence/indexing/__init__.py +0 -0
  88. semantic_code_intelligence/indexing/chunker.py +174 -0
  89. semantic_code_intelligence/indexing/parallel.py +86 -0
  90. semantic_code_intelligence/indexing/scanner.py +146 -0
  91. semantic_code_intelligence/indexing/semantic_chunker.py +337 -0
  92. semantic_code_intelligence/llm/__init__.py +62 -0
  93. semantic_code_intelligence/llm/cache.py +219 -0
  94. semantic_code_intelligence/llm/cached_provider.py +145 -0
  95. semantic_code_intelligence/llm/conversation.py +190 -0
  96. semantic_code_intelligence/llm/cross_refactor.py +272 -0
  97. semantic_code_intelligence/llm/investigation.py +274 -0
  98. semantic_code_intelligence/llm/mock_provider.py +77 -0
  99. semantic_code_intelligence/llm/ollama_provider.py +122 -0
  100. semantic_code_intelligence/llm/openai_provider.py +100 -0
  101. semantic_code_intelligence/llm/provider.py +92 -0
  102. semantic_code_intelligence/llm/rate_limiter.py +164 -0
  103. semantic_code_intelligence/llm/reasoning.py +438 -0
  104. semantic_code_intelligence/llm/safety.py +110 -0
  105. semantic_code_intelligence/llm/streaming.py +251 -0
  106. semantic_code_intelligence/lsp/__init__.py +609 -0
  107. semantic_code_intelligence/mcp/__init__.py +393 -0
  108. semantic_code_intelligence/parsing/__init__.py +19 -0
  109. semantic_code_intelligence/parsing/parser.py +375 -0
  110. semantic_code_intelligence/plugins/__init__.py +255 -0
  111. semantic_code_intelligence/plugins/examples/__init__.py +1 -0
  112. semantic_code_intelligence/plugins/examples/code_quality.py +73 -0
  113. semantic_code_intelligence/plugins/examples/search_annotator.py +56 -0
  114. semantic_code_intelligence/scalability/__init__.py +205 -0
  115. semantic_code_intelligence/search/__init__.py +0 -0
  116. semantic_code_intelligence/search/formatter.py +123 -0
  117. semantic_code_intelligence/search/grep.py +361 -0
  118. semantic_code_intelligence/search/hybrid_search.py +170 -0
  119. semantic_code_intelligence/search/keyword_search.py +311 -0
  120. semantic_code_intelligence/search/section_expander.py +103 -0
  121. semantic_code_intelligence/services/__init__.py +0 -0
  122. semantic_code_intelligence/services/indexing_service.py +630 -0
  123. semantic_code_intelligence/services/search_service.py +269 -0
  124. semantic_code_intelligence/storage/__init__.py +0 -0
  125. semantic_code_intelligence/storage/chunk_hash_store.py +86 -0
  126. semantic_code_intelligence/storage/hash_store.py +66 -0
  127. semantic_code_intelligence/storage/index_manifest.py +85 -0
  128. semantic_code_intelligence/storage/index_stats.py +138 -0
  129. semantic_code_intelligence/storage/query_history.py +160 -0
  130. semantic_code_intelligence/storage/symbol_registry.py +209 -0
  131. semantic_code_intelligence/storage/vector_store.py +297 -0
  132. semantic_code_intelligence/tests/__init__.py +0 -0
  133. semantic_code_intelligence/tests/test_ai_features.py +351 -0
  134. semantic_code_intelligence/tests/test_chunker.py +119 -0
  135. semantic_code_intelligence/tests/test_cli.py +188 -0
  136. semantic_code_intelligence/tests/test_config.py +154 -0
  137. semantic_code_intelligence/tests/test_context.py +381 -0
  138. semantic_code_intelligence/tests/test_embeddings.py +73 -0
  139. semantic_code_intelligence/tests/test_endtoend.py +1142 -0
  140. semantic_code_intelligence/tests/test_enhanced_embeddings.py +92 -0
  141. semantic_code_intelligence/tests/test_hash_store.py +79 -0
  142. semantic_code_intelligence/tests/test_logging.py +55 -0
  143. semantic_code_intelligence/tests/test_new_cli.py +138 -0
  144. semantic_code_intelligence/tests/test_parser.py +495 -0
  145. semantic_code_intelligence/tests/test_phase10.py +355 -0
  146. semantic_code_intelligence/tests/test_phase11.py +593 -0
  147. semantic_code_intelligence/tests/test_phase12.py +375 -0
  148. semantic_code_intelligence/tests/test_phase13.py +663 -0
  149. semantic_code_intelligence/tests/test_phase14.py +568 -0
  150. semantic_code_intelligence/tests/test_phase15.py +814 -0
  151. semantic_code_intelligence/tests/test_phase16.py +792 -0
  152. semantic_code_intelligence/tests/test_phase17.py +815 -0
  153. semantic_code_intelligence/tests/test_phase18.py +934 -0
  154. semantic_code_intelligence/tests/test_phase19.py +986 -0
  155. semantic_code_intelligence/tests/test_phase20.py +2753 -0
  156. semantic_code_intelligence/tests/test_phase20b.py +2058 -0
  157. semantic_code_intelligence/tests/test_phase20c.py +962 -0
  158. semantic_code_intelligence/tests/test_phase21.py +428 -0
  159. semantic_code_intelligence/tests/test_phase22.py +799 -0
  160. semantic_code_intelligence/tests/test_phase23.py +783 -0
  161. semantic_code_intelligence/tests/test_phase24.py +715 -0
  162. semantic_code_intelligence/tests/test_phase25.py +496 -0
  163. semantic_code_intelligence/tests/test_phase26.py +251 -0
  164. semantic_code_intelligence/tests/test_phase27.py +531 -0
  165. semantic_code_intelligence/tests/test_phase8.py +592 -0
  166. semantic_code_intelligence/tests/test_phase9.py +643 -0
  167. semantic_code_intelligence/tests/test_plugins.py +293 -0
  168. semantic_code_intelligence/tests/test_priority_features.py +727 -0
  169. semantic_code_intelligence/tests/test_router.py +41 -0
  170. semantic_code_intelligence/tests/test_scalability.py +138 -0
  171. semantic_code_intelligence/tests/test_scanner.py +125 -0
  172. semantic_code_intelligence/tests/test_search.py +160 -0
  173. semantic_code_intelligence/tests/test_semantic_chunker.py +255 -0
  174. semantic_code_intelligence/tests/test_tools.py +182 -0
  175. semantic_code_intelligence/tests/test_vector_store.py +151 -0
  176. semantic_code_intelligence/tests/test_watcher.py +211 -0
  177. semantic_code_intelligence/tools/__init__.py +442 -0
  178. semantic_code_intelligence/tools/executor.py +232 -0
  179. semantic_code_intelligence/tools/protocol.py +200 -0
  180. semantic_code_intelligence/tui/__init__.py +454 -0
  181. semantic_code_intelligence/utils/__init__.py +0 -0
  182. semantic_code_intelligence/utils/logging.py +112 -0
  183. semantic_code_intelligence/version.py +3 -0
  184. semantic_code_intelligence/web/__init__.py +11 -0
  185. semantic_code_intelligence/web/api.py +289 -0
  186. semantic_code_intelligence/web/server.py +397 -0
  187. semantic_code_intelligence/web/ui.py +659 -0
  188. semantic_code_intelligence/web/visualize.py +226 -0
  189. semantic_code_intelligence/workspace/__init__.py +427 -0
@@ -0,0 +1,428 @@
1
+ """Phase 21 — Mypy Strict Typing & Pytest Coverage Gate.
2
+
3
+ Tests verify:
4
+ 1. pyproject.toml mypy strict config exists and is correct
5
+ 2. pyproject.toml coverage config exists with fail_under gate
6
+ 3. All 49 original mypy errors stay fixed (regression guard)
7
+ 4. Type annotations are present on key functions
8
+ 5. No bare ``dict`` return types remain in source
9
+ 6. Critical bug fixes (SafetyReport import, FileDependency attr, etc.)
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import ast
15
+ import configparser
16
+ import importlib
17
+ import inspect
18
+ import re
19
+ import subprocess
20
+ import sys
21
+ from pathlib import Path
22
+ from typing import Any
23
+ from unittest.mock import MagicMock, patch
24
+
25
+ import pytest
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Helpers
29
+ # ---------------------------------------------------------------------------
30
+
31
+ _PROJECT_ROOT = Path(__file__).resolve().parents[2]
32
+ _PYPROJECT = _PROJECT_ROOT / "pyproject.toml"
33
+ _SRC = _PROJECT_ROOT / "semantic_code_intelligence"
34
+
35
+
36
+ def _read_pyproject() -> str:
37
+ return _PYPROJECT.read_text(encoding="utf-8")
38
+
39
+
40
+ # ═══════════════════════════════════════════════════════════════════════════
41
+ # 1 — pyproject.toml: [tool.mypy] section
42
+ # ═══════════════════════════════════════════════════════════════════════════
43
+
44
+ class TestMypyConfig:
45
+ """[tool.mypy] section validations."""
46
+
47
+ def test_mypy_section_exists(self) -> None:
48
+ text = _read_pyproject()
49
+ assert "[tool.mypy]" in text
50
+
51
+ def test_strict_enabled(self) -> None:
52
+ text = _read_pyproject()
53
+ assert "strict = true" in text
54
+
55
+ def test_warn_return_any(self) -> None:
56
+ text = _read_pyproject()
57
+ assert "warn_return_any = true" in text
58
+
59
+ def test_warn_unused_ignores(self) -> None:
60
+ text = _read_pyproject()
61
+ assert "warn_unused_ignores = true" in text
62
+
63
+ def test_ignore_missing_imports(self) -> None:
64
+ text = _read_pyproject()
65
+ assert "ignore_missing_imports = true" in text
66
+
67
+ def test_tests_excluded(self) -> None:
68
+ text = _read_pyproject()
69
+ assert 'exclude = ["tests/"]' in text
70
+
71
+
72
+ # ═══════════════════════════════════════════════════════════════════════════
73
+ # 2 — pyproject.toml: coverage config
74
+ # ═══════════════════════════════════════════════════════════════════════════
75
+
76
+ class TestCoverageConfig:
77
+ """[tool.coverage.*] section validations."""
78
+
79
+ def test_coverage_run_section_exists(self) -> None:
80
+ text = _read_pyproject()
81
+ assert "[tool.coverage.run]" in text
82
+
83
+ def test_coverage_source(self) -> None:
84
+ text = _read_pyproject()
85
+ assert 'source = ["semantic_code_intelligence"]' in text
86
+
87
+ def test_coverage_omit_tests(self) -> None:
88
+ text = _read_pyproject()
89
+ assert "semantic_code_intelligence/tests/*" in text
90
+
91
+ def test_coverage_report_section_exists(self) -> None:
92
+ text = _read_pyproject()
93
+ assert "[tool.coverage.report]" in text
94
+
95
+ def test_fail_under_gate(self) -> None:
96
+ text = _read_pyproject()
97
+ # Expect fail_under = 70 (or any integer >= 70)
98
+ match = re.search(r"fail_under\s*=\s*(\d+)", text)
99
+ assert match is not None, "fail_under not found in pyproject.toml"
100
+ assert int(match.group(1)) >= 70
101
+
102
+ def test_show_missing(self) -> None:
103
+ text = _read_pyproject()
104
+ assert "show_missing = true" in text
105
+
106
+
107
+ # ═══════════════════════════════════════════════════════════════════════════
108
+ # 3 — Bug-fix regression guards (the 49 original errors)
109
+ # ═══════════════════════════════════════════════════════════════════════════
110
+
111
+ class TestSafetyReportImport:
112
+ """ci/pr.py must import SafetyReport."""
113
+
114
+ def test_import_exists(self) -> None:
115
+ from semantic_code_intelligence.ci import pr
116
+ assert hasattr(pr, "SafetyReport")
117
+
118
+ def test_safety_report_in_source(self) -> None:
119
+ src = (_SRC / "ci" / "pr.py").read_text(encoding="utf-8")
120
+ assert "SafetyReport" in src
121
+ assert "from semantic_code_intelligence.llm.safety import" in src
122
+
123
+
124
+ class TestFileDependencyAttr:
125
+ """investigation.py must use .import_text not .module."""
126
+
127
+ def test_no_dot_module_usage(self) -> None:
128
+ src = (_SRC / "llm" / "investigation.py").read_text(encoding="utf-8")
129
+ # Should NOT contain d.module
130
+ assert ".module" not in src or "import_text" in src
131
+
132
+ def test_import_text_usage(self) -> None:
133
+ src = (_SRC / "llm" / "investigation.py").read_text(encoding="utf-8")
134
+ assert "import_text" in src
135
+
136
+
137
+ class TestVizCmdReposList:
138
+ """viz_cmd.py must iterate list, not call .values()."""
139
+
140
+ def test_no_values_call(self) -> None:
141
+ src = (_SRC / "cli" / "commands" / "viz_cmd.py").read_text(encoding="utf-8")
142
+ assert ".repos.values()" not in src
143
+
144
+ def test_direct_iteration(self) -> None:
145
+ src = (_SRC / "cli" / "commands" / "viz_cmd.py").read_text(encoding="utf-8")
146
+ assert "ws.repos]" in src or "r.to_dict() for r in ws.repos" in src
147
+
148
+
149
+ class TestQualityCmdDuplicateVar:
150
+ """quality_cmd.py should not reuse dead_code var name for duplicates."""
151
+
152
+ def test_duplicate_loop_uses_dup_var(self) -> None:
153
+ src = (_SRC / "cli" / "commands" / "quality_cmd.py").read_text(encoding="utf-8")
154
+ # The duplicates loop should use 'dup' not 'd'
155
+ assert "for dup in report.duplicates:" in src
156
+
157
+
158
+ class TestCrossRefactorTuple:
159
+ """cross_refactor.py pair_key must be tuple[str, str]."""
160
+
161
+ def test_no_bare_tuple_sorted(self) -> None:
162
+ src = (_SRC / "llm" / "cross_refactor.py").read_text(encoding="utf-8")
163
+ # Should NOT have tuple(sorted([...]))
164
+ assert "tuple(sorted(" not in src
165
+
166
+
167
+ class TestImpactAffectedSymbolVar:
168
+ """impact.py loop vars for AffectedSymbol lists renamed."""
169
+
170
+ def test_direct_loop_uses_af(self) -> None:
171
+ src = (_SRC / "ci" / "impact.py").read_text(encoding="utf-8")
172
+ assert "for af in direct:" in src or "for af in direct[" in src
173
+
174
+
175
+ # ═══════════════════════════════════════════════════════════════════════════
176
+ # 4 — Type annotation presence checks
177
+ # ═══════════════════════════════════════════════════════════════════════════
178
+
179
+ class TestGetProviderAnnotated:
180
+ """_get_provider functions must have type annotations."""
181
+
182
+ @pytest.mark.parametrize("mod_path", [
183
+ "semantic_code_intelligence.cli.commands.chat_cmd",
184
+ "semantic_code_intelligence.cli.commands.ask_cmd",
185
+ "semantic_code_intelligence.cli.commands.investigate_cmd",
186
+ ])
187
+ def test_get_provider_has_return_annotation(self, mod_path: str) -> None:
188
+ mod = importlib.import_module(mod_path)
189
+ fn = getattr(mod, "_get_provider")
190
+ hints = fn.__annotations__
191
+ assert "return" in hints, f"{mod_path}._get_provider missing return annotation"
192
+
193
+ @pytest.mark.parametrize("mod_path", [
194
+ "semantic_code_intelligence.cli.commands.chat_cmd",
195
+ "semantic_code_intelligence.cli.commands.ask_cmd",
196
+ "semantic_code_intelligence.cli.commands.investigate_cmd",
197
+ ])
198
+ def test_get_provider_has_config_annotation(self, mod_path: str) -> None:
199
+ mod = importlib.import_module(mod_path)
200
+ fn = getattr(mod, "_get_provider")
201
+ hints = fn.__annotations__
202
+ assert "config" in hints, f"{mod_path}._get_provider missing config annotation"
203
+
204
+
205
+ class TestDoctorCmdTypedDicts:
206
+ """doctor_cmd.py functions must return dict[str, Any], not bare dict."""
207
+
208
+ def test_check_python_return_annotation(self) -> None:
209
+ from semantic_code_intelligence.cli.commands import doctor_cmd
210
+ hints = doctor_cmd._check_python.__annotations__
211
+ assert "return" in hints
212
+
213
+ def test_check_package_return_annotation(self) -> None:
214
+ from semantic_code_intelligence.cli.commands import doctor_cmd
215
+ hints = doctor_cmd._check_package.__annotations__
216
+ assert "return" in hints
217
+
218
+ def test_check_project_return_annotation(self) -> None:
219
+ from semantic_code_intelligence.cli.commands import doctor_cmd
220
+ hints = doctor_cmd._check_project.__annotations__
221
+ assert "return" in hints
222
+
223
+ def test_run_checks_return_annotation(self) -> None:
224
+ from semantic_code_intelligence.cli.commands import doctor_cmd
225
+ hints = doctor_cmd.run_checks.__annotations__
226
+ assert "return" in hints
227
+
228
+
229
+ class TestSearchServiceTypedDict:
230
+ """search_service.SearchResult.to_dict must return dict[str, Any]."""
231
+
232
+ def test_to_dict_return_type(self) -> None:
233
+ from semantic_code_intelligence.services.search_service import SearchResult
234
+ hints = SearchResult.to_dict.__annotations__
235
+ assert "return" in hints
236
+
237
+
238
+ # ═══════════════════════════════════════════════════════════════════════════
239
+ # 5 — No bare dict returns in source (AST scan)
240
+ # ═══════════════════════════════════════════════════════════════════════════
241
+
242
+ class TestNoBareDictReturns:
243
+ """Source files should not have bare 'dict' return annotations."""
244
+
245
+ @pytest.mark.parametrize("rel_path", [
246
+ "cli/commands/doctor_cmd.py",
247
+ "services/search_service.py",
248
+ "tools/__init__.py",
249
+ "bridge/context_provider.py",
250
+ ])
251
+ def test_no_bare_dict_in_file(self, rel_path: str) -> None:
252
+ src = (_SRC / rel_path).read_text(encoding="utf-8")
253
+ tree = ast.parse(src)
254
+ for node in ast.walk(tree):
255
+ if isinstance(node, ast.FunctionDef):
256
+ ret = node.returns
257
+ if ret is not None:
258
+ # Check for bare Name("dict") without subscript
259
+ if isinstance(ret, ast.Name) and ret.id == "dict":
260
+ pytest.fail(
261
+ f"{rel_path}:{node.lineno} — "
262
+ f"function '{node.name}' has bare 'dict' return"
263
+ )
264
+ # Check for list[dict] without subscript
265
+ if isinstance(ret, ast.Subscript):
266
+ if isinstance(ret.slice, ast.Name) and ret.slice.id == "dict":
267
+ pytest.fail(
268
+ f"{rel_path}:{node.lineno} — "
269
+ f"function '{node.name}' has list[dict] return"
270
+ )
271
+
272
+
273
+ # ═══════════════════════════════════════════════════════════════════════════
274
+ # 6 — No-any-return fixes
275
+ # ═══════════════════════════════════════════════════════════════════════════
276
+
277
+ class TestNoAnyReturnFixes:
278
+ """Key functions should cast/annotate to avoid returning Any."""
279
+
280
+ def test_ollama_api_call_returns_typed(self) -> None:
281
+ src = (_SRC / "llm" / "ollama_provider.py").read_text(encoding="utf-8")
282
+ assert "result: dict[str, Any]" in src
283
+
284
+ def test_vector_store_size_int_cast(self) -> None:
285
+ src = (_SRC / "storage" / "vector_store.py").read_text(encoding="utf-8")
286
+ assert "int(self.index.ntotal)" in src
287
+
288
+ def test_embedding_dim_none_guard(self) -> None:
289
+ src = (_SRC / "embeddings" / "generator.py").read_text(encoding="utf-8")
290
+ assert "if dim is None:" in src
291
+
292
+ def test_templates_typed_generators(self) -> None:
293
+ src = (_SRC / "ci" / "templates.py").read_text(encoding="utf-8")
294
+ assert "Callable[..., str]" in src
295
+
296
+
297
+ # ═══════════════════════════════════════════════════════════════════════════
298
+ # 7 — Stale type:ignore removal
299
+ # ═══════════════════════════════════════════════════════════════════════════
300
+
301
+ class TestNoStaleTypeIgnore:
302
+ """Unused type:ignore comments should be removed."""
303
+
304
+ def test_plugins_no_unused_ignore(self) -> None:
305
+ src = (_SRC / "plugins" / "__init__.py").read_text(encoding="utf-8")
306
+ # The exec_module line should NOT have type: ignore
307
+ for line in src.splitlines():
308
+ if "exec_module" in line:
309
+ assert "type: ignore" not in line, f"Stale type:ignore: {line}"
310
+
311
+ def test_openai_no_unused_ignore(self) -> None:
312
+ src = (_SRC / "llm" / "openai_provider.py").read_text(encoding="utf-8")
313
+ for line in src.splitlines():
314
+ if "import openai" in line and "ignore" in line.lower():
315
+ pytest.fail(f"Stale type:ignore on openai import: {line}")
316
+
317
+
318
+ # ═══════════════════════════════════════════════════════════════════════════
319
+ # 8 — docs/__init__.py click type fix
320
+ # ═══════════════════════════════════════════════════════════════════════════
321
+
322
+ class TestDocsClickType:
323
+ """docs/__init__.py should not use click.BaseCommand as type hint."""
324
+
325
+ def test_no_base_command_annotation(self) -> None:
326
+ src = (_SRC / "docs" / "__init__.py").read_text(encoding="utf-8")
327
+ assert "group: click.BaseCommand" not in src
328
+
329
+ def test_uses_proper_type(self) -> None:
330
+ src = (_SRC / "docs" / "__init__.py").read_text(encoding="utf-8")
331
+ # Should use click.Group | click.Command or similar
332
+ assert "click.Group" in src or "click.Command" in src
333
+
334
+
335
+ # ═══════════════════════════════════════════════════════════════════════════
336
+ # 9 — Functional regression: fixed modules still work
337
+ # ═══════════════════════════════════════════════════════════════════════════
338
+
339
+ class TestFixedModulesImport:
340
+ """All fixed modules import without error."""
341
+
342
+ @pytest.mark.parametrize("mod_path", [
343
+ "semantic_code_intelligence.ci.pr",
344
+ "semantic_code_intelligence.ci.impact",
345
+ "semantic_code_intelligence.ci.templates",
346
+ "semantic_code_intelligence.cli.commands.doctor_cmd",
347
+ "semantic_code_intelligence.cli.commands.quality_cmd",
348
+ "semantic_code_intelligence.cli.commands.chat_cmd",
349
+ "semantic_code_intelligence.cli.commands.ask_cmd",
350
+ "semantic_code_intelligence.cli.commands.investigate_cmd",
351
+ "semantic_code_intelligence.cli.commands.viz_cmd",
352
+ "semantic_code_intelligence.cli.commands.cross_refactor_cmd",
353
+ "semantic_code_intelligence.llm.ollama_provider",
354
+ "semantic_code_intelligence.llm.openai_provider",
355
+ "semantic_code_intelligence.llm.investigation",
356
+ "semantic_code_intelligence.llm.cross_refactor",
357
+ "semantic_code_intelligence.storage.vector_store",
358
+ "semantic_code_intelligence.embeddings.generator",
359
+ "semantic_code_intelligence.services.search_service",
360
+ "semantic_code_intelligence.tools",
361
+ "semantic_code_intelligence.search.formatter",
362
+ "semantic_code_intelligence.bridge.context_provider",
363
+ "semantic_code_intelligence.web.api",
364
+ "semantic_code_intelligence.docs",
365
+ "semantic_code_intelligence.plugins",
366
+ ])
367
+ def test_module_imports(self, mod_path: str) -> None:
368
+ importlib.import_module(mod_path)
369
+
370
+
371
+ # ═══════════════════════════════════════════════════════════════════════════
372
+ # 10 — Coverage gate configuration is respected
373
+ # ═══════════════════════════════════════════════════════════════════════════
374
+
375
+ class TestCoverageGateIntegrity:
376
+ """Coverage gate integrates properly with pytest-cov."""
377
+
378
+ def test_pytest_cov_installed(self) -> None:
379
+ import pytest_cov # noqa: F401
380
+
381
+ def test_coverage_source_matches_package(self) -> None:
382
+ text = _read_pyproject()
383
+ assert "semantic_code_intelligence" in text
384
+ # Coverage source points to the right package
385
+ match = re.search(r'source\s*=\s*\["([^"]+)"\]', text)
386
+ assert match is not None
387
+ assert match.group(1) == "semantic_code_intelligence"
388
+
389
+
390
+ # ═══════════════════════════════════════════════════════════════════════════
391
+ # 11 — TYPE_CHECKING guard pattern
392
+ # ═══════════════════════════════════════════════════════════════════════════
393
+
394
+ class TestTypeCheckingGuard:
395
+ """Files using TYPE_CHECKING should guard runtime-only imports."""
396
+
397
+ @pytest.mark.parametrize("rel_path", [
398
+ "cli/commands/chat_cmd.py",
399
+ "cli/commands/ask_cmd.py",
400
+ "cli/commands/investigate_cmd.py",
401
+ ])
402
+ def test_type_checking_import(self, rel_path: str) -> None:
403
+ src = (_SRC / rel_path).read_text(encoding="utf-8")
404
+ assert "TYPE_CHECKING" in src
405
+ assert "if TYPE_CHECKING:" in src
406
+
407
+
408
+ # ═══════════════════════════════════════════════════════════════════════════
409
+ # 12 — Version tagging
410
+ # ═══════════════════════════════════════════════════════════════════════════
411
+
412
+ class TestVersion:
413
+ """Version must be ≥ 0.4.0 for stable release."""
414
+
415
+ def test_version_in_init(self) -> None:
416
+ from semantic_code_intelligence import __version__
417
+ parts = __version__.split(".")
418
+ assert len(parts) >= 3
419
+ major, minor = int(parts[0]), int(parts[1])
420
+ assert (major, minor) >= (0, 4)
421
+
422
+ def test_version_in_pyproject(self) -> None:
423
+ text = _read_pyproject()
424
+ match = re.search(r'version\s*=\s*"(\d+\.\d+\.\d+)"', text)
425
+ assert match is not None
426
+ parts = match.group(1).split(".")
427
+ major, minor = int(parts[0]), int(parts[1])
428
+ assert (major, minor) >= (0, 4)