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.
Files changed (96) hide show
  1. package/bin/tzero-mcp.js +9 -16
  2. package/bin/tzero.js +9 -16
  3. package/index.js +7 -2
  4. package/package.json +2 -1
  5. package/src/addmcp.py +1194 -0
  6. package/src/main.py +60 -0
  7. package/src/siber_akademi_icon.py +77 -0
  8. package/src/tzero/jarvis/semantic_compressor.py +373 -0
  9. package/src/tzero.py +58 -0
  10. package/src/tzero_benchmark.py +475 -0
  11. package/src/tzero_bridge.py +362 -0
  12. package/src/tzero_ci.py +153 -0
  13. package/src/tzero_commit.py +192 -0
  14. package/src/tzero_cost.py +163 -0
  15. package/src/tzero_dashboard.py +1448 -0
  16. package/src/tzero_deps.py +287 -0
  17. package/src/tzero_drift.py +208 -0
  18. package/src/tzero_explain.py +240 -0
  19. package/src/tzero_features.py +3022 -0
  20. package/src/tzero_fix.py +228 -0
  21. package/src/tzero_fixer/__init__.py +63 -0
  22. package/src/tzero_fixer/amalgamation.py +334 -0
  23. package/src/tzero_fixer/ast_syntax_healer.py +464 -0
  24. package/src/tzero_fixer/ast_type_modernizer.py +288 -0
  25. package/src/tzero_fixer/async_io_modernizer.py +127 -0
  26. package/src/tzero_fixer/automated_regression_suite.py +600 -0
  27. package/src/tzero_fixer/benchmarks_and_diagnostics.py +174 -0
  28. package/src/tzero_fixer/ci_cd_self_healing_gate.py +246 -0
  29. package/src/tzero_fixer/code_duplicity_and_refactorer.py +151 -0
  30. package/src/tzero_fixer/code_smells_healer.py +260 -0
  31. package/src/tzero_fixer/cwe_remediator.py +171 -0
  32. package/src/tzero_fixer/dead_code_eliminator.py +150 -0
  33. package/src/tzero_fixer/dependency_vulnerability_auditor.py +309 -0
  34. package/src/tzero_fixer/git_advanced_surgeon.py +280 -0
  35. package/src/tzero_fixer/hardware_and_disk_doctor.py +325 -0
  36. package/src/tzero_fixer/import_graph_healer.py +412 -0
  37. package/src/tzero_fixer/interactive_tui.py +91 -0
  38. package/src/tzero_fixer/knowledge_base.py +295 -0
  39. package/src/tzero_fixer/knowledge_base_extended.py +940 -0
  40. package/src/tzero_fixer/linters_and_static_analyzer.py +307 -0
  41. package/src/tzero_fixer/mcp_server.py +447 -0
  42. package/src/tzero_fixer/models.py +385 -0
  43. package/src/tzero_fixer/orchestrator.py +375 -0
  44. package/src/tzero_fixer/os_doctor.py +502 -0
  45. package/src/tzero_fixer/packaging_abi_surgeon.py +323 -0
  46. package/src/tzero_fixer/pc_fixer_doctor.py +599 -0
  47. package/src/tzero_fixer/performance_and_memory_profiler.py +274 -0
  48. package/src/tzero_fixer/polyglot_ast_parsers.py +313 -0
  49. package/src/tzero_fixer/polyglot_build_healer.py +364 -0
  50. package/src/tzero_fixer/polyglot_config_healer.py +318 -0
  51. package/src/tzero_fixer/polyglot_doctor.py +372 -0
  52. package/src/tzero_fixer/polyglot_linters.py +619 -0
  53. package/src/tzero_fixer/polyglot_schema_validator.py +305 -0
  54. package/src/tzero_fixer/security_cwe_database.py +444 -0
  55. package/src/tzero_fixer/security_deep_rules.py +114 -0
  56. package/src/tzero_fixer/security_rules_catalog.py +110 -0
  57. package/src/tzero_fixer/security_zero_leak.py +326 -0
  58. package/src/tzero_fixer/smart_patch_recommender.py +223 -0
  59. package/src/tzero_fixer/synthetic_workspace_generator.py +220 -0
  60. package/src/tzero_fixer/system_diagnostics_extended.py +493 -0
  61. package/src/tzero_fixer/system_repair_recipes.py +447 -0
  62. package/src/tzero_fixer/venv_doctor.py +309 -0
  63. package/src/tzero_gateway.py +885 -0
  64. package/src/tzero_guard.py +243 -0
  65. package/src/tzero_infra.py +274 -0
  66. package/src/tzero_jarvis/__init__.py +124 -0
  67. package/src/tzero_jarvis/causal_graph.py +310 -0
  68. package/src/tzero_jarvis/graph_visualizer.py +395 -0
  69. package/src/tzero_jarvis/hybrid_search.py +214 -0
  70. package/src/tzero_jarvis/latent_press.py +137 -0
  71. package/src/tzero_jarvis/memory_consolidation.py +212 -0
  72. package/src/tzero_jarvis/minhash_lsh.py +184 -0
  73. package/src/tzero_jarvis/orchestrator.py +335 -0
  74. package/src/tzero_jarvis/self_healing.py +308 -0
  75. package/src/tzero_jarvis/semantic_compressor.py +887 -0
  76. package/src/tzero_jarvis/swarm.py +261 -0
  77. package/src/tzero_jarvis/swarm_bft.py +144 -0
  78. package/src/tzero_jarvis/workspace_orchestrator.py +1167 -0
  79. package/src/tzero_jarvis/zero_key_router.py +173 -0
  80. package/src/tzero_mcp.py +4858 -0
  81. package/src/tzero_neural_cortex.py +1425 -0
  82. package/src/tzero_packer.py +370 -0
  83. package/src/tzero_patch.py +624 -0
  84. package/src/tzero_pc_fixer.py +12657 -0
  85. package/src/tzero_pipe.py +95 -0
  86. package/src/tzero_polyglot.py +422 -0
  87. package/src/tzero_reachability.py +272 -0
  88. package/src/tzero_replay.py +141 -0
  89. package/src/tzero_security.py +570 -0
  90. package/src/tzero_snapshot.py +258 -0
  91. package/src/tzero_stubs.py +313 -0
  92. package/src/tzero_superdesign.py +774 -0
  93. package/src/tzero_sync.py +241 -0
  94. package/src/tzero_task_benchmark.py +363 -0
  95. package/src/tzero_tui.py +244 -0
  96. package/src/tzero_v3.py +9435 -0
@@ -0,0 +1,599 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ T-Zero PC Fixer Doctor Classic Facade and Compatibility Layer
4
+ ============================================================
5
+ Provides backward-compatible PCFixerDoctor class for legacy test suites and APIs.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import ast
11
+ import json
12
+ import os
13
+ import re
14
+ import shutil
15
+ import sys
16
+ import time
17
+ from dataclasses import asdict
18
+ from pathlib import Path
19
+ from typing import Any, Dict, List, Optional, Set, Tuple
20
+
21
+ from tzero_fixer.models import (
22
+ DiagnosticCategory,
23
+ DiagnosticIssue,
24
+ DiagnosticReport,
25
+ HealAction,
26
+ HealReport,
27
+ SeverityLevel,
28
+ )
29
+
30
+ class PCFixerDoctor:
31
+ """Enterprise-grade self-healing PC, runtime environment, and codebase doctor."""
32
+
33
+ COMMON_STDLIB_SYMBOLS = {
34
+ "os": "import os",
35
+ "sys": "import sys",
36
+ "re": "import re",
37
+ "json": "import json",
38
+ "math": "import math",
39
+ "time": "import time",
40
+ "pathlib": "from pathlib import Path",
41
+ "Path": "from pathlib import Path",
42
+ "ast": "import ast",
43
+ "dataclass": "from dataclasses import dataclass",
44
+ "field": "from dataclasses import field",
45
+ "asdict": "from dataclasses import asdict",
46
+ "Dict": "from typing import Dict",
47
+ "List": "from typing import List",
48
+ "Optional": "from typing import Optional",
49
+ "Union": "from typing import Union",
50
+ "Tuple": "from typing import Tuple",
51
+ "Any": "from typing import Any",
52
+ "Callable": "from typing import Callable",
53
+ "Set": "from typing import Set",
54
+ }
55
+
56
+ IGNORE_DIRS = {
57
+ ".git", ".svn", ".hg", "__pycache__", ".pytest_cache", ".mypy_cache",
58
+ "node_modules", ".venv", "venv", "env", "build", "dist", ".idea", ".vscode"
59
+ }
60
+
61
+ @classmethod
62
+ def check_tools_on_path(cls) -> Dict[str, bool]:
63
+ """Check availability of essential development CLI tools on system PATH."""
64
+ tools = ["git", "python", "pip", "npm", "npx", "code", "cursor"]
65
+ res = {}
66
+ for t in tools:
67
+ res[t] = shutil.which(t) is not None
68
+ return res
69
+
70
+ @classmethod
71
+ def inspect_virtualenv(cls, target_dir: str = ".") -> Dict[str, Any]:
72
+ """Check integrity of local virtual environments (.venv, venv, env)."""
73
+ root = Path(target_dir).resolve()
74
+ candidates = [root / ".venv", root / "venv", root / "env"]
75
+ found = None
76
+ for c in candidates:
77
+ if c.is_dir() and (c / "pyvenv.cfg").is_file():
78
+ found = c
79
+ break
80
+
81
+ if not found:
82
+ return {
83
+ "active": hasattr(sys, "real_prefix") or (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix),
84
+ "detected": False,
85
+ "path": None,
86
+ "valid": True,
87
+ "error": None
88
+ }
89
+
90
+ cfg_file = found / "pyvenv.cfg"
91
+ home_path = None
92
+ try:
93
+ with open(cfg_file, "r", encoding="utf-8", errors="replace") as f:
94
+ for line in f:
95
+ if line.strip().startswith("home ="):
96
+ home_path = line.split("=", 1)[1].strip()
97
+ break
98
+ except Exception as e:
99
+ return {"active": True, "detected": True, "path": str(found), "valid": False, "error": f"Cannot read pyvenv.cfg: {e}"}
100
+
101
+ home_valid = bool(home_path and os.path.isdir(home_path))
102
+ return {
103
+ "active": True,
104
+ "detected": True,
105
+ "path": str(found),
106
+ "cfg_home": home_path,
107
+ "valid": home_valid,
108
+ "error": None if home_valid else f"Base python home invalid: '{home_path}'"
109
+ }
110
+
111
+ @classmethod
112
+ def scan_syntax_and_imports(cls, file_path: str) -> List[DiagnosticIssue]:
113
+ """Scan a single Python file for syntax flaws, BOM artifacts, and missing imports."""
114
+ issues: List[DiagnosticIssue] = []
115
+ path = Path(file_path)
116
+ if not path.is_file() or path.suffix != ".py":
117
+ return issues
118
+
119
+ try:
120
+ with open(path, "rb") as f:
121
+ raw_bytes = f.read()
122
+ except Exception as e:
123
+ issues.append(DiagnosticIssue(
124
+ category="env",
125
+ severity="error",
126
+ file_path=str(path),
127
+ line=None,
128
+ message=f"Permission or read error: {e}",
129
+ suggested_fix="Check file permissions",
130
+ auto_fixable=False
131
+ ))
132
+ return issues
133
+
134
+ # Check BOM / Zero-width characters
135
+ if raw_bytes.startswith(b"\xef\xbb\xbf"):
136
+ issues.append(DiagnosticIssue(
137
+ category="encoding",
138
+ severity="warning",
139
+ file_path=str(path),
140
+ line=1,
141
+ message="File contains UTF-8 Byte Order Mark (BOM) which may disrupt interpreters",
142
+ suggested_fix="Strip UTF-8 BOM",
143
+ auto_fixable=True,
144
+ metadata={"action": "strip_bom"}
145
+ ))
146
+
147
+ try:
148
+ content = raw_bytes.decode("utf-8")
149
+ except UnicodeDecodeError:
150
+ issues.append(DiagnosticIssue(
151
+ category="encoding",
152
+ severity="error",
153
+ file_path=str(path),
154
+ line=1,
155
+ message="File encoding is not valid UTF-8",
156
+ suggested_fix="Transcode to clean UTF-8",
157
+ auto_fixable=True,
158
+ metadata={"action": "fix_encoding"}
159
+ ))
160
+ content = raw_bytes.decode("utf-8", errors="replace")
161
+
162
+ # Zero-width spaces
163
+ if "\u200b" in content or "\ufeff" in content:
164
+ issues.append(DiagnosticIssue(
165
+ category="encoding",
166
+ severity="warning",
167
+ file_path=str(path),
168
+ line=1,
169
+ message="File contains hidden zero-width spaces causing phantom syntax bugs",
170
+ suggested_fix="Strip zero-width characters",
171
+ auto_fixable=True,
172
+ metadata={"action": "strip_zero_width"}
173
+ ))
174
+
175
+ # Check AST parse
176
+ try:
177
+ tree = ast.parse(content, filename=str(path))
178
+ except SyntaxError as se:
179
+ # Analyze syntax error reason
180
+ err_line = se.lineno or 1
181
+ err_msg = se.msg or "Syntax error"
182
+ suggested = "Fix syntax error"
183
+ action = "syntax_fix"
184
+
185
+ lines = content.splitlines()
186
+ if 0 < err_line <= len(lines):
187
+ line_str = lines[err_line - 1].strip()
188
+ # Check missing colon
189
+ block_starters = ("def ", "class ", "if ", "elif ", "else", "try", "except", "finally", "for ", "while ", "with ")
190
+ if any(line_str.startswith(b) for b in block_starters) and not line_str.endswith(":"):
191
+ suggested = "Append missing colon ':' to block definition"
192
+ action = "append_colon"
193
+ # Check Python 2 print
194
+ elif re.match(r"^print\s+[\"'].*", line_str):
195
+ suggested = "Convert Python 2 print statement to print(...) function call"
196
+ action = "convert_print2"
197
+
198
+ issues.append(DiagnosticIssue(
199
+ category="syntax",
200
+ severity="error",
201
+ file_path=str(path),
202
+ line=err_line,
203
+ message=f"SyntaxError: {err_msg} on line {err_line}",
204
+ suggested_fix=suggested,
205
+ auto_fixable=True,
206
+ metadata={"action": action, "lineno": err_line}
207
+ ))
208
+ return issues
209
+
210
+ # Check missing top-level standard library imports
211
+ imported_symbols: Set[str] = set()
212
+ for node in ast.walk(tree):
213
+ if isinstance(node, ast.Import):
214
+ for alias in node.names:
215
+ imported_symbols.add(alias.name.split(".")[0])
216
+ imported_symbols.add(alias.asname or alias.name)
217
+ elif isinstance(node, ast.ImportFrom):
218
+ for alias in node.names:
219
+ imported_symbols.add(alias.asname or alias.name)
220
+
221
+ referenced_names: Set[str] = set()
222
+ for node in ast.walk(tree):
223
+ if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
224
+ referenced_names.add(node.id)
225
+
226
+ # Builtins
227
+ builtins_set = set(dir(__builtins__))
228
+
229
+ for sym, import_stmt in cls.COMMON_STDLIB_SYMBOLS.items():
230
+ if sym in referenced_names and sym not in imported_symbols and sym not in builtins_set:
231
+ issues.append(DiagnosticIssue(
232
+ category="import",
233
+ severity="warning",
234
+ file_path=str(path),
235
+ line=1,
236
+ message=f"Referenced symbol '{sym}' is missing explicit import statement",
237
+ suggested_fix=f"Add '{import_stmt}' at top of file",
238
+ auto_fixable=True,
239
+ metadata={"action": "add_import", "symbol": sym, "stmt": import_stmt}
240
+ ))
241
+
242
+ return issues
243
+
244
+ @classmethod
245
+ def scan_cache_and_bloat(cls, target_dir: str = ".") -> List[DiagnosticIssue]:
246
+ """Scan for orphaned .pyc files, stale git locks, and bloated caches."""
247
+ issues: List[DiagnosticIssue] = []
248
+ root = Path(target_dir).resolve()
249
+
250
+ # Check stale .git/index.lock
251
+ git_lock = root / ".git" / "index.lock"
252
+ if git_lock.is_file():
253
+ age_sec = time.time() - os.path.getmtime(git_lock)
254
+ if age_sec > 300: # older than 5 minutes
255
+ issues.append(DiagnosticIssue(
256
+ category="cache",
257
+ severity="warning",
258
+ file_path=str(git_lock),
259
+ line=None,
260
+ message=f"Stale git lockfile detected ({int(age_sec)}s old) blocking git operations",
261
+ suggested_fix="Remove stale .git/index.lock",
262
+ auto_fixable=True,
263
+ metadata={"action": "remove_file", "path": str(git_lock)}
264
+ ))
265
+
266
+ # Check orphaned .pyc in __pycache__
267
+ for root_dir, dirs, files in os.walk(str(root)):
268
+ # skip ignored
269
+ dirs[:] = [d for d in dirs if d not in cls.IGNORE_DIRS or d == "__pycache__"]
270
+ if os.path.basename(root_dir) == "__pycache__":
271
+ parent_dir = Path(root_dir).parent
272
+ for f in files:
273
+ if f.endswith(".pyc"):
274
+ base_name = f.split(".")[0]
275
+ py_sibling = parent_dir / f"{base_name}.py"
276
+ if not py_sibling.is_file():
277
+ pyc_path = os.path.join(root_dir, f)
278
+ issues.append(DiagnosticIssue(
279
+ category="cache",
280
+ severity="info",
281
+ file_path=pyc_path,
282
+ line=None,
283
+ message=f"Orphaned bytecode {f} has no corresponding .py source file",
284
+ suggested_fix="Purge orphaned bytecode",
285
+ auto_fixable=True,
286
+ metadata={"action": "remove_file", "path": pyc_path}
287
+ ))
288
+
289
+ return issues
290
+
291
+ @classmethod
292
+ def diagnose(cls, target_dir: str = ".") -> DiagnosticReport:
293
+ """Run comprehensive diagnostic inspection across environment, workspace, and files."""
294
+ root = Path(target_dir).resolve()
295
+ tools_status = cls.check_tools_on_path()
296
+ venv_status = cls.inspect_virtualenv(target_dir)
297
+
298
+ issues: List[DiagnosticIssue] = []
299
+
300
+ # Check Python version
301
+ py_ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
302
+ if sys.version_info < (3, 9):
303
+ issues.append(DiagnosticIssue(
304
+ category="env",
305
+ severity="error",
306
+ file_path=None,
307
+ line=None,
308
+ message=f"Python version {py_ver} is below minimum supported 3.9",
309
+ suggested_fix="Upgrade Python to 3.9 or higher",
310
+ auto_fixable=False
311
+ ))
312
+
313
+ # Check venv
314
+ if venv_status.get("detected") and not venv_status.get("valid"):
315
+ issues.append(DiagnosticIssue(
316
+ category="env",
317
+ severity="error",
318
+ file_path=os.path.join(venv_status["path"], "pyvenv.cfg"),
319
+ line=None,
320
+ message=f"Virtualenv corrupted: {venv_status.get('error')}",
321
+ suggested_fix="Re-point pyvenv.cfg home to active python interpreter",
322
+ auto_fixable=True,
323
+ metadata={"action": "repair_venv_cfg", "path": venv_status["path"]}
324
+ ))
325
+
326
+ # Scan files
327
+ for root_dir, dirs, files in os.walk(str(root)):
328
+ dirs[:] = [d for d in dirs if d not in cls.IGNORE_DIRS]
329
+ for file in files:
330
+ if file.endswith(".py"):
331
+ full_path = os.path.join(root_dir, file)
332
+ issues.extend(cls.scan_syntax_and_imports(full_path))
333
+
334
+ # Scan cache & bloat
335
+ issues.extend(cls.scan_cache_and_bloat(target_dir))
336
+
337
+ # Tally counts
338
+ syntax_errors = sum(1 for i in issues if i.category == "syntax")
339
+ missing_imports = sum(1 for i in issues if i.category == "import")
340
+ env_issues = sum(1 for i in issues if i.category == "env")
341
+ cache_issues = sum(1 for i in issues if i.category in ("cache", "encoding"))
342
+
343
+ # Health score calculation
344
+ penalty = (syntax_errors * 20) + (missing_imports * 5) + (env_issues * 15) + (cache_issues * 2)
345
+ score = max(0, min(100, 100 - penalty))
346
+
347
+ return DiagnosticReport(
348
+ timestamp=time.time(),
349
+ target_dir=str(root),
350
+ health_score_pct=score,
351
+ total_issues=len(issues),
352
+ syntax_errors=syntax_errors,
353
+ missing_imports=missing_imports,
354
+ env_issues=env_issues,
355
+ cache_issues=cache_issues,
356
+ issues=issues,
357
+ tools_status=tools_status,
358
+ python_version=py_ver,
359
+ virtualenv_status=venv_status
360
+ )
361
+
362
+ @classmethod
363
+ def auto_heal(cls, target_dir: str = ".", dry_run: bool = False,
364
+ fix_syntax: bool = True, add_imports: bool = True,
365
+ clean_caches: bool = True, repair_venv: bool = True) -> HealReport:
366
+ """Execute autonomous self-healing for detected codebase and environment issues."""
367
+ diag_before = cls.diagnose(target_dir)
368
+ actions: List[HealAction] = []
369
+
370
+ files_to_recheck: Set[str] = set()
371
+
372
+ for issue in diag_before.issues:
373
+ action_type = issue.metadata.get("action")
374
+ if not issue.auto_fixable or not action_type:
375
+ continue
376
+
377
+ # 1. Clean UTF-8 BOM or Zero-width chars
378
+ if action_type in ("strip_bom", "strip_zero_width", "fix_encoding") and issue.file_path:
379
+ try:
380
+ p = Path(issue.file_path)
381
+ with open(p, "rb") as f:
382
+ data = f.read()
383
+ if action_type == "strip_bom" and data.startswith(b"\xef\xbb\xbf"):
384
+ data = data[3:]
385
+ text = data.decode("utf-8", errors="replace")
386
+ if action_type == "strip_zero_width":
387
+ text = text.replace("\u200b", "").replace("\ufeff", "")
388
+ if not dry_run:
389
+ with open(p, "w", encoding="utf-8", newline="\n") as f:
390
+ f.write(text)
391
+ actions.append(HealAction(
392
+ category="encoding",
393
+ file_path=issue.file_path,
394
+ description=f"Sanitized encoding/BOM in {p.name}",
395
+ success=True
396
+ ))
397
+ files_to_recheck.add(issue.file_path)
398
+ except Exception as e:
399
+ actions.append(HealAction(
400
+ category="encoding",
401
+ file_path=issue.file_path,
402
+ description=f"Failed sanitizing encoding: {e}",
403
+ success=False
404
+ ))
405
+
406
+ # 2. Fix syntax errors (missing colon, Python 2 print)
407
+ elif action_type in ("append_colon", "convert_print2") and fix_syntax and issue.file_path:
408
+ try:
409
+ p = Path(issue.file_path)
410
+ with open(p, "r", encoding="utf-8", errors="replace") as f:
411
+ lines = f.readlines()
412
+ lineno = issue.metadata.get("lineno", 1)
413
+ if 0 < lineno <= len(lines):
414
+ orig_line = lines[lineno - 1]
415
+ mod_line = orig_line
416
+ if action_type == "append_colon":
417
+ mod_line = orig_line.rstrip("\r\n") + ":\n"
418
+ elif action_type == "convert_print2":
419
+ mod_line = re.sub(r"^(\s*)print\s+(.*)$", r"\1print(\2)", orig_line)
420
+
421
+ if orig_line != mod_line:
422
+ diff = "".join(difflib.unified_diff(
423
+ [orig_line], [mod_line], fromfile="before", tofile="after"
424
+ ))
425
+ lines[lineno - 1] = mod_line
426
+ if not dry_run:
427
+ with open(p, "w", encoding="utf-8", newline="\n") as f:
428
+ f.writelines(lines)
429
+ actions.append(HealAction(
430
+ category="syntax",
431
+ file_path=issue.file_path,
432
+ description=f"Patched {action_type} on line {lineno} of {p.name}",
433
+ success=True,
434
+ diff=diff
435
+ ))
436
+ files_to_recheck.add(issue.file_path)
437
+ except Exception as e:
438
+ actions.append(HealAction(
439
+ category="syntax",
440
+ file_path=issue.file_path,
441
+ description=f"Failed patching syntax: {e}",
442
+ success=False
443
+ ))
444
+
445
+ # 3. Add missing standard library import
446
+ elif action_type == "add_import" and add_imports and issue.file_path:
447
+ try:
448
+ p = Path(issue.file_path)
449
+ with open(p, "r", encoding="utf-8", errors="replace") as f:
450
+ lines = f.readlines()
451
+ stmt = issue.metadata.get("stmt")
452
+ if stmt:
453
+ # Find safe insert location (after future imports / docstring or line 0)
454
+ insert_idx = 0
455
+ for idx, l in enumerate(lines[:15]):
456
+ if l.startswith("#") or "coding:" in l or l.startswith('"""') or l.startswith("'''"):
457
+ insert_idx = idx + 1
458
+ elif l.startswith("from __future__"):
459
+ insert_idx = idx + 1
460
+
461
+ lines.insert(insert_idx, f"{stmt}\n")
462
+ diff = f"+ {stmt}\n"
463
+ if not dry_run:
464
+ with open(p, "w", encoding="utf-8", newline="\n") as f:
465
+ f.writelines(lines)
466
+ actions.append(HealAction(
467
+ category="import",
468
+ file_path=issue.file_path,
469
+ description=f"Injected missing '{stmt}' into {p.name}",
470
+ success=True,
471
+ diff=diff
472
+ ))
473
+ files_to_recheck.add(issue.file_path)
474
+ except Exception as e:
475
+ actions.append(HealAction(
476
+ category="import",
477
+ file_path=issue.file_path,
478
+ description=f"Failed adding import: {e}",
479
+ success=False
480
+ ))
481
+
482
+ # 4. Remove stale lockfiles or orphaned bytecode
483
+ elif action_type == "remove_file" and clean_caches:
484
+ target_path = issue.metadata.get("path") or issue.file_path
485
+ if target_path and os.path.exists(target_path):
486
+ try:
487
+ if not dry_run:
488
+ os.remove(target_path)
489
+ actions.append(HealAction(
490
+ category="cache",
491
+ file_path=target_path,
492
+ description=f"Purged {os.path.basename(target_path)}",
493
+ success=True
494
+ ))
495
+ except Exception as e:
496
+ actions.append(HealAction(
497
+ category="cache",
498
+ file_path=target_path,
499
+ description=f"Failed removing {target_path}: {e}",
500
+ success=False
501
+ ))
502
+
503
+ # 5. Repair virtualenv config
504
+ elif action_type == "repair_venv_cfg" and repair_venv:
505
+ venv_dir = issue.metadata.get("path")
506
+ if venv_dir:
507
+ cfg_file = os.path.join(venv_dir, "pyvenv.cfg")
508
+ curr_py_home = os.path.dirname(sys.executable)
509
+ try:
510
+ lines = []
511
+ if os.path.isfile(cfg_file):
512
+ with open(cfg_file, "r", encoding="utf-8", errors="replace") as f:
513
+ for line in f:
514
+ if line.strip().startswith("home ="):
515
+ lines.append(f"home = {curr_py_home}\n")
516
+ else:
517
+ lines.append(line)
518
+ if not dry_run:
519
+ with open(cfg_file, "w", encoding="utf-8", newline="\n") as f:
520
+ f.writelines(lines)
521
+ actions.append(HealAction(
522
+ category="env",
523
+ file_path=cfg_file,
524
+ description=f"Updated pyvenv.cfg home path to '{curr_py_home}'",
525
+ success=True
526
+ ))
527
+ except Exception as e:
528
+ actions.append(HealAction(
529
+ category="env",
530
+ file_path=cfg_file,
531
+ description=f"Failed updating pyvenv.cfg: {e}",
532
+ success=False
533
+ ))
534
+
535
+ # Re-diagnose to evaluate final score
536
+ diag_after = cls.diagnose(target_dir) if not dry_run else diag_before
537
+ successful_heals = sum(1 for a in actions if a.success)
538
+
539
+ return HealReport(
540
+ timestamp=time.time(),
541
+ target_dir=str(Path(target_dir).resolve()),
542
+ initial_health_score=diag_before.health_score_pct,
543
+ final_health_score=diag_after.health_score_pct,
544
+ issues_detected=diag_before.total_issues,
545
+ issues_healed=successful_heals,
546
+ dry_run=dry_run,
547
+ actions=actions
548
+ )
549
+
550
+ @classmethod
551
+ def render_cli(cls, report: DiagnosticReport, heal_report: Optional[HealReport] = None):
552
+ """Render beautiful, ANSI-colored doctor report to terminal."""
553
+ print("=" * 80)
554
+ print("\033[1;36m🩺 T-ZERO PC-FIXER & ENVIRONMENT AUTONOMOUS DOCTOR\033[0m")
555
+ print("=" * 80)
556
+ print(f"Target Directory: \033[33m{report.target_dir}\033[0m")
557
+ print(f"Python Runtime: \033[32mv{report.python_version}\033[0m ({sys.executable})")
558
+ print(f"Virtualenv: \033[32m{report.virtualenv_status.get('path') or 'Global / System'}\033[0m")
559
+
560
+ # Tools status
561
+ tools_str = " ".join([
562
+ f"\033[32m[OK] {t}\033[0m" if ok else f"\033[31m[--] {t}\033[0m"
563
+ for t, ok in report.tools_status.items()
564
+ ])
565
+ print(f"CLI Tools: {tools_str}")
566
+
567
+ score_color = "\033[32m" if report.health_score_pct >= 90 else ("\033[33m" if report.health_score_pct >= 70 else "\033[31m")
568
+ print(f"Health Score: {score_color}{report.health_score_pct} / 100\033[0m")
569
+ print("-" * 80)
570
+
571
+ if report.total_issues == 0:
572
+ print("\033[32m✨ Codebase and Environment are in 100% pristine condition! Zero issues found.\033[0m")
573
+ else:
574
+ print(f"Detected Issues: \033[1;31m{report.total_issues}\033[0m "
575
+ f"(Syntax: {report.syntax_errors}, Imports: {report.missing_imports}, "
576
+ f"Env: {report.env_issues}, Cache: {report.cache_issues})")
577
+ print()
578
+ for idx, issue in enumerate(report.issues[:15], start=1):
579
+ loc = f"{os.path.basename(issue.file_path or 'system')}:{issue.line or '-'}"
580
+ icon = "\033[31m[FAIL]\033[0m" if issue.severity == "error" else "\033[33m[WARN]\033[0m"
581
+ print(f" {idx:2d}. {icon} \033[1m{loc:<22}\033[0m {issue.message}")
582
+ print(f" \033[36mFix:\033[0m {issue.suggested_fix}")
583
+
584
+ if len(report.issues) > 15:
585
+ print(f" ... and {len(report.issues) - 15} more issues.")
586
+
587
+ if heal_report:
588
+ print("-" * 80)
589
+ print("\033[1;32m💉 AUTONOMOUS SELF-HEALING RESULTS:\033[0m")
590
+ print(f" Issues Healed: \033[32m{heal_report.issues_healed} / {heal_report.issues_detected}\033[0m")
591
+ print(f" Health Boost: \033[33m{heal_report.initial_health_score}%\033[0m -> \033[1;32m{heal_report.final_health_score}%\033[0m")
592
+ for a in heal_report.actions:
593
+ status_icon = "\033[32m✔\033[0m" if a.success else "\033[31m✘\033[0m"
594
+ print(f" {status_icon} [{a.category.upper()}] {a.description}")
595
+ if a.diff:
596
+ for dline in a.diff.splitlines()[:3]:
597
+ print(f" \033[35m{dline}\033[0m")
598
+
599
+ print("=" * 80)