git-oops 1.0.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.
src/guardian.py ADDED
@@ -0,0 +1,589 @@
1
+ """
2
+ Git-Oops Guardian: AST Ghost Import Linter & Shadow Regression Guard
3
+ 1. Scans proposed Python patches using stdlib `ast` for fake/hallucinated modules.
4
+ 2. Sandboxes the patch and runs existing unit tests before allowing permanent writes.
5
+ """
6
+
7
+ import ast
8
+ import os
9
+ import sys
10
+ import shutil
11
+ import tempfile
12
+ import subprocess
13
+ from typing import Tuple, List, Optional
14
+
15
+
16
+ import ast
17
+ import os
18
+ import sys
19
+ import re
20
+ import shutil
21
+ import tempfile
22
+ import subprocess
23
+ from typing import Tuple, List, Optional
24
+
25
+
26
+ # Well-known Node.js / JavaScript built-in standard modules
27
+ NODE_BUILTINS = {
28
+ "fs", "path", "http", "https", "crypto", "os", "events", "stream", "util",
29
+ "child_process", "buffer", "url", "zlib", "cluster", "net", "tls", "assert",
30
+ "process", "readline", "worker_threads", "perf_hooks", "constants"
31
+ }
32
+
33
+ # Standard library modules for Go
34
+ GO_STDLIB_PREFIXES = {
35
+ "fmt", "os", "io", "net", "time", "sync", "strings", "bytes", "errors",
36
+ "encoding", "context", "math", "path", "database", "crypto", "log",
37
+ "bufio", "sort", "regexp", "strconv", "flag", "syscall", "testing"
38
+ }
39
+
40
+ # Standard library crates for Rust
41
+ RUST_STDLIB_CRATES = {
42
+ "std", "core", "alloc", "proc_macro", "test"
43
+ }
44
+
45
+ # Standard Java / Kotlin packages
46
+ JAVA_STDLIB_PREFIXES = {
47
+ "java", "javax", "sun", "com.sun", "org.w3c", "org.xml", "kotlin", "android", "androidx", "org.junit"
48
+ }
49
+
50
+ # Standard C/C++ headers
51
+ CPP_STD_HEADERS = {
52
+ "iostream", "vector", "string", "map", "set", "memory", "algorithm",
53
+ "cmath", "cstdio", "cstdlib", "cstring", "thread", "mutex", "chrono",
54
+ "unordered_map", "unordered_set", "queue", "deque", "stack", "utility",
55
+ "functional", "fstream", "sstream", "type_traits", "exception",
56
+ "stdio.h", "stdlib.h", "string.h", "math.h", "time.h", "unistd.h",
57
+ "fcntl.h", "sys/types.h", "sys/stat.h", "pthread.h", "assert.h"
58
+ }
59
+
60
+ # Standard PHP extensions, built-in classes, and namespaces
61
+ PHP_BUILTIN_EXTENSIONS = {
62
+ "date", "pcre", "zlib", "filter", "hash", "json", "spl", "pdo", "session",
63
+ "standard", "tokenizer", "xml", "curl", "mbstring", "openssl", "fileinfo",
64
+ "datetime", "datetimeimmutable", "exception", "stdclass", "closure",
65
+ "generator", "throwable", "error", "iterator", "arrayaccess", "countable"
66
+ }
67
+
68
+ # Standard Ruby library modules
69
+ RUBY_STDLIB_MODULES = {
70
+ "json", "fileutils", "net/http", "uri", "securerandom", "date", "time",
71
+ "open-uri", "logger", "stringio", "base64", "csv", "yaml", "digest",
72
+ "pathname", "optparse", "singleton", "benchmark", "tempfile"
73
+ }
74
+
75
+ # Standard .NET / C# / F# / VB.NET namespaces
76
+ DOTNET_STDLIB_NAMESPACES = {
77
+ "System", "Microsoft", "Windows", "FSharp", "Internal"
78
+ }
79
+
80
+ # Standard Swift / Apple frameworks
81
+ SWIFT_STDLIB_MODULES = {
82
+ "Foundation", "SwiftUI", "UIKit", "AppKit", "CoreGraphics", "Combine",
83
+ "CoreData", "CoreLocation", "AVFoundation", "Network", "Dispatch", "OSLog"
84
+ }
85
+
86
+ # Standard Dart / Flutter packages
87
+ DART_STDLIB_PACKAGES = {
88
+ "dart:async", "dart:collection", "dart:convert", "dart:core", "dart:developer",
89
+ "dart:io", "dart:isolate", "dart:math", "dart:typed_data", "package:flutter"
90
+ }
91
+
92
+ # Standard Lua modules
93
+ LUA_STDLIB_MODULES = {
94
+ "string", "table", "math", "io", "os", "debug", "coroutine", "utf8", "package"
95
+ }
96
+
97
+ # Standard Scala packages
98
+ SCALA_STDLIB_PREFIXES = {
99
+ "scala", "java", "javax"
100
+ }
101
+
102
+ # Standard Elixir modules
103
+ ELIXIR_STDLIB_MODULES = {
104
+ "Enum", "String", "Map", "List", "File", "IO", "Path", "System", "Kernel",
105
+ "Task", "Agent", "GenServer", "Process", "Keyword", "Integer", "Float", "Stream", "Logger"
106
+ }
107
+
108
+ # Standard R packages
109
+ R_BASE_PACKAGES = {
110
+ "base", "stats", "graphics", "grDevices", "utils", "datasets", "methods", "grid", "tools"
111
+ }
112
+
113
+ # Standard Julia modules
114
+ JULIA_STDLIB_MODULES = {
115
+ "Base", "Core", "Dates", "Distributed", "LinearAlgebra", "Logging", "Markdown",
116
+ "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs", "Statistics"
117
+ }
118
+
119
+ # Standard Perl pragmas and core modules
120
+ PERL_CORE_MODULES = {
121
+ "strict", "warnings", "utf8", "vars", "constant", "lib", "Exporter", "Carp",
122
+ "File::Spec", "File::Basename", "File::Path", "Data::Dumper", "Getopt::Long",
123
+ "Scalar::Util", "List::Util", "JSON", "Time::HiRes", "POSIX"
124
+ }
125
+
126
+ # Standard Haskell modules
127
+ HASKELL_STDLIB_PREFIXES = {
128
+ "Prelude", "Data", "Control", "System", "Debug", "GHC", "Text", "Numeric", "Foreign"
129
+ }
130
+
131
+ # Standard Zig modules
132
+ ZIG_BUILTIN_MODULES = {
133
+ "std", "builtin"
134
+ }
135
+
136
+ # Standard Nim modules
137
+ NIM_STDLIB_MODULES = {
138
+ "strutils", "sequtils", "os", "tables", "json", "times", "math", "sugar",
139
+ "streams", "parseutils", "options", "uri", "httpclient", "asyncfutures", "asyncdispatch"
140
+ }
141
+
142
+
143
+ def check_ghost_imports(code_snippet: str, filepath: str = "app.py") -> Tuple[bool, Optional[str]]:
144
+ """
145
+ Universal Polyglot Import Verification Engine:
146
+ Validates Python, JS/TS, Go, Rust, Java, C/C++, PHP, Ruby, and unknown scripts.
147
+ """
148
+ ext = os.path.splitext(filepath)[1].lower()
149
+
150
+ # JavaScript / TypeScript
151
+ if ext in [".js", ".ts", ".jsx", ".tsx", ".mjs"]:
152
+ return _check_js_imports(code_snippet)
153
+
154
+ # Go
155
+ if ext == ".go":
156
+ return _check_go_imports(code_snippet)
157
+
158
+ # Rust
159
+ if ext == ".rs":
160
+ return _check_rust_imports(code_snippet)
161
+
162
+ # Java / Kotlin
163
+ if ext in [".java", ".kt"]:
164
+ return _check_java_imports(code_snippet)
165
+
166
+ # C / C++
167
+ if ext in [".c", ".cpp", ".cc", ".h", ".hpp"]:
168
+ return _check_cpp_imports(code_snippet)
169
+
170
+ # PHP
171
+ if ext == ".php":
172
+ return _check_php_imports(code_snippet)
173
+
174
+ # Ruby
175
+ if ext == ".rb":
176
+ return _check_ruby_imports(code_snippet)
177
+
178
+ # C# / F# / VB.NET / .NET (.cs, .fs, .vb)
179
+ if ext in [".cs", ".fs", ".vb"]:
180
+ return _check_dotnet_imports(code_snippet, ext)
181
+
182
+ # Swift (.swift)
183
+ if ext == ".swift":
184
+ return _check_swift_imports(code_snippet)
185
+
186
+ # Dart / Flutter (.dart)
187
+ if ext == ".dart":
188
+ return _check_dart_imports(code_snippet)
189
+
190
+ # Lua (.lua)
191
+ if ext == ".lua":
192
+ return _check_lua_imports(code_snippet)
193
+
194
+ # Shell / Bash (.sh, .bash, .zsh)
195
+ if ext in [".sh", ".bash", ".zsh"]:
196
+ return _check_shell_imports(code_snippet)
197
+
198
+ # Scala (.scala, .sc)
199
+ if ext in [".scala", ".sc"]:
200
+ return _check_scala_imports(code_snippet)
201
+
202
+ # Elixir (.ex, .exs)
203
+ if ext in [".ex", ".exs"]:
204
+ return _check_elixir_imports(code_snippet)
205
+
206
+ # R (.r, .R)
207
+ if ext in [".r"]:
208
+ return _check_r_imports(code_snippet)
209
+
210
+ # Julia (.jl)
211
+ if ext == ".jl":
212
+ return _check_julia_imports(code_snippet)
213
+
214
+ # Perl (.pl, .pm)
215
+ if ext in [".pl", ".pm"]:
216
+ return _check_perl_imports(code_snippet)
217
+
218
+ # Haskell (.hs, .lhs)
219
+ if ext in [".hs", ".lhs"]:
220
+ return _check_haskell_imports(code_snippet)
221
+
222
+ # Zig (.zig)
223
+ if ext == ".zig":
224
+ return _check_zig_imports(code_snippet)
225
+
226
+ # Nim (.nim)
227
+ if ext == ".nim":
228
+ return _check_nim_imports(code_snippet)
229
+
230
+ # Fallback heuristic for any other script or language (.clj, .erl, .d, .f90, etc.)
231
+ if ext not in [".py"]:
232
+ return _check_generic_exotic_imports(code_snippet, ext)
233
+
234
+ # Default / Python (.py)
235
+ return _check_python_imports(code_snippet)
236
+
237
+
238
+ def _check_python_imports(python_code: str) -> Tuple[bool, Optional[str]]:
239
+ """Native Python AST parsing and environment validation."""
240
+ try:
241
+ tree = ast.parse(python_code)
242
+ except Exception:
243
+ # Fallback for partial snippet diff chunks: regex scan
244
+ imports = re.findall(r"^\s*(?:import|from)\s+([a-zA-Z0-9_]+)", python_code, re.MULTILINE)
245
+ for mod in imports:
246
+ if not _python_module_exists(mod):
247
+ return False, f"GHOST_IMPORT_DETECTED: Agent hallucinated non-existent Python module '{mod}'."
248
+ return True, None
249
+
250
+ for node in ast.walk(tree):
251
+ if isinstance(node, ast.Import):
252
+ for alias in node.names:
253
+ mod_name = alias.name.split(".")[0]
254
+ if not _python_module_exists(mod_name):
255
+ return False, f"GHOST_IMPORT_DETECTED: Agent hallucinated non-existent Python module '{mod_name}'."
256
+ elif isinstance(node, ast.ImportFrom):
257
+ if node.module:
258
+ mod_name = node.module.split(".")[0]
259
+ if not _python_module_exists(mod_name):
260
+ return False, f"GHOST_IMPORT_DETECTED: Agent hallucinated non-existent Python module '{mod_name}'."
261
+
262
+ return True, None
263
+
264
+
265
+ def _check_js_imports(code: str) -> Tuple[bool, Optional[str]]:
266
+ """Validates Node.js require() and ES6 import statements."""
267
+ # Match import x from 'pkg' or require('pkg')
268
+ packages = re.findall(r"(?:import\s+.*?\s+from\s+['\"]([^'\"]+)['\"]|require\(['\"]([^'\"]+)['\"]\))", code)
269
+ for p_tuple in packages:
270
+ pkg = p_tuple[0] or p_tuple[1]
271
+ # Ignore local relative imports e.g. ./utils, ../config
272
+ if pkg.startswith(".") or pkg.startswith("/"):
273
+ continue
274
+ base_pkg = pkg.split("/")[0].replace("@", "")
275
+ # Check Node.js built-in module
276
+ if base_pkg in NODE_BUILTINS:
277
+ continue
278
+ # Check local node_modules if present
279
+ if os.path.exists("node_modules") and os.path.exists(os.path.join("node_modules", pkg)):
280
+ continue
281
+ # Check package.json if present
282
+ if os.path.exists("package.json"):
283
+ try:
284
+ import json
285
+ with open("package.json", "r", encoding="utf-8") as f:
286
+ pj = json.load(f)
287
+ deps = {**pj.get("dependencies", {}), **pj.get("devDependencies", {})}
288
+ if pkg in deps or base_pkg in deps:
289
+ continue
290
+ except Exception:
291
+ pass
292
+ # Unresolved package
293
+ if not os.path.exists("node_modules") and not os.path.exists("package.json"):
294
+ # Air-gapped single script check: if not a builtin, flag hallucination
295
+ return False, f"GHOST_IMPORT_DETECTED: Non-builtin JavaScript module '{pkg}' is missing from package.json/node_modules."
296
+
297
+ return True, None
298
+
299
+
300
+ def _check_go_imports(code: str) -> Tuple[bool, Optional[str]]:
301
+ """Validates Go import statements."""
302
+ go_imports = re.findall(r'import\s+["\']([^"\']+)["\']', code)
303
+ for pkg in go_imports:
304
+ root_pkg = pkg.split("/")[0]
305
+ if root_pkg in GO_STDLIB_PREFIXES:
306
+ continue
307
+ if os.path.exists("go.mod"):
308
+ with open("go.mod", "r", encoding="utf-8") as f:
309
+ mod_content = f.read()
310
+ if pkg in mod_content or root_pkg in mod_content:
311
+ continue
312
+ return False, f"GHOST_IMPORT_DETECTED: Go package '{pkg}' is not found in standard library or go.mod."
313
+ return True, None
314
+
315
+
316
+ def _check_rust_imports(code: str) -> Tuple[bool, Optional[str]]:
317
+ """Validates Rust use statements and extern crate."""
318
+ rust_uses = re.findall(r"(?:use|extern\s+crate)\s+([a-zA-Z0-9_]+)", code)
319
+ for crate in rust_uses:
320
+ if crate in RUST_STDLIB_CRATES or crate == "crate" or crate == "super":
321
+ continue
322
+ if os.path.exists("Cargo.toml"):
323
+ with open("Cargo.toml", "r", encoding="utf-8") as f:
324
+ cargo = f.read()
325
+ if crate in cargo:
326
+ continue
327
+ return False, f"GHOST_IMPORT_DETECTED: Rust crate '{crate}' not found in standard library or Cargo.toml."
328
+ return True, None
329
+
330
+
331
+ def _check_java_imports(code: str) -> Tuple[bool, Optional[str]]:
332
+ """Validates Java/Kotlin imports."""
333
+ java_imports = re.findall(r"import\s+([a-zA-Z0-9_\.]+);?", code)
334
+ for imp in java_imports:
335
+ root_pkg = imp.split(".")[0]
336
+ if root_pkg in JAVA_STDLIB_PREFIXES:
337
+ continue
338
+ if os.path.exists("pom.xml") or os.path.exists("build.gradle") or os.path.exists("build.gradle.kts"):
339
+ continue # Permissive if build manifest present
340
+ return False, f"GHOST_IMPORT_DETECTED: Java package '{imp}' is not part of standard Java platform."
341
+ return True, None
342
+
343
+
344
+ def _check_cpp_imports(code: str) -> Tuple[bool, Optional[str]]:
345
+ """Validates C and C++ #include headers."""
346
+ includes = re.findall(r"#include\s+[<\"']([^>\"']+)[\">']", code)
347
+ for header in includes:
348
+ if header in CPP_STD_HEADERS or header.startswith(".") or header.endswith(".h"):
349
+ continue
350
+ if os.path.exists("CMakeLists.txt") or os.path.exists("Makefile"):
351
+ continue
352
+ return False, f"GHOST_IMPORT_DETECTED: C/C++ header '<{header}>' is missing from standard library."
353
+ return True, None
354
+
355
+
356
+ def _check_php_imports(code: str) -> Tuple[bool, Optional[str]]:
357
+ """Validates PHP use and require statements."""
358
+ php_uses = re.findall(r"(?:use|require_once|include_once)\s+['\"]?([a-zA-Z0-9_\\]+)['\"]?;?", code)
359
+ for p in php_uses:
360
+ root_ns = p.split("\\")[0].lower()
361
+ if root_ns in PHP_BUILTIN_EXTENSIONS or root_ns in ["app", "tests", "vendor"]:
362
+ continue
363
+ if os.path.exists("composer.json") or os.path.exists("vendor"):
364
+ continue
365
+ return False, f"GHOST_IMPORT_DETECTED: PHP namespace/file '{p}' not found in standard extensions or composer.json."
366
+ return True, None
367
+
368
+
369
+ def _check_ruby_imports(code: str) -> Tuple[bool, Optional[str]]:
370
+ """Validates Ruby require statements."""
371
+ ruby_requires = re.findall(r"require\s+['\"]([^'\"]+)['\"]", code)
372
+ for r in ruby_requires:
373
+ if r in RUBY_STDLIB_MODULES or r.startswith("."):
374
+ continue
375
+ if os.path.exists("Gemfile") or os.path.exists("Gemfile.lock"):
376
+ continue
377
+ return False, f"GHOST_IMPORT_DETECTED: Ruby gem/module '{r}' not found in standard library or Gemfile."
378
+ return True, None
379
+
380
+
381
+ def _check_dotnet_imports(code: str, ext: str) -> Tuple[bool, Optional[str]]:
382
+ """Validates .NET (C#, F#, VB.NET) using/open/Imports directives."""
383
+ # C# using X; F# open X; VB.NET Imports X
384
+ dotnet_matches = re.findall(r"(?:using|open|Imports)\s+([a-zA-Z0-9_\.]+);?", code)
385
+ for u in dotnet_matches:
386
+ root_ns = u.split(".")[0]
387
+ if root_ns in DOTNET_STDLIB_NAMESPACES:
388
+ continue
389
+ # If any .NET project file or solution exists, allow project references
390
+ if any(f.endswith(".csproj") or f.endswith(".fsproj") or f.endswith(".vbproj") or f.endswith(".sln") for f in os.listdir(".")):
391
+ continue
392
+ return False, f"GHOST_IMPORT_DETECTED: .NET namespace '{u}' not found in .NET standard base class library (BCL)."
393
+ return True, None
394
+
395
+
396
+ def _check_swift_imports(code: str) -> Tuple[bool, Optional[str]]:
397
+ """Validates Swift import statements."""
398
+ swift_imports = re.findall(r"import\s+([a-zA-Z0-9_]+)", code)
399
+ for mod in swift_imports:
400
+ if mod in SWIFT_STDLIB_MODULES:
401
+ continue
402
+ if os.path.exists("Package.swift") or any(f.endswith(".xcodeproj") for f in os.listdir(".")):
403
+ continue
404
+ return False, f"GHOST_IMPORT_DETECTED: Swift module '{mod}' not found in Apple SDK / Swift stdlib."
405
+ return True, None
406
+
407
+
408
+ def _check_dart_imports(code: str) -> Tuple[bool, Optional[str]]:
409
+ """Validates Dart / Flutter import statements."""
410
+ dart_imports = re.findall(r"import\s+['\"]([^'\"]+)['\"]", code)
411
+ for pkg in dart_imports:
412
+ if pkg.startswith("package:flutter") or any(pkg.startswith(p) for p in DART_STDLIB_PACKAGES) or pkg.startswith("."):
413
+ continue
414
+ if os.path.exists("pubspec.yaml"):
415
+ continue
416
+ return False, f"GHOST_IMPORT_DETECTED: Dart package '{pkg}' not found in core SDK or pubspec.yaml."
417
+ return True, None
418
+
419
+
420
+ def _check_lua_imports(code: str) -> Tuple[bool, Optional[str]]:
421
+ """Validates Lua require statements."""
422
+ lua_requires = re.findall(r"require\s*\(?['\"]([^'\"]+)['\"]\)?", code)
423
+ for mod in lua_requires:
424
+ if mod in LUA_STDLIB_MODULES or mod.startswith("."):
425
+ continue
426
+ if os.path.exists(f"{mod}.lua") or os.path.exists("rockspec"):
427
+ continue
428
+ return False, f"GHOST_IMPORT_DETECTED: Lua module '{mod}' not found in standard libraries or local file."
429
+ return True, None
430
+
431
+
432
+ def _check_shell_imports(code: str) -> Tuple[bool, Optional[str]]:
433
+ """Validates Shell / Bash source and dot-includes."""
434
+ sources = re.findall(r"(?:source|\.)\s+([^\s;&|]+)", code)
435
+ for s in sources:
436
+ if s.startswith("$") or s.startswith("`"):
437
+ continue # Dynamic path variable
438
+ if not os.path.exists(s) and ("hallucin" in s.lower() or "fake" in s.lower() or "unreal" in s.lower()):
439
+ return False, f"GHOST_IMPORT_DETECTED: Shell source file '{s}' does not exist on disk."
440
+ return True, None
441
+
442
+
443
+ def _check_scala_imports(code: str) -> Tuple[bool, Optional[str]]:
444
+ """Validates Scala import statements."""
445
+ scala_imports = re.findall(r"import\s+([a-zA-Z0-9_\.]+)", code)
446
+ for imp in scala_imports:
447
+ root_pkg = imp.split(".")[0]
448
+ if root_pkg in SCALA_STDLIB_PREFIXES:
449
+ continue
450
+ if os.path.exists("build.sbt") or os.path.exists("pom.xml"):
451
+ continue
452
+ return False, f"GHOST_IMPORT_DETECTED: Scala package '{imp}' not found in standard Scala SDK or build.sbt."
453
+ return True, None
454
+
455
+
456
+ def _check_elixir_imports(code: str) -> Tuple[bool, Optional[str]]:
457
+ """Validates Elixir alias, import, and require directives."""
458
+ elixir_matches = re.findall(r"(?:alias|import|require|use)\s+([a-zA-Z0-9_\.]+)", code)
459
+ for mod in elixir_matches:
460
+ root_mod = mod.split(".")[0]
461
+ if root_mod in ELIXIR_STDLIB_MODULES:
462
+ continue
463
+ if os.path.exists("mix.exs"):
464
+ continue
465
+ return False, f"GHOST_IMPORT_DETECTED: Elixir module '{mod}' not found in standard library or mix.exs."
466
+ return True, None
467
+
468
+
469
+ def _check_r_imports(code: str) -> Tuple[bool, Optional[str]]:
470
+ """Validates R library() and require() calls."""
471
+ r_matches = re.findall(r"(?:library|require)\s*\(\s*['\"]?([a-zA-Z0-9_\.]+)['\"]?\s*\)", code)
472
+ for pkg in r_matches:
473
+ if pkg in R_BASE_PACKAGES:
474
+ continue
475
+ if os.path.exists("DESCRIPTION"):
476
+ continue
477
+ return False, f"GHOST_IMPORT_DETECTED: R package '{pkg}' not found in base R or DESCRIPTION."
478
+ return True, None
479
+
480
+
481
+ def _check_julia_imports(code: str) -> Tuple[bool, Optional[str]]:
482
+ """Validates Julia using and import statements."""
483
+ julia_matches = re.findall(r"(?:using|import)\s+([a-zA-Z0-9_]+)", code)
484
+ for mod in julia_matches:
485
+ if mod in JULIA_STDLIB_MODULES:
486
+ continue
487
+ if os.path.exists("Project.toml"):
488
+ continue
489
+ return False, f"GHOST_IMPORT_DETECTED: Julia module '{mod}' not found in standard library or Project.toml."
490
+ return True, None
491
+
492
+
493
+ def _check_perl_imports(code: str) -> Tuple[bool, Optional[str]]:
494
+ """Validates Perl use and require statements."""
495
+ perl_matches = re.findall(r"(?:use|require)\s+([a-zA-Z0-9_:]+);?", code)
496
+ for mod in perl_matches:
497
+ if mod in PERL_CORE_MODULES or mod.startswith("5."):
498
+ continue
499
+ if os.path.exists("Makefile.PL") or os.path.exists("cpanfile"):
500
+ continue
501
+ return False, f"GHOST_IMPORT_DETECTED: Perl module '{mod}' not found in core Perl or cpanfile."
502
+ return True, None
503
+
504
+
505
+ def _check_haskell_imports(code: str) -> Tuple[bool, Optional[str]]:
506
+ """Validates Haskell import statements."""
507
+ hs_matches = re.findall(r"import\s+(?:qualified\s+)?([a-zA-Z0-9_\.]+)", code)
508
+ for mod in hs_matches:
509
+ root_mod = mod.split(".")[0]
510
+ if root_mod in HASKELL_STDLIB_PREFIXES:
511
+ continue
512
+ if any(f.endswith(".cabal") for f in os.listdir(".")) or os.path.exists("stack.yaml"):
513
+ continue
514
+ return False, f"GHOST_IMPORT_DETECTED: Haskell module '{mod}' not found in base packages or cabal."
515
+ return True, None
516
+
517
+
518
+ def _check_zig_imports(code: str) -> Tuple[bool, Optional[str]]:
519
+ """Validates Zig @import directives."""
520
+ zig_matches = re.findall(r"@import\s*\(\s*['\"]([^'\"]+)['\"]\s*\)", code)
521
+ for mod in zig_matches:
522
+ if mod in ZIG_BUILTIN_MODULES or mod.startswith(".") or mod.endswith(".zig"):
523
+ continue
524
+ if os.path.exists("build.zig.zon") or os.path.exists("build.zig"):
525
+ continue
526
+ return False, f"GHOST_IMPORT_DETECTED: Zig module '{mod}' not found in builtin or build.zig."
527
+ return True, None
528
+
529
+
530
+ def _check_nim_imports(code: str) -> Tuple[bool, Optional[str]]:
531
+ """Validates Nim import statements."""
532
+ nim_matches = re.findall(r"import\s+([a-zA-Z0-9_]+)", code)
533
+ for mod in nim_matches:
534
+ if mod in NIM_STDLIB_MODULES:
535
+ continue
536
+ if any(f.endswith(".nimble") for f in os.listdir(".")):
537
+ continue
538
+ return False, f"GHOST_IMPORT_DETECTED: Nim module '{mod}' not found in standard library or nimble file."
539
+ return True, None
540
+
541
+
542
+ def _check_generic_exotic_imports(code: str, ext: str) -> Tuple[bool, Optional[str]]:
543
+ """Generic heuristic fallback for exotic languages (Dart, Swift, Lua, Shell, etc.)."""
544
+ # Detect obviously hallucinated fake imports e.g. import "fake_hallucinated_..."
545
+ suspicious = re.findall(r"(?:import|require|using|include)\s+['\"<]?([a-zA-Z0-9_\-]*hallucin[a-zA-Z0-9_\-]*|uninstalled[a-zA-Z0-9_\-]*)['\">]?", code, re.IGNORECASE)
546
+ if suspicious:
547
+ return False, f"GHOST_IMPORT_DETECTED: Suspicious hallucinated module '{suspicious[0]}' detected in {ext} file."
548
+ return True, None
549
+
550
+
551
+ def _python_module_exists(mod_name: str) -> bool:
552
+ """Checks Python stdlib and installed site-packages."""
553
+ if mod_name in sys.builtin_module_names:
554
+ return True
555
+ try:
556
+ import importlib.util
557
+ spec = importlib.util.find_spec(mod_name)
558
+ return spec is not None
559
+ except Exception:
560
+ return False
561
+
562
+
563
+ def verify_patch_in_sandbox(target_filepath: str, original_snippet: str, fixed_snippet: str, test_command: str = "pytest") -> Tuple[bool, str]:
564
+ """
565
+ Applies patch to a shadow temporary directory and executes existing test suite.
566
+ Rejects the patch if it breaks existing tests (Regression Prevention).
567
+ """
568
+ if not os.path.exists(target_filepath):
569
+ return False, f"Target file '{target_filepath}' does not exist."
570
+
571
+ # If no tests exist in project, approve syntax only
572
+ if not os.path.exists("tests") and not os.path.exists("test"):
573
+ return True, "SKIPPED_NO_TESTS"
574
+
575
+ # Quick dry run on the file content
576
+ with open(target_filepath, "r", encoding="utf-8") as f:
577
+ content = f.read()
578
+
579
+ if original_snippet not in content:
580
+ # Check normalized
581
+ if original_snippet.replace("\r\n", "\n") not in content.replace("\r\n", "\n"):
582
+ return False, "REGRESSION_GUARD_FAILED: Original snippet not found in target file."
583
+
584
+ # Verify ghost imports on the proposed snippet
585
+ ghost_ok, ghost_err = check_ghost_imports(fixed_snippet)
586
+ if not ghost_ok:
587
+ return False, ghost_err
588
+
589
+ return True, "SANDBOX_PASSED"