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.
- git_oops-1.0.0.dist-info/METADATA +198 -0
- git_oops-1.0.0.dist-info/RECORD +32 -0
- git_oops-1.0.0.dist-info/WHEEL +5 -0
- git_oops-1.0.0.dist-info/entry_points.txt +2 -0
- git_oops-1.0.0.dist-info/licenses/LICENSE +21 -0
- git_oops-1.0.0.dist-info/top_level.txt +1 -0
- src/__init__.py +16 -0
- src/blackbox.py +119 -0
- src/circuit_breaker.py +129 -0
- src/cli.py +556 -0
- src/consensus.py +58 -0
- src/engine.py +222 -0
- src/firewall.py +48 -0
- src/guardian.py +589 -0
- src/hardware.py +137 -0
- src/hooks.py +525 -0
- src/hud.py +493 -0
- src/local_llm.py +116 -0
- src/mcp_server.py +124 -0
- src/replay.py +100 -0
- src/reviewer.py +157 -0
- src/rules.py +291 -0
- src/salvage.py +92 -0
- src/sentinel.py +79 -0
- src/shadow.py +70 -0
- src/solver.py +158 -0
- src/squeezer.py +64 -0
- src/supply_chain.py +142 -0
- src/tester.py +54 -0
- src/ui.py +124 -0
- src/updater.py +49 -0
- src/watchdog.py +96 -0
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"
|