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,123 @@
1
+ """Forensic check: detect Python/JS escape conflicts in embedded code strings.
2
+
3
+ When JavaScript or CSS lives inside Python triple-quoted strings (*_js.py,
4
+ *_css.py, *_assets*.py), Python interprets \\n as literal newline. Inside a
5
+ JS single-quoted string literal, a literal newline is a syntax error that
6
+ kills the entire <script> block silently.
7
+
8
+ This check scans touched *_js.py / *_css.py / *_assets*.py files for
9
+ unescaped \\n/\\t/\\r inside JS string literals (single or double quoted).
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import re
14
+
15
+ from vigil_forensic._shared import EvidenceReference, GateCategory, GateImpact, GateSeverity
16
+ from vigil_forensic.gate_models import PostExecGateContext
17
+ from .common import build_check_result, build_finding, iter_touched_snapshots
18
+ from ..source_analysis import is_source_file
19
+ import logging
20
+ _log = logging.getLogger(__name__)
21
+
22
+ # Matches: '...\n...' or "...\n..." inside Python triple-quoted strings
23
+ # that will render as literal newlines in JS.
24
+ # We look for single-char \n \t \r that are NOT preceded by another backslash.
25
+ # Pattern: a line containing a JS string with unescaped newline-producing escape.
26
+ _JS_FILE_PATTERNS = ("_js.py", "_css.py", "_assets")
27
+
28
+ # Inside a Python """...""", these are literal control chars:
29
+ # '\n' -> actual newline (JS syntax error in string literal)
30
+ # '\t' -> actual tab (usually OK but suspicious)
31
+ # '\r' -> actual CR (JS syntax error in string literal)
32
+ # We detect: a quote, then content with a real newline before closing quote.
33
+ # Simpler approach: find lines with orphaned quotes (string opened but not closed on same line)
34
+ # inside files that embed JS.
35
+
36
+ # Even simpler: scan for the pattern that caused the real bug:
37
+ # Python source has 'something\nsomething' (without raw prefix or double-backslash)
38
+ # which renders as a literal newline inside JS.
39
+ _DANGEROUS_ESCAPE_RE = re.compile(
40
+ r"""(?<![\\])\\n(?![\\])""" # \n not preceded or followed by another backslash
41
+ )
42
+
43
+
44
+ def run_embedded_string_checks(ctx: PostExecGateContext):
45
+ """Scan *_js.py / *_css.py for Python escape sequences that break embedded JS/CSS."""
46
+ findings = []
47
+ for snapshot in iter_touched_snapshots(ctx):
48
+ if not snapshot.exists:
49
+ continue
50
+ if not any(pat in snapshot.path for pat in _JS_FILE_PATTERNS):
51
+ continue
52
+ if not is_source_file(snapshot.path):
53
+ continue
54
+
55
+ # We need to check the PYTHON SOURCE, not the rendered output.
56
+ # Look for lines inside triple-quoted strings that contain
57
+ # single-backslash \n which Python will turn into a literal newline.
58
+ # The tricky part: we're reading the file as Python source.
59
+ lines = snapshot.text.splitlines()
60
+ in_triple = False
61
+ triple_char = ""
62
+ for line_idx, line in enumerate(lines, 1):
63
+ # Track triple-quote state (simplified — enough for _js.py files)
64
+ count_triple_dq = line.count('"""')
65
+ count_triple_sq = line.count("'''")
66
+ if count_triple_dq % 2 == 1:
67
+ in_triple = not in_triple
68
+ triple_char = '"'
69
+ if count_triple_sq % 2 == 1:
70
+ in_triple = not in_triple
71
+ triple_char = "'"
72
+
73
+ if not in_triple:
74
+ continue
75
+
76
+ # Inside a triple-quoted string: look for JS string literals
77
+ # containing \n that Python will interpret as literal newline.
78
+ # In the Python source, this looks like: split('\n')
79
+ # The \n here is a real newline in the output.
80
+ # We want to find: quote + backslash-n + quote patterns
81
+ # that are NOT \\n (escaped backslash).
82
+ for m in re.finditer(r"""(?:['"]).*?(?<!\\)\\n.*?(?:['"])""", line):
83
+ # Check it's not \\n (double backslash)
84
+ match_text = m.group()
85
+ if "\\\\n" in match_text:
86
+ continue # properly escaped
87
+ findings.append(
88
+ build_finding(
89
+ check_id="embedded_string.unescaped_newline",
90
+ category=GateCategory.FALLBACK,
91
+ title=f"Unescaped \\n in JS/CSS string: {snapshot.path}:{line_idx}",
92
+ severity=GateSeverity.HIGH,
93
+ impact=GateImpact.REVISE,
94
+ summary=(
95
+ f"File {snapshot.path} line {line_idx} contains '\\n' inside a "
96
+ "Python triple-quoted string that embeds JS/CSS. Python renders "
97
+ "this as a literal newline, which is a JS syntax error inside "
98
+ "string literals. The entire <script> block will fail to parse."
99
+ ),
100
+ recommendation=(
101
+ "Use '\\\\n' (double backslash) so Python outputs '\\n' which "
102
+ "JS interprets as a newline escape. Or use a raw string r'...'."
103
+ ),
104
+ evidence=[
105
+ EvidenceReference(
106
+ kind="file",
107
+ path=snapshot.path,
108
+ detail=f"line:{line_idx} match:{match_text[:60]}",
109
+ )
110
+ ],
111
+
112
+ repair_kind='refactor',
113
+ executor_action='Address finding details',
114
+ proof_required='No embedded strings',
115
+ allowlist_allowed=False,
116
+ )
117
+ )
118
+
119
+ return build_check_result(
120
+ check_id="embedded_string",
121
+ category=GateCategory.FALLBACK,
122
+ findings=findings,
123
+ )
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ from vigil_forensic._shared import BINARY_EXTENSIONS as _BINARY_EXTENSIONS
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, normalize_path
8
+ import logging
9
+ _log = logging.getLogger(__name__)
10
+
11
+ _EMPTY_ALLOWLIST = {"__init__.py", ".gitkeep"}
12
+ # Sprint C3 (2026-04-23): _BINARY_EXTENSIONS imported above from
13
+ # SYSTEM.shared_helpers.file_extensions. Keep the private alias so existing
14
+ # call sites (abs_path.suffix.lower() in _BINARY_EXTENSIONS) resolve.
15
+
16
+
17
+ def run_empty_output_checks(ctx: PostExecGateContext):
18
+ findings = []
19
+ if not ctx.changed_files_observed or ctx.task_intent == "metadata_only":
20
+ return build_check_result(check_id="empty_output", category=GateCategory.REPORTING)
21
+ for raw_path in ctx.changed_files_observed:
22
+ normalized = normalize_path(raw_path)
23
+ abs_path = ctx.project_dir / normalized
24
+
25
+ # Probe existence first. A PermissionError on stat() (raised inside
26
+ # pathlib.Path.exists) must NOT be masked as a missing or empty
27
+ # file: that would silently hide a real issue. Surface it via
28
+ # meta.file_unreadable and move on.
29
+ try:
30
+ present = abs_path.exists()
31
+ except (PermissionError, OSError) as exc:
32
+ from vigil_forensic.meta_findings import emit_meta_finding
33
+ emit_meta_finding(
34
+ "meta.file_unreadable",
35
+ path=normalized,
36
+ detail=f"{type(exc).__name__}: {exc}",
37
+ )
38
+ continue
39
+
40
+ if not present:
41
+ continue # drift_checks handles missing files
42
+ try:
43
+ is_file = abs_path.is_file()
44
+ except (PermissionError, OSError) as exc:
45
+ from vigil_forensic.meta_findings import emit_meta_finding
46
+ emit_meta_finding(
47
+ "meta.file_unreadable",
48
+ path=normalized,
49
+ detail=f"{type(exc).__name__}: {exc}",
50
+ )
51
+ continue
52
+ if not is_file:
53
+ continue
54
+ basename = abs_path.name
55
+ if basename in _EMPTY_ALLOWLIST:
56
+ continue
57
+ if abs_path.suffix.lower() in _BINARY_EXTENSIONS:
58
+ continue
59
+ try:
60
+ size = abs_path.stat().st_size
61
+ except (PermissionError, OSError) as exc:
62
+ from vigil_forensic.meta_findings import emit_meta_finding
63
+ emit_meta_finding(
64
+ "meta.file_unreadable",
65
+ path=normalized,
66
+ detail=f"{type(exc).__name__}: {exc}",
67
+ )
68
+ continue
69
+ if size == 0:
70
+ findings.append(
71
+ build_finding(
72
+ check_id="empty_output.zero_bytes",
73
+ category=GateCategory.REPORTING,
74
+ title=f"Changed file is empty (0 bytes): {normalized}",
75
+ severity=GateSeverity.MEDIUM,
76
+ impact=GateImpact.REVISE,
77
+ summary=f"Executor reported {normalized} as changed but the file is 0 bytes. This may indicate a false-success or truncated write.",
78
+ recommendation="Verify the file was written correctly. If intentionally empty, document why.",
79
+ evidence=[EvidenceReference(kind="file", path=normalized, detail="0 bytes")],
80
+
81
+ repair_kind='fix_contract',
82
+ executor_action='Fix empty output',
83
+ proof_required='Non-empty output',
84
+ allowlist_allowed=False,
85
+ )
86
+ )
87
+ return build_check_result(check_id="empty_output", category=GateCategory.REPORTING, findings=findings)