tzero-mcp 10.7.0 → 10.7.1
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.
- package/bin/tzero-mcp.js +9 -16
- package/bin/tzero.js +9 -16
- package/index.js +7 -2
- package/package.json +2 -1
- package/src/addmcp.py +1194 -0
- package/src/main.py +60 -0
- package/src/siber_akademi_icon.py +77 -0
- package/src/tzero/jarvis/semantic_compressor.py +373 -0
- package/src/tzero.py +58 -0
- package/src/tzero_benchmark.py +475 -0
- package/src/tzero_bridge.py +362 -0
- package/src/tzero_ci.py +153 -0
- package/src/tzero_commit.py +192 -0
- package/src/tzero_cost.py +163 -0
- package/src/tzero_dashboard.py +1448 -0
- package/src/tzero_deps.py +287 -0
- package/src/tzero_drift.py +208 -0
- package/src/tzero_explain.py +240 -0
- package/src/tzero_features.py +3022 -0
- package/src/tzero_fix.py +228 -0
- package/src/tzero_fixer/__init__.py +63 -0
- package/src/tzero_fixer/amalgamation.py +334 -0
- package/src/tzero_fixer/ast_syntax_healer.py +464 -0
- package/src/tzero_fixer/ast_type_modernizer.py +288 -0
- package/src/tzero_fixer/async_io_modernizer.py +127 -0
- package/src/tzero_fixer/automated_regression_suite.py +600 -0
- package/src/tzero_fixer/benchmarks_and_diagnostics.py +174 -0
- package/src/tzero_fixer/ci_cd_self_healing_gate.py +246 -0
- package/src/tzero_fixer/code_duplicity_and_refactorer.py +151 -0
- package/src/tzero_fixer/code_smells_healer.py +260 -0
- package/src/tzero_fixer/cwe_remediator.py +171 -0
- package/src/tzero_fixer/dead_code_eliminator.py +150 -0
- package/src/tzero_fixer/dependency_vulnerability_auditor.py +309 -0
- package/src/tzero_fixer/git_advanced_surgeon.py +280 -0
- package/src/tzero_fixer/hardware_and_disk_doctor.py +325 -0
- package/src/tzero_fixer/import_graph_healer.py +412 -0
- package/src/tzero_fixer/interactive_tui.py +91 -0
- package/src/tzero_fixer/knowledge_base.py +295 -0
- package/src/tzero_fixer/knowledge_base_extended.py +940 -0
- package/src/tzero_fixer/linters_and_static_analyzer.py +307 -0
- package/src/tzero_fixer/mcp_server.py +447 -0
- package/src/tzero_fixer/models.py +385 -0
- package/src/tzero_fixer/orchestrator.py +375 -0
- package/src/tzero_fixer/os_doctor.py +502 -0
- package/src/tzero_fixer/packaging_abi_surgeon.py +323 -0
- package/src/tzero_fixer/pc_fixer_doctor.py +599 -0
- package/src/tzero_fixer/performance_and_memory_profiler.py +274 -0
- package/src/tzero_fixer/polyglot_ast_parsers.py +313 -0
- package/src/tzero_fixer/polyglot_build_healer.py +364 -0
- package/src/tzero_fixer/polyglot_config_healer.py +318 -0
- package/src/tzero_fixer/polyglot_doctor.py +372 -0
- package/src/tzero_fixer/polyglot_linters.py +619 -0
- package/src/tzero_fixer/polyglot_schema_validator.py +305 -0
- package/src/tzero_fixer/security_cwe_database.py +444 -0
- package/src/tzero_fixer/security_deep_rules.py +114 -0
- package/src/tzero_fixer/security_rules_catalog.py +110 -0
- package/src/tzero_fixer/security_zero_leak.py +326 -0
- package/src/tzero_fixer/smart_patch_recommender.py +223 -0
- package/src/tzero_fixer/synthetic_workspace_generator.py +220 -0
- package/src/tzero_fixer/system_diagnostics_extended.py +493 -0
- package/src/tzero_fixer/system_repair_recipes.py +447 -0
- package/src/tzero_fixer/venv_doctor.py +309 -0
- package/src/tzero_gateway.py +885 -0
- package/src/tzero_guard.py +243 -0
- package/src/tzero_infra.py +274 -0
- package/src/tzero_jarvis/__init__.py +124 -0
- package/src/tzero_jarvis/causal_graph.py +310 -0
- package/src/tzero_jarvis/graph_visualizer.py +395 -0
- package/src/tzero_jarvis/hybrid_search.py +214 -0
- package/src/tzero_jarvis/latent_press.py +137 -0
- package/src/tzero_jarvis/memory_consolidation.py +212 -0
- package/src/tzero_jarvis/minhash_lsh.py +184 -0
- package/src/tzero_jarvis/orchestrator.py +335 -0
- package/src/tzero_jarvis/self_healing.py +308 -0
- package/src/tzero_jarvis/semantic_compressor.py +887 -0
- package/src/tzero_jarvis/swarm.py +261 -0
- package/src/tzero_jarvis/swarm_bft.py +144 -0
- package/src/tzero_jarvis/workspace_orchestrator.py +1167 -0
- package/src/tzero_jarvis/zero_key_router.py +173 -0
- package/src/tzero_mcp.py +4858 -0
- package/src/tzero_neural_cortex.py +1425 -0
- package/src/tzero_packer.py +370 -0
- package/src/tzero_patch.py +624 -0
- package/src/tzero_pc_fixer.py +12657 -0
- package/src/tzero_pipe.py +95 -0
- package/src/tzero_polyglot.py +422 -0
- package/src/tzero_reachability.py +272 -0
- package/src/tzero_replay.py +141 -0
- package/src/tzero_security.py +570 -0
- package/src/tzero_snapshot.py +258 -0
- package/src/tzero_stubs.py +313 -0
- package/src/tzero_superdesign.py +774 -0
- package/src/tzero_sync.py +241 -0
- package/src/tzero_task_benchmark.py +363 -0
- package/src/tzero_tui.py +244 -0
- package/src/tzero_v3.py +9435 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""T-Zero Import Graph & Dependency Cycle Healer.
|
|
3
|
+
|
|
4
|
+
Performs deep static graph analysis on module imports:
|
|
5
|
+
- Tarjan's Strongly Connected Components (SCC) algorithm for circular import cycles.
|
|
6
|
+
- Missing Standard Library and Typing symbol auto-injection (80+ symbols).
|
|
7
|
+
- Dead / unused import detection and pruning with zero blast radius.
|
|
8
|
+
- Wildcard import expansion (from module import * -> explicit symbols).
|
|
9
|
+
- Standard library module shadowing detection (e.g. workspace email.py or random.py).
|
|
10
|
+
|
|
11
|
+
Author: Toprak Ahmet Aydogmus (Siber Akademi)
|
|
12
|
+
Version: 10.4.0
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import ast
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import time
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
21
|
+
|
|
22
|
+
from .models import (
|
|
23
|
+
DiagnosticCategory,
|
|
24
|
+
DiagnosticIssue,
|
|
25
|
+
HealAction,
|
|
26
|
+
SeverityLevel,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ImportGraphHealer:
|
|
31
|
+
"""Graph-theoretic dependency analyzer and surgical import injector."""
|
|
32
|
+
|
|
33
|
+
COMMON_STDLIB_SYMBOLS: Dict[str, str] = {
|
|
34
|
+
# Core OS & System
|
|
35
|
+
"os": "import os",
|
|
36
|
+
"sys": "import sys",
|
|
37
|
+
"shutil": "import shutil",
|
|
38
|
+
"subprocess": "import subprocess",
|
|
39
|
+
"tempfile": "import tempfile",
|
|
40
|
+
"platform": "import platform",
|
|
41
|
+
"time": "import time",
|
|
42
|
+
"datetime": "import datetime",
|
|
43
|
+
"Path": "from pathlib import Path",
|
|
44
|
+
"pathlib": "import pathlib",
|
|
45
|
+
# Data & Parsing
|
|
46
|
+
"json": "import json",
|
|
47
|
+
"re": "import re",
|
|
48
|
+
"math": "import math",
|
|
49
|
+
"ast": "import ast",
|
|
50
|
+
"difflib": "import difflib",
|
|
51
|
+
"hashlib": "import hashlib",
|
|
52
|
+
"base64": "import base64",
|
|
53
|
+
"uuid": "import uuid",
|
|
54
|
+
# Data Structures & Functional
|
|
55
|
+
"dataclass": "from dataclasses import dataclass",
|
|
56
|
+
"field": "from dataclasses import field",
|
|
57
|
+
"asdict": "from dataclasses import asdict",
|
|
58
|
+
"defaultdict": "from collections import defaultdict",
|
|
59
|
+
"Counter": "from collections import Counter",
|
|
60
|
+
"deque": "from collections import deque",
|
|
61
|
+
"namedtuple": "from collections import namedtuple",
|
|
62
|
+
"partial": "from functools import partial",
|
|
63
|
+
"lru_cache": "from functools import lru_cache",
|
|
64
|
+
"chain": "from itertools import chain",
|
|
65
|
+
# Typing Protocols
|
|
66
|
+
"Any": "from typing import Any",
|
|
67
|
+
"Dict": "from typing import Dict",
|
|
68
|
+
"List": "from typing import List",
|
|
69
|
+
"Optional": "from typing import Optional",
|
|
70
|
+
"Union": "from typing import Union",
|
|
71
|
+
"Tuple": "from typing import Tuple",
|
|
72
|
+
"Set": "from typing import Set",
|
|
73
|
+
"Callable": "from typing import Callable",
|
|
74
|
+
"Iterable": "from typing import Iterable",
|
|
75
|
+
"Iterator": "from typing import Iterator",
|
|
76
|
+
"Generator": "from typing import Generator",
|
|
77
|
+
"TypeVar": "from typing import TypeVar",
|
|
78
|
+
"Generic": "from typing import Generic",
|
|
79
|
+
"Sequence": "from typing import Sequence",
|
|
80
|
+
"Mapping": "from typing import Mapping",
|
|
81
|
+
"Literal": "from typing import Literal",
|
|
82
|
+
"Final": "from typing import Final",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
STDLIB_MODULE_NAMES = set(sys.builtin_module_names).union({
|
|
86
|
+
"abc", "argparse", "array", "ast", "asyncio", "base64", "binascii",
|
|
87
|
+
"bisect", "bz2", "calendar", "cmath", "cmd", "code", "codecs",
|
|
88
|
+
"collections", "colorsys", "compileall", "concurrent", "configparser",
|
|
89
|
+
"contextlib", "contextvars", "copy", "copyreg", "cProfile", "csv",
|
|
90
|
+
"ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal",
|
|
91
|
+
"difflib", "dis", "distutils", "doctest", "email", "encodings",
|
|
92
|
+
"enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput",
|
|
93
|
+
"fnmatch", "fractions", "ftplib", "functools", "gc", "getopt",
|
|
94
|
+
"getpass", "gettext", "glob", "graphlib", "gzip", "hashlib", "heapq",
|
|
95
|
+
"hmac", "html", "http", "idlelib", "imaplib", "imghdr", "importlib",
|
|
96
|
+
"inspect", "io", "ipaddress", "itertools", "json", "keyword", "linecache",
|
|
97
|
+
"locale", "logging", "lzma", "mailbox", "mailcap", "marshal", "math",
|
|
98
|
+
"mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing",
|
|
99
|
+
"netrc", "nntplib", "numbers", "operator", "optparse", "os", "ossaudiodev",
|
|
100
|
+
"pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform",
|
|
101
|
+
"plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats",
|
|
102
|
+
"pty", "pwd", "py_compile", "pyclbr", "pydoc", "queue", "quopri", "random",
|
|
103
|
+
"re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched",
|
|
104
|
+
"secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal",
|
|
105
|
+
"site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd",
|
|
106
|
+
"sqlite3", "ssl", "stat", "statistics", "string", "stringprep", "struct",
|
|
107
|
+
"subprocess", "sunau", "symtable", "sys", "sysconfig", "syslog", "tabnanny",
|
|
108
|
+
"tarfile", "telnetlib", "tempfile", "termios", "test", "textwrap", "threading",
|
|
109
|
+
"time", "timeit", "tkinter", "token", "tokenize", "tomllib", "trace",
|
|
110
|
+
"traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types",
|
|
111
|
+
"typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv",
|
|
112
|
+
"warnings", "wave", "weakref", "webbrowser", "winreg", "winsound",
|
|
113
|
+
"wsgiref", "xdrlib", "xml", "xmlrpc", "zipapp", "zipfile", "zipimport", "zlib"
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
DANGEROUS_SHADOW_NAMES = {
|
|
117
|
+
"email", "random", "test", "queue", "io", "types", "token", "string",
|
|
118
|
+
"numbers", "time", "json", "ast", "math", "copy", "socket", "logging"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
IGNORED_DIRS = {
|
|
122
|
+
".git", ".svn", ".hg", "__pycache__", ".pytest_cache", ".mypy_cache",
|
|
123
|
+
"node_modules", ".venv", "venv", "env", "build", "dist", ".idea", ".vscode"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def check_stdlib_shadowing(cls, workspace_dir: str = ".") -> List[DiagnosticIssue]:
|
|
128
|
+
"""Detect workspace files that dangerously shadow Python standard library modules."""
|
|
129
|
+
issues: List[DiagnosticIssue] = []
|
|
130
|
+
root = Path(workspace_dir).resolve()
|
|
131
|
+
|
|
132
|
+
for f in root.iterdir():
|
|
133
|
+
if f.is_file() and f.suffix == ".py":
|
|
134
|
+
stem = f.stem.lower()
|
|
135
|
+
if stem in cls.DANGEROUS_SHADOW_NAMES:
|
|
136
|
+
issues.append(DiagnosticIssue(
|
|
137
|
+
category=DiagnosticCategory.IMPORTS.value,
|
|
138
|
+
severity=SeverityLevel.CRITICAL.value,
|
|
139
|
+
file_path=str(f),
|
|
140
|
+
line=1,
|
|
141
|
+
column=0,
|
|
142
|
+
error_code="TZ-IMP-SHADOW-STDLIB",
|
|
143
|
+
message=f"Root workspace module '{f.name}' shadows Python standard library module '{stem}'",
|
|
144
|
+
suggested_fix=f"Rename '{f.name}' to '{stem}_service.py' or similar to avoid breaking imports",
|
|
145
|
+
auto_fixable=False,
|
|
146
|
+
metadata={"stem": stem, "path": str(f)}
|
|
147
|
+
))
|
|
148
|
+
|
|
149
|
+
return issues
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def scan_file_imports_and_references(cls, file_path: str, tree: ast.AST) -> Tuple[Set[str], Set[str], List[ast.AST]]:
|
|
153
|
+
"""Extract imported symbols, referenced names, and import AST nodes."""
|
|
154
|
+
imported_symbols: Set[str] = set()
|
|
155
|
+
import_nodes: List[ast.AST] = []
|
|
156
|
+
|
|
157
|
+
for node in ast.walk(tree):
|
|
158
|
+
if isinstance(node, ast.Import):
|
|
159
|
+
import_nodes.append(node)
|
|
160
|
+
for alias in node.names:
|
|
161
|
+
imported_symbols.add(alias.asname or alias.name.split(".")[0])
|
|
162
|
+
elif isinstance(node, ast.ImportFrom):
|
|
163
|
+
import_nodes.append(node)
|
|
164
|
+
for alias in node.names:
|
|
165
|
+
imported_symbols.add(alias.asname or alias.name)
|
|
166
|
+
|
|
167
|
+
referenced_names: Set[str] = set()
|
|
168
|
+
for node in ast.walk(tree):
|
|
169
|
+
if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
|
|
170
|
+
referenced_names.add(node.id)
|
|
171
|
+
|
|
172
|
+
return imported_symbols, referenced_names, import_nodes
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def detect_missing_stdlib_imports(cls, file_path: str, content: str, tree: ast.AST) -> List[DiagnosticIssue]:
|
|
176
|
+
"""Detect standard library symbols used in code without being imported."""
|
|
177
|
+
issues: List[DiagnosticIssue] = []
|
|
178
|
+
imported_symbols, referenced_names, _ = cls.scan_file_imports_and_references(file_path, tree)
|
|
179
|
+
|
|
180
|
+
builtins_set = set(dir(__builtins__))
|
|
181
|
+
|
|
182
|
+
for sym, import_stmt in cls.COMMON_STDLIB_SYMBOLS.items():
|
|
183
|
+
if sym in referenced_names and sym not in imported_symbols and sym not in builtins_set:
|
|
184
|
+
issues.append(DiagnosticIssue(
|
|
185
|
+
category=DiagnosticCategory.IMPORTS.value,
|
|
186
|
+
severity=SeverityLevel.WARNING.value,
|
|
187
|
+
file_path=file_path,
|
|
188
|
+
line=1,
|
|
189
|
+
column=0,
|
|
190
|
+
error_code="TZ-IMP-MISSING-STDLIB",
|
|
191
|
+
message=f"Referenced symbol '{sym}' is missing an import statement",
|
|
192
|
+
suggested_fix=f"Add '{import_stmt}' at top of file",
|
|
193
|
+
auto_fixable=True,
|
|
194
|
+
metadata={"action": "inject_import", "symbol": sym, "stmt": import_stmt, "path": file_path}
|
|
195
|
+
))
|
|
196
|
+
|
|
197
|
+
return issues
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def detect_wildcard_imports(cls, file_path: str, tree: ast.AST) -> List[DiagnosticIssue]:
|
|
201
|
+
"""Detect wildcard 'from module import *' anti-patterns."""
|
|
202
|
+
issues: List[DiagnosticIssue] = []
|
|
203
|
+
for node in ast.walk(tree):
|
|
204
|
+
if isinstance(node, ast.ImportFrom):
|
|
205
|
+
for alias in node.names:
|
|
206
|
+
if alias.name == "*":
|
|
207
|
+
issues.append(DiagnosticIssue(
|
|
208
|
+
category=DiagnosticCategory.IMPORTS.value,
|
|
209
|
+
severity=SeverityLevel.WARNING.value,
|
|
210
|
+
file_path=file_path,
|
|
211
|
+
line=node.lineno,
|
|
212
|
+
column=node.col_offset,
|
|
213
|
+
error_code="TZ-IMP-WILDCARD",
|
|
214
|
+
message=f"Wildcard import 'from {node.module or '.'} import *' pollutes namespace",
|
|
215
|
+
suggested_fix="Replace '*' with explicit symbol imports",
|
|
216
|
+
auto_fixable=False,
|
|
217
|
+
metadata={"module": node.module, "lineno": node.lineno}
|
|
218
|
+
))
|
|
219
|
+
return issues
|
|
220
|
+
|
|
221
|
+
@classmethod
|
|
222
|
+
def build_workspace_import_graph(cls, workspace_dir: str = ".") -> Tuple[Dict[str, Set[str]], Dict[str, str]]:
|
|
223
|
+
"""Construct directed import graph (module_name -> set of imported module_names)."""
|
|
224
|
+
root = Path(workspace_dir).resolve()
|
|
225
|
+
graph: Dict[str, Set[str]] = {}
|
|
226
|
+
path_to_mod: Dict[str, str] = {}
|
|
227
|
+
|
|
228
|
+
# 1. Map all local python files to module names
|
|
229
|
+
for root_dir, dirs, files in os.walk(str(root)):
|
|
230
|
+
dirs[:] = [d for d in dirs if d not in cls.IGNORED_DIRS]
|
|
231
|
+
for file in files:
|
|
232
|
+
if file.endswith(".py"):
|
|
233
|
+
full_p = os.path.join(root_dir, file)
|
|
234
|
+
rel_p = os.path.relpath(full_p, str(root))
|
|
235
|
+
mod_name = os.path.splitext(rel_p)[0].replace(os.sep, ".")
|
|
236
|
+
if mod_name.endswith(".__init__"):
|
|
237
|
+
mod_name = mod_name[:-9]
|
|
238
|
+
path_to_mod[full_p] = mod_name
|
|
239
|
+
graph[mod_name] = set()
|
|
240
|
+
|
|
241
|
+
# 2. Extract outgoing dependencies
|
|
242
|
+
for full_p, mod_name in path_to_mod.items():
|
|
243
|
+
try:
|
|
244
|
+
with open(full_p, "r", encoding="utf-8", errors="replace") as f:
|
|
245
|
+
content = f.read()
|
|
246
|
+
tree = ast.parse(content, filename=full_p)
|
|
247
|
+
for node in ast.walk(tree):
|
|
248
|
+
if isinstance(node, ast.Import):
|
|
249
|
+
for alias in node.names:
|
|
250
|
+
base = alias.name.split(".")[0]
|
|
251
|
+
if base in graph:
|
|
252
|
+
graph[mod_name].add(base)
|
|
253
|
+
elif isinstance(node, ast.ImportFrom):
|
|
254
|
+
if node.module:
|
|
255
|
+
base = node.module.split(".")[0]
|
|
256
|
+
if base in graph:
|
|
257
|
+
graph[mod_name].add(base)
|
|
258
|
+
except Exception:
|
|
259
|
+
continue
|
|
260
|
+
|
|
261
|
+
return graph, path_to_mod
|
|
262
|
+
|
|
263
|
+
@classmethod
|
|
264
|
+
def find_circular_import_cycles(cls, graph: Dict[str, Set[str]]) -> List[List[str]]:
|
|
265
|
+
"""Find all circular import cycles using Tarjan's Strongly Connected Components algorithm."""
|
|
266
|
+
index = 0
|
|
267
|
+
indices: Dict[str, int] = {}
|
|
268
|
+
lowlinks: Dict[str, int] = {}
|
|
269
|
+
on_stack: Set[str] = set()
|
|
270
|
+
stack: List[str] = []
|
|
271
|
+
sccs: List[List[str]] = []
|
|
272
|
+
|
|
273
|
+
def strongconnect(node: str):
|
|
274
|
+
nonlocal index
|
|
275
|
+
indices[node] = index
|
|
276
|
+
lowlinks[node] = index
|
|
277
|
+
index += 1
|
|
278
|
+
stack.append(node)
|
|
279
|
+
on_stack.add(node)
|
|
280
|
+
|
|
281
|
+
for neighbor in graph.get(node, set()):
|
|
282
|
+
if neighbor not in indices:
|
|
283
|
+
strongconnect(neighbor)
|
|
284
|
+
lowlinks[node] = min(lowlinks[node], lowlinks[neighbor])
|
|
285
|
+
elif neighbor in on_stack:
|
|
286
|
+
lowlinks[node] = min(lowlinks[node], indices[neighbor])
|
|
287
|
+
|
|
288
|
+
if lowlinks[node] == indices[node]:
|
|
289
|
+
scc = []
|
|
290
|
+
while True:
|
|
291
|
+
w = stack.pop()
|
|
292
|
+
on_stack.remove(w)
|
|
293
|
+
scc.append(w)
|
|
294
|
+
if w == node:
|
|
295
|
+
break
|
|
296
|
+
if len(scc) > 1:
|
|
297
|
+
sccs.append(scc)
|
|
298
|
+
|
|
299
|
+
for node in graph:
|
|
300
|
+
if node not in indices:
|
|
301
|
+
strongconnect(node)
|
|
302
|
+
|
|
303
|
+
return sccs
|
|
304
|
+
|
|
305
|
+
@classmethod
|
|
306
|
+
def diagnose_workspace(cls, workspace_dir: str = ".") -> List[DiagnosticIssue]:
|
|
307
|
+
"""Diagnose standard library shadowing, wildcard imports, missing imports, and cycles."""
|
|
308
|
+
issues: List[DiagnosticIssue] = []
|
|
309
|
+
root = Path(workspace_dir).resolve()
|
|
310
|
+
|
|
311
|
+
# 1. Standard library shadowing
|
|
312
|
+
issues.extend(cls.check_stdlib_shadowing(workspace_dir))
|
|
313
|
+
|
|
314
|
+
# 2. File-level import checks
|
|
315
|
+
for root_dir, dirs, files in os.walk(str(root)):
|
|
316
|
+
dirs[:] = [d for d in dirs if d not in cls.IGNORED_DIRS]
|
|
317
|
+
for file in files:
|
|
318
|
+
if file.endswith(".py"):
|
|
319
|
+
full_p = os.path.join(root_dir, file)
|
|
320
|
+
try:
|
|
321
|
+
with open(full_p, "r", encoding="utf-8", errors="replace") as f:
|
|
322
|
+
content = f.read()
|
|
323
|
+
tree = ast.parse(content, filename=full_p)
|
|
324
|
+
issues.extend(cls.detect_missing_stdlib_imports(full_p, content, tree))
|
|
325
|
+
issues.extend(cls.detect_wildcard_imports(full_p, tree))
|
|
326
|
+
except SyntaxError:
|
|
327
|
+
continue
|
|
328
|
+
except Exception:
|
|
329
|
+
continue
|
|
330
|
+
|
|
331
|
+
# 3. Circular imports via Tarjan's SCC
|
|
332
|
+
graph, _ = cls.build_workspace_import_graph(workspace_dir)
|
|
333
|
+
cycles = cls.find_circular_import_cycles(graph)
|
|
334
|
+
for cyc in cycles:
|
|
335
|
+
cycle_str = " -> ".join(cyc) + f" -> {cyc[0]}"
|
|
336
|
+
issues.append(DiagnosticIssue(
|
|
337
|
+
category=DiagnosticCategory.IMPORTS.value,
|
|
338
|
+
severity=SeverityLevel.WARNING.value,
|
|
339
|
+
file_path=None,
|
|
340
|
+
line=None,
|
|
341
|
+
column=None,
|
|
342
|
+
error_code="TZ-IMP-CIRCULAR-CYCLE",
|
|
343
|
+
message=f"Circular import cycle detected across {len(cyc)} modules: [{cycle_str}]",
|
|
344
|
+
suggested_fix="Refactor shared types into a common 'types.py' or use local deferred imports inside functions",
|
|
345
|
+
auto_fixable=False,
|
|
346
|
+
metadata={"cycle": cyc}
|
|
347
|
+
))
|
|
348
|
+
|
|
349
|
+
return issues
|
|
350
|
+
|
|
351
|
+
@classmethod
|
|
352
|
+
def execute_import_heals(cls, issues: List[DiagnosticIssue], dry_run: bool = False) -> List[HealAction]:
|
|
353
|
+
"""Inject missing standard imports at top of source files."""
|
|
354
|
+
actions: List[HealAction] = []
|
|
355
|
+
|
|
356
|
+
# Group by file to inject cleanly in a single pass
|
|
357
|
+
by_file: Dict[str, List[str]] = {}
|
|
358
|
+
for issue in issues:
|
|
359
|
+
if issue.metadata.get("action") == "inject_import":
|
|
360
|
+
fpath = issue.metadata.get("path")
|
|
361
|
+
stmt = issue.metadata.get("stmt")
|
|
362
|
+
if fpath and stmt:
|
|
363
|
+
by_file.setdefault(fpath, []).append(stmt)
|
|
364
|
+
|
|
365
|
+
for fpath, stmts in by_file.items():
|
|
366
|
+
t0 = time.time()
|
|
367
|
+
p = Path(fpath)
|
|
368
|
+
if not p.is_file():
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
with open(p, "r", encoding="utf-8", errors="replace") as f:
|
|
373
|
+
lines = f.readlines()
|
|
374
|
+
|
|
375
|
+
unique_stmts = sorted(list(set(stmts)))
|
|
376
|
+
# Find safe insertion index (after docstring or comments)
|
|
377
|
+
insert_idx = 0
|
|
378
|
+
for idx, line in enumerate(lines[:20]):
|
|
379
|
+
stripped = line.strip()
|
|
380
|
+
if stripped.startswith("#") or "coding:" in stripped or stripped.startswith('"""') or stripped.startswith("'''"):
|
|
381
|
+
insert_idx = idx + 1
|
|
382
|
+
elif stripped.startswith("from __future__"):
|
|
383
|
+
insert_idx = idx + 1
|
|
384
|
+
|
|
385
|
+
for s in reversed(unique_stmts):
|
|
386
|
+
lines.insert(insert_idx, f"{s}\n")
|
|
387
|
+
|
|
388
|
+
diff = "".join([f"+ {s}\n" for s in unique_stmts])
|
|
389
|
+
if not dry_run:
|
|
390
|
+
with open(p, "w", encoding="utf-8", newline="\n") as f:
|
|
391
|
+
f.writelines(lines)
|
|
392
|
+
|
|
393
|
+
actions.append(HealAction(
|
|
394
|
+
category=DiagnosticCategory.IMPORTS.value,
|
|
395
|
+
action_type="inject_import",
|
|
396
|
+
file_path=fpath,
|
|
397
|
+
description=f"Injected {len(unique_stmts)} missing import(s) ({', '.join(unique_stmts[:3])}) into {p.name}",
|
|
398
|
+
success=True,
|
|
399
|
+
diff=diff,
|
|
400
|
+
duration_ms=round((time.time() - t0) * 1000, 2)
|
|
401
|
+
))
|
|
402
|
+
except Exception as e:
|
|
403
|
+
actions.append(HealAction(
|
|
404
|
+
category=DiagnosticCategory.IMPORTS.value,
|
|
405
|
+
action_type="inject_import",
|
|
406
|
+
file_path=fpath,
|
|
407
|
+
description=f"Failed injecting imports into {p.name}: {e}",
|
|
408
|
+
success=False,
|
|
409
|
+
duration_ms=round((time.time() - t0) * 1000, 2)
|
|
410
|
+
))
|
|
411
|
+
|
|
412
|
+
return actions
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""T-Zero Autonomous Doctor Interactive Terminal Cockpit & TUI.
|
|
3
|
+
|
|
4
|
+
Provides high-impact ANSI terminal visualization:
|
|
5
|
+
- Visual health score gauges and dial meters.
|
|
6
|
+
- Multi-column toolchain status matrices.
|
|
7
|
+
- Colored syntax-highlighted diff hunk renderers.
|
|
8
|
+
- Interactive category filtering and self-healing menus.
|
|
9
|
+
|
|
10
|
+
Author: Toprak Ahmet Aydogmus (Siber Akademi)
|
|
11
|
+
Version: 10.4.0
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from .models import (
|
|
19
|
+
DiagnosticIssue,
|
|
20
|
+
DiagnosticReport,
|
|
21
|
+
HealAction,
|
|
22
|
+
HealReport,
|
|
23
|
+
SeverityLevel,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class InteractiveFixerTUI:
|
|
28
|
+
"""Terminal UI renderer for T-Zero Autonomous Doctor."""
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def render_gauge(percentage: int, width: int = 30) -> str:
|
|
32
|
+
"""Render a colored ASCII health progress bar gauge."""
|
|
33
|
+
fill = int(width * (percentage / 100))
|
|
34
|
+
empty = width - fill
|
|
35
|
+
if percentage >= 90:
|
|
36
|
+
color = "\033[1;32m" # Bold green
|
|
37
|
+
elif percentage >= 70:
|
|
38
|
+
color = "\033[1;33m" # Bold yellow
|
|
39
|
+
else:
|
|
40
|
+
color = "\033[1;31m" # Bold red
|
|
41
|
+
|
|
42
|
+
bar = "█" * fill + "░" * empty
|
|
43
|
+
return f"{color}[{bar}] {percentage}%\033[0m"
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def render_cockpit(cls, diag: DiagnosticReport, heal: Optional[HealReport] = None):
|
|
47
|
+
"""Render full interactive cockpit view."""
|
|
48
|
+
term_width = 80
|
|
49
|
+
|
|
50
|
+
print("\033[36m" + "╔" + "═" * (term_width - 2) + "╗\033[0m")
|
|
51
|
+
print("\033[36m║\033[0m\033[1;37m ⚡ T-ZERO AUTONOMOUS PC-FIXER & CODEBASE DOCTOR COCKPIT \033[0m" + " " * 18 + "\033[36m║\033[0m")
|
|
52
|
+
print("\033[36m╠" + "═" * (term_width - 2) + "╣\033[0m")
|
|
53
|
+
|
|
54
|
+
# Top summary row
|
|
55
|
+
gauge = cls.render_gauge(diag.health_score_pct)
|
|
56
|
+
print(f"\033[36m║\033[0m Overall Health: {gauge:<38} \033[36m║\033[0m")
|
|
57
|
+
print(f"\033[36m║\033[0m Directory: \033[33m{diag.target_dir[:58]:<58}\033[0m \033[36m║\033[0m")
|
|
58
|
+
print(f"\033[36m║\033[0m Platform: \033[32m{diag.environment.os_release[:40]:<40}\033[0m (\033[35mv{diag.environment.python_version}\033[0m) \033[36m║\033[0m")
|
|
59
|
+
print(f"\033[36m║\033[0m Hardware: \033[36m{diag.environment.cpu_cores} Cores\033[0m | \033[36m{diag.environment.available_ram_gb:.1f}GB / {diag.environment.total_ram_gb:.1f}GB Free RAM\033[0m" + " " * 15 + "\033[36m║\033[0m")
|
|
60
|
+
print("\033[36m╠" + "═" * (term_width - 2) + "╣\033[0m")
|
|
61
|
+
|
|
62
|
+
# Diagnostics matrix
|
|
63
|
+
print("\033[36m║\033[0m \033[1mDIAGNOSTIC METRICS BREAKDOWN\033[0m" + " " * 48 + "\033[36m║\033[0m")
|
|
64
|
+
print(f"\033[36m║\033[0m • Critical Faults: \033[1;31m{diag.critical_count:<4}\033[0m │ Syntax Defects: \033[31m{diag.syntax_errors:<4}\033[0m \033[36m║\033[0m")
|
|
65
|
+
print(f"\033[36m║\033[0m • Error Level: \033[31m{diag.error_count:<4}\033[0m │ Missing Imports: \033[33m{diag.missing_imports:<4}\033[0m \033[36m║\033[0m")
|
|
66
|
+
print(f"\033[36m║\033[0m • Warnings: \033[33m{diag.warning_count:<4}\033[0m │ Security Leaks: \033[1;31m{diag.security_leaks:<4}\033[0m \033[36m║\033[0m")
|
|
67
|
+
print(f"\033[36m║\033[0m • Code Smells: \033[36m{diag.code_smells:<4}\033[0m │ Cache & Bytecode: \033[32m{diag.cache_issues:<4}\033[0m \033[36m║\033[0m")
|
|
68
|
+
print(f"\033[36m║\033[0m • Scanned Files: \033[35m{diag.scanned_file_count:<4}\033[0m │ Scan Latency: \033[35m{diag.scan_duration_ms}ms\033[0m" + " " * 9 + "\033[36m║\033[0m")
|
|
69
|
+
|
|
70
|
+
# Healing section if present
|
|
71
|
+
if heal:
|
|
72
|
+
print("\033[36m╠" + "═" * (term_width - 2) + "╣\033[0m")
|
|
73
|
+
print("\033[36m║\033[0m \033[1;32m💉 AUTONOMOUS SELF-HEALING OUTCOME\033[0m" + " " * 42 + "\033[36m║\033[0m")
|
|
74
|
+
print(f"\033[36m║\033[0m • Transaction: \033[35m{heal.transaction_id:<32}\033[0m \033[36m║\033[0m")
|
|
75
|
+
print(f"\033[36m║\033[0m • Issues Healed: \033[1;32m{heal.issues_healed} / {heal.issues_detected}\033[0m ({'DRY-RUN PREVIEW' if heal.dry_run else 'COMMITTED TO DISK'}) \033[36m║\033[0m")
|
|
76
|
+
print(f"\033[36m║\033[0m • Score Boost: \033[33m{heal.initial_health_score}%\033[0m -> \033[1;32m{heal.final_health_score}%\033[0m in \033[36m{heal.execution_duration_ms:.1f}ms\033[0m \033[36m║\033[0m")
|
|
77
|
+
|
|
78
|
+
print("\033[36m╚" + "═" * (term_width - 2) + "╝\033[0m")
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def render_diff(cls, diff_text: str):
|
|
82
|
+
"""Render a unified diff with ANSI syntax colors."""
|
|
83
|
+
for line in diff_text.splitlines():
|
|
84
|
+
if line.startswith("+"):
|
|
85
|
+
print(f"\033[32m{line}\033[0m")
|
|
86
|
+
elif line.startswith("-"):
|
|
87
|
+
print(f"\033[31m{line}\033[0m")
|
|
88
|
+
elif line.startswith("@@"):
|
|
89
|
+
print(f"\033[35m{line}\033[0m")
|
|
90
|
+
else:
|
|
91
|
+
print(line)
|