vigil-codeintel 0.1.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 (131) hide show
  1. vigil_codeintel-0.1.0.dist-info/METADATA +780 -0
  2. vigil_codeintel-0.1.0.dist-info/RECORD +131 -0
  3. vigil_codeintel-0.1.0.dist-info/WHEEL +5 -0
  4. vigil_codeintel-0.1.0.dist-info/entry_points.txt +3 -0
  5. vigil_codeintel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. vigil_codeintel-0.1.0.dist-info/top_level.txt +3 -0
  7. vigil_forensic/__init__.py +224 -0
  8. vigil_forensic/_git_utils.py +178 -0
  9. vigil_forensic/_shared.py +510 -0
  10. vigil_forensic/_stubs.py +156 -0
  11. vigil_forensic/gate_checks/__init__.py +1 -0
  12. vigil_forensic/gate_checks/_ast_helpers.py +629 -0
  13. vigil_forensic/gate_checks/_deployment_detector.py +573 -0
  14. vigil_forensic/gate_checks/atomic_write_checks.py +1143 -0
  15. vigil_forensic/gate_checks/authority_checks.py +95 -0
  16. vigil_forensic/gate_checks/boundary_breach_checks.py +202 -0
  17. vigil_forensic/gate_checks/broad_except_checks.py +301 -0
  18. vigil_forensic/gate_checks/broad_except_hidden_sentinel_checks.py +365 -0
  19. vigil_forensic/gate_checks/common.py +253 -0
  20. vigil_forensic/gate_checks/config_safety_checks.py +704 -0
  21. vigil_forensic/gate_checks/config_ssot_checks.py +78 -0
  22. vigil_forensic/gate_checks/conflict_checks.py +193 -0
  23. vigil_forensic/gate_checks/context_fallback_checks.py +697 -0
  24. vigil_forensic/gate_checks/context_health_checks.py +289 -0
  25. vigil_forensic/gate_checks/contract_shape_drift_checks.py +459 -0
  26. vigil_forensic/gate_checks/dirty_baseline_check.py +274 -0
  27. vigil_forensic/gate_checks/duplication_checks.py +387 -0
  28. vigil_forensic/gate_checks/embedded_string_checks.py +123 -0
  29. vigil_forensic/gate_checks/empty_output_checks.py +87 -0
  30. vigil_forensic/gate_checks/encoding_checks.py +847 -0
  31. vigil_forensic/gate_checks/export_completeness_checks.py +156 -0
  32. vigil_forensic/gate_checks/fallback_checks.py +41 -0
  33. vigil_forensic/gate_checks/file_proliferation_checks.py +171 -0
  34. vigil_forensic/gate_checks/fix_without_test_checks.py +69 -0
  35. vigil_forensic/gate_checks/forensic_cluster_runners/__init__.py +9 -0
  36. vigil_forensic/gate_checks/forensic_cluster_runners/_helpers.py +71 -0
  37. vigil_forensic/gate_checks/forensic_cluster_runners/advanced_checks.py +322 -0
  38. vigil_forensic/gate_checks/forensic_cluster_runners/core.py +273 -0
  39. vigil_forensic/gate_checks/forensic_cluster_runners/integrity_checks.py +203 -0
  40. vigil_forensic/gate_checks/forensic_cluster_runners/quality_checks.py +666 -0
  41. vigil_forensic/gate_checks/forensic_clusters/__init__.py +193 -0
  42. vigil_forensic/gate_checks/forensic_clusters/allowlist.py +426 -0
  43. vigil_forensic/gate_checks/forensic_clusters/allowlist_writer.py +302 -0
  44. vigil_forensic/gate_checks/forensic_clusters/api_protocol.py +231 -0
  45. vigil_forensic/gate_checks/forensic_clusters/async_quality.py +1156 -0
  46. vigil_forensic/gate_checks/forensic_clusters/code_style.py +808 -0
  47. vigil_forensic/gate_checks/forensic_clusters/core.py +319 -0
  48. vigil_forensic/gate_checks/forensic_clusters/data_quality.py +763 -0
  49. vigil_forensic/gate_checks/forensic_clusters/dead_code.py +480 -0
  50. vigil_forensic/gate_checks/forensic_clusters/edit_mutation.py +842 -0
  51. vigil_forensic/gate_checks/forensic_clusters/exception_boundary.py +240 -0
  52. vigil_forensic/gate_checks/forensic_clusters/legacy_debt.py +556 -0
  53. vigil_forensic/gate_checks/forensic_clusters/static_analysis.py +834 -0
  54. vigil_forensic/gate_checks/forensic_clusters/structural_quality.py +298 -0
  55. vigil_forensic/gate_checks/god_object_zones_checks.py +173 -0
  56. vigil_forensic/gate_checks/hallucination_checks.py +566 -0
  57. vigil_forensic/gate_checks/hunter_artifact_completeness_check.py +139 -0
  58. vigil_forensic/gate_checks/implementation_overfit_checks.py +380 -0
  59. vigil_forensic/gate_checks/import_integrity_checks.py +233 -0
  60. vigil_forensic/gate_checks/imports_in_function_checks.py +283 -0
  61. vigil_forensic/gate_checks/ml_checks.py +318 -0
  62. vigil_forensic/gate_checks/performance_checks.py +106 -0
  63. vigil_forensic/gate_checks/project_specific_runner.py +691 -0
  64. vigil_forensic/gate_checks/provider_capability_checks.py +73 -0
  65. vigil_forensic/gate_checks/refactor_completeness_checks.py +274 -0
  66. vigil_forensic/gate_checks/reliability_checks.py +389 -0
  67. vigil_forensic/gate_checks/reporting_checks.py +55 -0
  68. vigil_forensic/gate_checks/runtime_behavior_checks.py +220 -0
  69. vigil_forensic/gate_checks/security_injection_checks.py +332 -0
  70. vigil_forensic/gate_checks/semantic_intent_checks.py +139 -0
  71. vigil_forensic/gate_checks/size_complexity_checks.py +336 -0
  72. vigil_forensic/gate_checks/stuck_feature_flag_checks.py +354 -0
  73. vigil_forensic/gate_checks/syntax_validity_checks.py +217 -0
  74. vigil_forensic/gate_checks/temporal_freshness_checks.py +79 -0
  75. vigil_forensic/gate_checks/test_quality_checks.py +946 -0
  76. vigil_forensic/gate_checks/testing_checks.py +149 -0
  77. vigil_forensic/gate_checks/toctou_checks.py +367 -0
  78. vigil_forensic/gate_checks/type_checking_checks.py +316 -0
  79. vigil_forensic/gate_models.py +392 -0
  80. vigil_forensic/gate_packs/__init__.py +1 -0
  81. vigil_forensic/gate_packs/universal.py +179 -0
  82. vigil_forensic/gate_profile.json +31 -0
  83. vigil_forensic/gate_registry.py +21 -0
  84. vigil_forensic/language_profiles.py +219 -0
  85. vigil_forensic/meta_findings.py +207 -0
  86. vigil_forensic/self_audit.py +725 -0
  87. vigil_forensic/source_analysis.py +175 -0
  88. vigil_mapper/__init__.py +103 -0
  89. vigil_mapper/_ast_helpers_minimal.py +229 -0
  90. vigil_mapper/_extract_imports_impl.py +123 -0
  91. vigil_mapper/_file_count_guard.py +129 -0
  92. vigil_mapper/_git_utils.py +178 -0
  93. vigil_mapper/_runtime_ast.py +438 -0
  94. vigil_mapper/_runtime_dispatch.py +137 -0
  95. vigil_mapper/_seed_helpers.py +82 -0
  96. vigil_mapper/authority_builder.py +1102 -0
  97. vigil_mapper/cli_entry.py +731 -0
  98. vigil_mapper/conflict_builder.py +818 -0
  99. vigil_mapper/data_contract_builder.py +446 -0
  100. vigil_mapper/findings_builder.py +716 -0
  101. vigil_mapper/fingerprint.py +53 -0
  102. vigil_mapper/hotspot_builder.py +539 -0
  103. vigil_mapper/map_common.py +449 -0
  104. vigil_mapper/map_errors.py +55 -0
  105. vigil_mapper/map_models.py +431 -0
  106. vigil_mapper/map_models_ext.py +206 -0
  107. vigil_mapper/map_models_findings.py +130 -0
  108. vigil_mapper/map_storage.py +455 -0
  109. vigil_mapper/parse_cache.py +795 -0
  110. vigil_mapper/refactor_boundary_builder.py +266 -0
  111. vigil_mapper/runtime_builder.py +527 -0
  112. vigil_mapper/runtime_tracer.py +243 -0
  113. vigil_mapper/runtime_tracer_entry.py +199 -0
  114. vigil_mapper/semantic_diff.py +71 -0
  115. vigil_mapper/source_adapters/__init__.py +109 -0
  116. vigil_mapper/source_adapters/_base.py +264 -0
  117. vigil_mapper/source_adapters/_ir.py +156 -0
  118. vigil_mapper/source_adapters/_lexer.py +309 -0
  119. vigil_mapper/source_adapters/_patterns.py +212 -0
  120. vigil_mapper/source_adapters/_treesitter.py +182 -0
  121. vigil_mapper/source_adapters/go.py +553 -0
  122. vigil_mapper/source_adapters/java.py +541 -0
  123. vigil_mapper/source_adapters/javascript.py +626 -0
  124. vigil_mapper/source_adapters/python.py +325 -0
  125. vigil_mapper/source_adapters/typescript.py +749 -0
  126. vigil_mapper/structural_builder.py +586 -0
  127. vigil_mcp/__init__.py +1 -0
  128. vigil_mcp/_jobs.py +587 -0
  129. vigil_mcp/_paths.py +93 -0
  130. vigil_mcp/forensic_server.py +419 -0
  131. vigil_mcp/map_server.py +452 -0
@@ -0,0 +1,217 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+
5
+ from vigil_forensic._shared import EvidenceReference, GateCategory, GateImpact, GateSeverity, RepairKind
6
+ from vigil_forensic.gate_models import PostExecGateContext
7
+ from ..source_analysis import get_language_id, is_source_file
8
+ from .common import build_check_result, build_finding, iter_touched_snapshots
9
+ import logging
10
+ _log = logging.getLogger(__name__)
11
+
12
+
13
+ def _find_brace_imbalance(text: str) -> tuple[int, str] | None:
14
+ """Return ``(line_number, reason)`` for a brace/bracket/paren imbalance.
15
+
16
+ Lightweight textual check for non-Python sources. Tracks ``() [] {}`` but
17
+ respects string literals (`'` `"` backtick) and ``//`` / ``#`` /
18
+ ``/* ... */`` comments. This is intentionally conservative: when in
19
+ doubt it returns ``None`` (no finding) rather than a speculative FP.
20
+ """
21
+ depth_paren = 0
22
+ depth_bracket = 0
23
+ depth_brace = 0
24
+
25
+ in_line_comment = False
26
+ in_block_comment = False
27
+ in_string: str | None = None
28
+ string_start_line = 0
29
+ lineno = 1
30
+ last_open: list[tuple[str, int]] = []
31
+
32
+ i = 0
33
+ n = len(text)
34
+ while i < n:
35
+ ch = text[i]
36
+ nxt = text[i + 1] if i + 1 < n else ""
37
+
38
+ if ch == "\n":
39
+ lineno += 1
40
+ in_line_comment = False
41
+ i += 1
42
+ continue
43
+
44
+ if in_line_comment:
45
+ i += 1
46
+ continue
47
+
48
+ if in_block_comment:
49
+ if ch == "*" and nxt == "/":
50
+ in_block_comment = False
51
+ i += 2
52
+ continue
53
+ i += 1
54
+ continue
55
+
56
+ if in_string is not None:
57
+ if ch == "\\":
58
+ i += 2 # skip escaped char
59
+ continue
60
+ if ch == in_string:
61
+ in_string = None
62
+ i += 1
63
+ continue
64
+
65
+ # Not in string/comment.
66
+ if ch == "/" and nxt == "/":
67
+ in_line_comment = True
68
+ i += 2
69
+ continue
70
+ if ch == "/" and nxt == "*":
71
+ in_block_comment = True
72
+ i += 2
73
+ continue
74
+ if ch == "#":
75
+ # Only treat '#' as a line comment for shell-ish / generic sources;
76
+ # harmless for JS because it's never a valid operator start.
77
+ in_line_comment = True
78
+ i += 1
79
+ continue
80
+ if ch in ("'", '"', "`"):
81
+ in_string = ch
82
+ string_start_line = lineno
83
+ i += 1
84
+ continue
85
+
86
+ if ch == "(":
87
+ depth_paren += 1
88
+ last_open.append(("(", lineno))
89
+ elif ch == ")":
90
+ depth_paren -= 1
91
+ if depth_paren < 0:
92
+ return lineno, "unmatched ')'"
93
+ if last_open and last_open[-1][0] == "(":
94
+ last_open.pop()
95
+ elif ch == "[":
96
+ depth_bracket += 1
97
+ last_open.append(("[", lineno))
98
+ elif ch == "]":
99
+ depth_bracket -= 1
100
+ if depth_bracket < 0:
101
+ return lineno, "unmatched ']'"
102
+ if last_open and last_open[-1][0] == "[":
103
+ last_open.pop()
104
+ elif ch == "{":
105
+ depth_brace += 1
106
+ last_open.append(("{", lineno))
107
+ elif ch == "}":
108
+ depth_brace -= 1
109
+ if depth_brace < 0:
110
+ return lineno, "unmatched '}'"
111
+ if last_open and last_open[-1][0] == "{":
112
+ last_open.pop()
113
+
114
+ i += 1
115
+
116
+ if in_string is not None:
117
+ return string_start_line, f"unclosed string literal (opened line {string_start_line})"
118
+ if in_block_comment:
119
+ return lineno, "unclosed block comment"
120
+ if depth_paren or depth_bracket or depth_brace:
121
+ if last_open:
122
+ br, ln = last_open[-1]
123
+ return ln, f"unclosed '{br}' (opened line {ln})"
124
+ return lineno, "bracket depth nonzero at EOF"
125
+ return None
126
+
127
+
128
+ # Languages for which we apply the Python ``ast.parse`` check. Every other
129
+ # source language uses the textual balanced-brace fallback below -- we never
130
+ # try to parse non-Python as Python (F12c fix: same category of bug as the
131
+ # F9a Python-only branch).
132
+ _PYTHON_LANGUAGE_IDS: frozenset[str] = frozenset({"python"})
133
+
134
+ # Languages for which a textual brace/bracket/paren balance check is a useful
135
+ # heuristic. Shell/PowerShell/Go-style sources are NOT listed because their
136
+ # syntax differs enough that brace balance isn't a reliable signal.
137
+ _BRACE_LANGUAGE_IDS: frozenset[str] = frozenset({
138
+ "javascript", "typescript",
139
+ "java", "kotlin", "scala",
140
+ "cpp", "c", "csharp",
141
+ "rust",
142
+ "json",
143
+ })
144
+
145
+
146
+ def run_syntax_validity_checks(ctx: PostExecGateContext):
147
+ """Check that touched source files parse / balance correctly.
148
+
149
+ Language dispatch:
150
+ Python -> ``ast.parse()``
151
+ JS / TS / Java / C / ... -> textual brace/paren balance only
152
+ Shell / PS1 / Go / ... -> skipped (no reliable lightweight check)
153
+
154
+ Never attempts to parse a non-Python file as Python -- that was the F12c
155
+ bug (100% FP on ``.ts``/``.tsx``/``.jsx``/``.go`` snapshots).
156
+ """
157
+ findings = []
158
+ for snapshot in iter_touched_snapshots(ctx):
159
+ if not snapshot.exists or not is_source_file(snapshot.path):
160
+ continue
161
+ if not snapshot.text.strip():
162
+ continue # empty files are handled by empty_output_checks
163
+
164
+ lang = get_language_id(snapshot.path)
165
+
166
+ if lang in _PYTHON_LANGUAGE_IDS:
167
+ try:
168
+ ast.parse(snapshot.text, filename=snapshot.path)
169
+ except SyntaxError as exc:
170
+ line_info = f"line {exc.lineno}" if exc.lineno else "unknown line"
171
+ findings.append(
172
+ build_finding(
173
+ check_id="syntax_validity.parse_error",
174
+ category=GateCategory.REPORTING,
175
+ title=f"SyntaxError in {snapshot.path} ({line_info})",
176
+ severity=GateSeverity.HIGH,
177
+ impact=GateImpact.REVISE,
178
+ summary=(
179
+ f"File {snapshot.path} contains invalid Python syntax at {line_info}: "
180
+ f"{str(exc.msg)[:200]}. This will cause ImportError at runtime."
181
+ ),
182
+ recommendation="Fix the syntax error before accepting the executor result.",
183
+ evidence=[EvidenceReference(kind="file", path=snapshot.path, detail=f"SyntaxError:{line_info}")],
184
+ repair_kind=RepairKind.FIX_CONTRACT.value,
185
+ executor_action="Fix syntax error",
186
+ proof_required="Valid syntax",
187
+ allowlist_allowed=False,
188
+ )
189
+ )
190
+ elif lang in _BRACE_LANGUAGE_IDS:
191
+ imbalance = _find_brace_imbalance(snapshot.text)
192
+ if imbalance is not None:
193
+ line_no, reason = imbalance
194
+ findings.append(
195
+ build_finding(
196
+ check_id="syntax_validity.parse_error",
197
+ category=GateCategory.REPORTING,
198
+ title=f"Brace/bracket imbalance in {snapshot.path} (line {line_no})",
199
+ severity=GateSeverity.HIGH,
200
+ impact=GateImpact.REVISE,
201
+ summary=(
202
+ f"File {snapshot.path} has {reason} at line {line_no}. "
203
+ "Lightweight textual check -- fix the imbalance."
204
+ ),
205
+ recommendation="Close the unmatched bracket or remove the stray one.",
206
+ evidence=[EvidenceReference(kind="file", path=snapshot.path, detail=f"imbalance:{reason}")],
207
+ repair_kind=RepairKind.FIX_CONTRACT.value,
208
+ executor_action="Fix bracket imbalance",
209
+ proof_required="Balanced brackets",
210
+ allowlist_allowed=False,
211
+ )
212
+ )
213
+ # else: language without a lightweight check (shell, powershell, go, ...)
214
+ # -- emit 0 findings rather than 100% FP. Real syntax errors surface
215
+ # via the language's own tooling.
216
+
217
+ return build_check_result(check_id="syntax_validity", category=GateCategory.REPORTING, findings=findings)
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from vigil_forensic._shared import EvidenceReference, GateCategory, GateImpact, GateSeverity
6
+ from vigil_forensic.gate_models import PostExecGateContext
7
+ from .common import build_check_result, build_finding
8
+ import logging
9
+ _log = logging.getLogger(__name__)
10
+
11
+
12
+ def run_temporal_freshness_checks(ctx: PostExecGateContext):
13
+ findings = []
14
+ handoff_path = ctx.artifact_refs.get("executor_handoff", "")
15
+ prompt_path = ctx.session_artifacts.get("system_prompt", "")
16
+ forensic_path = ctx.artifact_refs.get("forensic", "")
17
+
18
+ handoff_ts = _mtime(handoff_path)
19
+ prompt_ts = _mtime(prompt_path)
20
+ forensic_ts = _mtime(forensic_path)
21
+
22
+ if prompt_ts > 0.0 and handoff_ts > 0.0 and handoff_ts + 0.001 < prompt_ts:
23
+ findings.append(
24
+ build_finding(
25
+ check_id="temporal.handoff_before_prompt",
26
+ category=GateCategory.TEMPORAL_FRESHNESS,
27
+ title="Executor handoff is older than the Claude prompt",
28
+ severity=GateSeverity.CRITICAL,
29
+ impact=GateImpact.BLOCK,
30
+ summary="The handoff artifact timestamp predates the prompt artifact for the same session.",
31
+ recommendation="Verify artifact selection by current session/attempt; do not reuse stale handoff files.",
32
+ evidence=[
33
+ EvidenceReference(kind="artifact", path=prompt_path, detail=f"prompt_mtime={prompt_ts}"),
34
+ EvidenceReference(kind="artifact", path=handoff_path, detail=f"handoff_mtime={handoff_ts}"),
35
+ ],
36
+
37
+ repair_kind='refactor',
38
+ executor_action='Address finding details',
39
+ proof_required='Freshness acceptable',
40
+ allowlist_allowed=False,
41
+ )
42
+ )
43
+
44
+ if handoff_ts > 0.0 and forensic_ts > 0.0 and forensic_ts + 0.001 < handoff_ts:
45
+ findings.append(
46
+ build_finding(
47
+ check_id="temporal.forensic_before_handoff",
48
+ category=GateCategory.TEMPORAL_FRESHNESS,
49
+ title="Forensic evidence is older than the executor handoff",
50
+ severity=GateSeverity.HIGH,
51
+ impact=GateImpact.REVISE,
52
+ summary="The forensic artifact appears older than the handoff it is supposed to validate.",
53
+ recommendation="Rerun forensic validation after the executor handoff and persist fresh evidence.",
54
+ evidence=[
55
+ EvidenceReference(kind="artifact", path=handoff_path, detail=f"handoff_mtime={handoff_ts}"),
56
+ EvidenceReference(kind="artifact", path=forensic_path, detail=f"forensic_mtime={forensic_ts}"),
57
+ ],
58
+
59
+ repair_kind='refactor',
60
+ executor_action='Address finding details',
61
+ proof_required='Freshness acceptable',
62
+ allowlist_allowed=False,
63
+ )
64
+ )
65
+
66
+ return build_check_result(
67
+ check_id="temporal_freshness",
68
+ category=GateCategory.TEMPORAL_FRESHNESS,
69
+ findings=findings,
70
+ )
71
+
72
+
73
+ def _mtime(path_text: str) -> float:
74
+ if not path_text:
75
+ return 0.0
76
+ path = Path(path_text)
77
+ if not path.exists() or not path.is_file():
78
+ return 0.0
79
+ return path.stat().st_mtime