tzero-mcp 10.7.0 → 10.7.2

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 (101) hide show
  1. package/CHANGELOG.md +589 -0
  2. package/CONTRIBUTING.md +71 -0
  3. package/README.md +25 -17
  4. package/bin/postinstall.js +34 -0
  5. package/bin/tzero-mcp.js +16 -19
  6. package/bin/tzero.js +14 -19
  7. package/index.d.ts +24 -0
  8. package/index.js +7 -3
  9. package/package.json +24 -2
  10. package/src/addmcp.py +1194 -0
  11. package/src/main.py +60 -0
  12. package/src/siber_akademi_icon.py +77 -0
  13. package/src/tzero/jarvis/semantic_compressor.py +373 -0
  14. package/src/tzero.py +58 -0
  15. package/src/tzero_benchmark.py +475 -0
  16. package/src/tzero_bridge.py +362 -0
  17. package/src/tzero_ci.py +153 -0
  18. package/src/tzero_commit.py +192 -0
  19. package/src/tzero_cost.py +163 -0
  20. package/src/tzero_dashboard.py +1448 -0
  21. package/src/tzero_deps.py +287 -0
  22. package/src/tzero_drift.py +208 -0
  23. package/src/tzero_explain.py +240 -0
  24. package/src/tzero_features.py +3022 -0
  25. package/src/tzero_fix.py +228 -0
  26. package/src/tzero_fixer/__init__.py +63 -0
  27. package/src/tzero_fixer/amalgamation.py +334 -0
  28. package/src/tzero_fixer/ast_syntax_healer.py +464 -0
  29. package/src/tzero_fixer/ast_type_modernizer.py +288 -0
  30. package/src/tzero_fixer/async_io_modernizer.py +127 -0
  31. package/src/tzero_fixer/automated_regression_suite.py +600 -0
  32. package/src/tzero_fixer/benchmarks_and_diagnostics.py +174 -0
  33. package/src/tzero_fixer/ci_cd_self_healing_gate.py +246 -0
  34. package/src/tzero_fixer/code_duplicity_and_refactorer.py +151 -0
  35. package/src/tzero_fixer/code_smells_healer.py +260 -0
  36. package/src/tzero_fixer/cwe_remediator.py +171 -0
  37. package/src/tzero_fixer/dead_code_eliminator.py +150 -0
  38. package/src/tzero_fixer/dependency_vulnerability_auditor.py +309 -0
  39. package/src/tzero_fixer/git_advanced_surgeon.py +280 -0
  40. package/src/tzero_fixer/hardware_and_disk_doctor.py +325 -0
  41. package/src/tzero_fixer/import_graph_healer.py +412 -0
  42. package/src/tzero_fixer/interactive_tui.py +91 -0
  43. package/src/tzero_fixer/knowledge_base.py +295 -0
  44. package/src/tzero_fixer/knowledge_base_extended.py +940 -0
  45. package/src/tzero_fixer/linters_and_static_analyzer.py +307 -0
  46. package/src/tzero_fixer/mcp_server.py +447 -0
  47. package/src/tzero_fixer/models.py +385 -0
  48. package/src/tzero_fixer/orchestrator.py +375 -0
  49. package/src/tzero_fixer/os_doctor.py +502 -0
  50. package/src/tzero_fixer/packaging_abi_surgeon.py +323 -0
  51. package/src/tzero_fixer/pc_fixer_doctor.py +599 -0
  52. package/src/tzero_fixer/performance_and_memory_profiler.py +274 -0
  53. package/src/tzero_fixer/polyglot_ast_parsers.py +313 -0
  54. package/src/tzero_fixer/polyglot_build_healer.py +364 -0
  55. package/src/tzero_fixer/polyglot_config_healer.py +318 -0
  56. package/src/tzero_fixer/polyglot_doctor.py +372 -0
  57. package/src/tzero_fixer/polyglot_linters.py +619 -0
  58. package/src/tzero_fixer/polyglot_schema_validator.py +305 -0
  59. package/src/tzero_fixer/security_cwe_database.py +444 -0
  60. package/src/tzero_fixer/security_deep_rules.py +114 -0
  61. package/src/tzero_fixer/security_rules_catalog.py +110 -0
  62. package/src/tzero_fixer/security_zero_leak.py +326 -0
  63. package/src/tzero_fixer/smart_patch_recommender.py +223 -0
  64. package/src/tzero_fixer/synthetic_workspace_generator.py +220 -0
  65. package/src/tzero_fixer/system_diagnostics_extended.py +493 -0
  66. package/src/tzero_fixer/system_repair_recipes.py +447 -0
  67. package/src/tzero_fixer/venv_doctor.py +309 -0
  68. package/src/tzero_gateway.py +885 -0
  69. package/src/tzero_guard.py +243 -0
  70. package/src/tzero_infra.py +274 -0
  71. package/src/tzero_jarvis/__init__.py +124 -0
  72. package/src/tzero_jarvis/causal_graph.py +310 -0
  73. package/src/tzero_jarvis/graph_visualizer.py +395 -0
  74. package/src/tzero_jarvis/hybrid_search.py +214 -0
  75. package/src/tzero_jarvis/latent_press.py +137 -0
  76. package/src/tzero_jarvis/memory_consolidation.py +212 -0
  77. package/src/tzero_jarvis/minhash_lsh.py +184 -0
  78. package/src/tzero_jarvis/orchestrator.py +335 -0
  79. package/src/tzero_jarvis/self_healing.py +308 -0
  80. package/src/tzero_jarvis/semantic_compressor.py +887 -0
  81. package/src/tzero_jarvis/swarm.py +261 -0
  82. package/src/tzero_jarvis/swarm_bft.py +144 -0
  83. package/src/tzero_jarvis/workspace_orchestrator.py +1167 -0
  84. package/src/tzero_jarvis/zero_key_router.py +173 -0
  85. package/src/tzero_mcp.py +4858 -0
  86. package/src/tzero_neural_cortex.py +1425 -0
  87. package/src/tzero_packer.py +370 -0
  88. package/src/tzero_patch.py +624 -0
  89. package/src/tzero_pc_fixer.py +12657 -0
  90. package/src/tzero_pipe.py +95 -0
  91. package/src/tzero_polyglot.py +422 -0
  92. package/src/tzero_reachability.py +272 -0
  93. package/src/tzero_replay.py +141 -0
  94. package/src/tzero_security.py +570 -0
  95. package/src/tzero_snapshot.py +258 -0
  96. package/src/tzero_stubs.py +313 -0
  97. package/src/tzero_superdesign.py +774 -0
  98. package/src/tzero_sync.py +241 -0
  99. package/src/tzero_task_benchmark.py +363 -0
  100. package/src/tzero_tui.py +244 -0
  101. package/src/tzero_v3.py +9435 -0
@@ -0,0 +1,364 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ T-Zero Polyglot Build, Container, & Infrastructure Healer
4
+ ========================================================
5
+ Specialized AST & syntax healer for cross-language build tools, Dockerfiles,
6
+ Makefiles, container configurations, web server rules, and multi-ecosystem manifests.
7
+
8
+ Key Capabilities:
9
+ - Dockerfile best-practice auditor & .dockerignore generator
10
+ - Makefile tab-vs-space indentation healer (eliminates 'missing separator' errors)
11
+ - Makefile .PHONY target generator
12
+ - Nginx / Caddy reverse proxy syntax and semicolon validator
13
+ - Rust Cargo.toml & Go go.mod basic syntax integrity checkers
14
+
15
+ Author: Siber Akademi T-Zero R&D Team
16
+ Version: 10.5.0 | 100% Free & Local Offline | Zero-Leak Certified
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import re
23
+ from dataclasses import dataclass
24
+ from pathlib import Path
25
+ from typing import Any, Dict, List, Optional, Tuple
26
+
27
+ from tzero_fixer.models import (
28
+ DiagnosticCategory,
29
+ DiagnosticIssue,
30
+ HealAction,
31
+ SeverityLevel,
32
+ )
33
+
34
+
35
+ class PolyglotBuildHealer:
36
+ """Surgeon for polyglot build scripts, container files, and infrastructure recipes."""
37
+
38
+ @classmethod
39
+ def diagnose_workspace(cls, workspace_path: str) -> List[DiagnosticIssue]:
40
+ """Audit build and container files across the workspace."""
41
+ issues: List[DiagnosticIssue] = []
42
+ root = Path(workspace_path).resolve()
43
+
44
+ # 1. Dockerfile check
45
+ for dockerfile in root.glob("**/Dockerfile*"):
46
+ if dockerfile.is_file() and not cls._is_ignored(dockerfile):
47
+ issues.extend(cls._audit_dockerfile(dockerfile, root))
48
+
49
+ # 2. Makefile check
50
+ for makefile in root.glob("**/Makefile*"):
51
+ if makefile.is_file() and not cls._is_ignored(makefile):
52
+ issues.extend(cls._audit_makefile(makefile))
53
+
54
+ # 3. Nginx / Caddy conf check
55
+ for conf in root.glob("**/*.conf"):
56
+ if conf.is_file() and not cls._is_ignored(conf):
57
+ issues.extend(cls._audit_webserver_conf(conf))
58
+
59
+ # 4. Cargo.toml check
60
+ for cargo in root.glob("**/Cargo.toml"):
61
+ if cargo.is_file() and not cls._is_ignored(cargo):
62
+ issues.extend(cls._audit_cargo_toml(cargo))
63
+
64
+ # 5. go.mod check
65
+ for gopkg in root.glob("**/go.mod"):
66
+ if gopkg.is_file() and not cls._is_ignored(gopkg):
67
+ issues.extend(cls._audit_go_mod(gopkg))
68
+
69
+ return issues
70
+
71
+ @classmethod
72
+ def _is_ignored(cls, p: Path) -> bool:
73
+ """Check if path is inside ignored VCS, venv, or build folders."""
74
+ parts = p.parts
75
+ ignored_names = {".git", ".venv", "venv", "node_modules", "dist", "build", ".tox", "__pycache__"}
76
+ return any(part in ignored_names for part in parts)
77
+
78
+ @classmethod
79
+ def _audit_dockerfile(cls, dockerfile: Path, root: Path) -> List[DiagnosticIssue]:
80
+ """Audit Dockerfile for best practices and check for .dockerignore."""
81
+ issues: List[DiagnosticIssue] = []
82
+ try:
83
+ content = dockerfile.read_text(encoding="utf-8", errors="replace")
84
+ lines = content.splitlines()
85
+ except Exception:
86
+ return issues
87
+
88
+ # Check for missing .dockerignore in directory
89
+ dockerignore = dockerfile.parent / ".dockerignore"
90
+ if not dockerignore.is_file():
91
+ issues.append(DiagnosticIssue(
92
+ category=DiagnosticCategory.WORKSPACE,
93
+ severity=SeverityLevel.WARNING,
94
+ description=f"Dockerfile in '{dockerfile.parent.name}' lacks a '.dockerignore' file (unnecessary files like .git or .venv will bloat context)",
95
+ file_path=str(dockerfile),
96
+ suggested_fix="Generate optimized .dockerignore to exclude git, venv, cache, and build artifacts",
97
+ metadata={"issue_type": "missing_dockerignore", "target_dir": str(dockerfile.parent)},
98
+ ))
99
+
100
+ for idx, line in enumerate(lines, start=1):
101
+ stripped = line.strip()
102
+
103
+ # FROM :latest warning
104
+ if stripped.startswith("FROM ") and ":latest" in stripped:
105
+ issues.append(DiagnosticIssue(
106
+ category=DiagnosticCategory.WORKSPACE,
107
+ severity=SeverityLevel.INFO,
108
+ description=f"Dockerfile uses unpinned ':latest' base image: '{stripped}'",
109
+ file_path=str(dockerfile),
110
+ line_number=idx,
111
+ suggested_fix="Pin base image to specific digest or version tag for deterministic builds",
112
+ metadata={"issue_type": "unpinned_docker_image"},
113
+ ))
114
+
115
+ # ADD instead of COPY
116
+ if stripped.startswith("ADD ") and not ("://" in stripped or stripped.endswith(".tar.gz") or stripped.endswith(".tgz")):
117
+ issues.append(DiagnosticIssue(
118
+ category=DiagnosticCategory.WORKSPACE,
119
+ severity=SeverityLevel.INFO,
120
+ description=f"Using 'ADD' instead of 'COPY' for simple local file copy: '{stripped}'",
121
+ file_path=str(dockerfile),
122
+ line_number=idx,
123
+ suggested_fix="Replace 'ADD' with 'COPY' for local files as per Dockerfile best practices",
124
+ metadata={"issue_type": "docker_add_to_copy", "line_num": idx},
125
+ ))
126
+
127
+ # apt-get install without cleanup
128
+ if "apt-get install" in stripped and "rm -rf /var/lib/apt/lists" not in stripped and not stripped.endswith("\\"):
129
+ issues.append(DiagnosticIssue(
130
+ category=DiagnosticCategory.WORKSPACE,
131
+ severity=SeverityLevel.WARNING,
132
+ description=f"apt-get install without cache cleanup in same RUN layer: '{stripped}'",
133
+ file_path=str(dockerfile),
134
+ line_number=idx,
135
+ suggested_fix="Append '&& rm -rf /var/lib/apt/lists/*' to reduce container image footprint",
136
+ metadata={"issue_type": "docker_apt_cleanup"},
137
+ ))
138
+
139
+ return issues
140
+
141
+ @classmethod
142
+ def _audit_makefile(cls, makefile: Path) -> List[DiagnosticIssue]:
143
+ """Audit Makefile for space vs tab indentation and missing .PHONY declarations."""
144
+ issues: List[DiagnosticIssue] = []
145
+ try:
146
+ content = makefile.read_text(encoding="utf-8", errors="replace")
147
+ lines = content.splitlines()
148
+ except Exception:
149
+ return issues
150
+
151
+ in_rule = False
152
+ space_indented_lines: List[int] = []
153
+ targets: List[str] = []
154
+
155
+ for idx, line in enumerate(lines, start=1):
156
+ if not line.strip() or line.strip().startswith("#"):
157
+ continue
158
+
159
+ # Detect target rule
160
+ target_match = re.match(r"^([a-zA-Z0-9_\-\.]+)\s*:\s*(.*)$", line)
161
+ if target_match:
162
+ in_rule = True
163
+ tgt = target_match.group(1)
164
+ if not tgt.startswith("."):
165
+ targets.append(tgt)
166
+ continue
167
+
168
+ # Recipe lines must start with TAB, not spaces
169
+ if in_rule:
170
+ if line.startswith(" ") or (line.startswith(" ") and not line.startswith("\t")):
171
+ space_indented_lines.append(idx)
172
+
173
+ if space_indented_lines:
174
+ issues.append(DiagnosticIssue(
175
+ category=DiagnosticCategory.SYNTAX,
176
+ severity=SeverityLevel.ERROR,
177
+ description=f"Makefile contains {len(space_indented_lines)} recipe line(s) indented with spaces instead of tabs (causes '*** missing separator' error)",
178
+ file_path=str(makefile),
179
+ line_number=space_indented_lines[0],
180
+ suggested_fix="Convert leading spaces on recipe lines to real tab (\\t) characters",
181
+ metadata={"issue_type": "makefile_spaces_to_tabs", "lines": space_indented_lines},
182
+ ))
183
+
184
+ if targets and ".PHONY:" not in content:
185
+ issues.append(DiagnosticIssue(
186
+ category=DiagnosticCategory.WORKSPACE,
187
+ severity=SeverityLevel.INFO,
188
+ description="Makefile declares targets without '.PHONY' directive (can conflict with files named after targets)",
189
+ file_path=str(makefile),
190
+ suggested_fix=f"Declare targets as .PHONY: {', '.join(targets[:5])}",
191
+ metadata={"issue_type": "makefile_missing_phony", "targets": targets},
192
+ ))
193
+
194
+ return issues
195
+
196
+ @classmethod
197
+ def _audit_webserver_conf(cls, conf_file: Path) -> List[DiagnosticIssue]:
198
+ """Audit Nginx / reverse proxy config files for missing semicolons."""
199
+ issues: List[DiagnosticIssue] = []
200
+ try:
201
+ content = conf_file.read_text(encoding="utf-8", errors="replace")
202
+ lines = content.splitlines()
203
+ except Exception:
204
+ return issues
205
+
206
+ # Only audit if it looks like an nginx/caddy configuration
207
+ if "server {" not in content and "http {" not in content and "location " not in content:
208
+ return issues
209
+
210
+ for idx, line in enumerate(lines, start=1):
211
+ stripped = line.strip()
212
+ if not stripped or stripped.startswith("#") or stripped.endswith("{") or stripped.endswith("}"):
213
+ continue
214
+ if not stripped.endswith(";"):
215
+ issues.append(DiagnosticIssue(
216
+ category=DiagnosticCategory.SYNTAX,
217
+ severity=SeverityLevel.ERROR,
218
+ description=f"Missing terminating semicolon in Nginx directive: '{stripped}'",
219
+ file_path=str(conf_file),
220
+ line_number=idx,
221
+ suggested_fix="Append ';' to directive line",
222
+ metadata={"issue_type": "nginx_missing_semicolon", "line_num": idx},
223
+ ))
224
+
225
+ return issues
226
+
227
+ @classmethod
228
+ def _audit_cargo_toml(cls, cargo_file: Path) -> List[DiagnosticIssue]:
229
+ """Audit Rust Cargo.toml for basic sections."""
230
+ issues: List[DiagnosticIssue] = []
231
+ try:
232
+ content = cargo_file.read_text(encoding="utf-8", errors="replace")
233
+ except Exception:
234
+ return issues
235
+
236
+ if "[package]" not in content and "[workspace]" not in content:
237
+ issues.append(DiagnosticIssue(
238
+ category=DiagnosticCategory.DEPENDENCY,
239
+ severity=SeverityLevel.ERROR,
240
+ description="Cargo.toml is missing mandatory '[package]' or '[workspace]' header",
241
+ file_path=str(cargo_file),
242
+ suggested_fix="Add '[package]' table with name, version, and edition = '2021'",
243
+ metadata={"issue_type": "cargo_missing_package"},
244
+ ))
245
+
246
+ return issues
247
+
248
+ @classmethod
249
+ def _audit_go_mod(cls, go_mod_file: Path) -> List[DiagnosticIssue]:
250
+ """Audit Go go.mod for module declaration."""
251
+ issues: List[DiagnosticIssue] = []
252
+ try:
253
+ content = go_mod_file.read_text(encoding="utf-8", errors="replace")
254
+ except Exception:
255
+ return issues
256
+
257
+ if not re.search(r"^module\s+\S+", content, re.MULTILINE):
258
+ issues.append(DiagnosticIssue(
259
+ category=DiagnosticCategory.DEPENDENCY,
260
+ severity=SeverityLevel.ERROR,
261
+ description="go.mod is missing mandatory 'module <path>' declaration",
262
+ file_path=str(go_mod_file),
263
+ suggested_fix="Declare module path on first line of go.mod",
264
+ metadata={"issue_type": "go_missing_module"},
265
+ ))
266
+
267
+ return issues
268
+
269
+ @classmethod
270
+ def heal_issue(cls, issue: DiagnosticIssue, dry_run: bool = False) -> HealAction:
271
+ """Autonomously heal polyglot build and container issues."""
272
+ itype = issue.metadata.get("issue_type")
273
+ file_path = issue.file_path
274
+
275
+ if itype == "missing_dockerignore":
276
+ target_dir = Path(issue.metadata.get("target_dir", "."))
277
+ dignore = target_dir / ".dockerignore"
278
+ if dry_run:
279
+ return HealAction(issue=issue, success=True, details=f"[Dry-Run] Would create .dockerignore in {target_dir}")
280
+
281
+ ignore_content = (
282
+ "# T-Zero Autonomous Dockerignore\n"
283
+ ".git\n"
284
+ ".gitignore\n"
285
+ ".venv\n"
286
+ "venv/\n"
287
+ "env/\n"
288
+ "node_modules/\n"
289
+ "__pycache__/\n"
290
+ "*.pyc\n"
291
+ "*.pyo\n"
292
+ "dist/\n"
293
+ "build/\n"
294
+ "*.egg-info/\n"
295
+ ".DS_Store\n"
296
+ "Thumbs.db\n"
297
+ ".env\n"
298
+ ".env.local\n"
299
+ "coverage/\n"
300
+ ".pytest_cache/\n"
301
+ )
302
+ dignore.write_text(ignore_content, encoding="utf-8")
303
+ return HealAction(issue=issue, success=True, details=f"Generated high-performance .dockerignore in '{target_dir.name}'")
304
+
305
+ if itype == "makefile_spaces_to_tabs" and file_path:
306
+ p = Path(file_path)
307
+ if dry_run:
308
+ return HealAction(issue=issue, success=True, details="[Dry-Run] Would convert recipe spaces to tabs in Makefile")
309
+
310
+ lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
311
+ new_lines = []
312
+ converted = 0
313
+ in_rule = False
314
+
315
+ for line in lines:
316
+ if not line.strip() or line.strip().startswith("#"):
317
+ new_lines.append(line)
318
+ continue
319
+
320
+ if re.match(r"^[a-zA-Z0-9_\-\.]+\s*:\s*.*$", line):
321
+ in_rule = True
322
+ new_lines.append(line)
323
+ continue
324
+
325
+ if in_rule:
326
+ if line.startswith(" "):
327
+ new_lines.append("\t" + line[4:])
328
+ converted += 1
329
+ continue
330
+ elif line.startswith(" "):
331
+ new_lines.append("\t" + line.lstrip(" "))
332
+ converted += 1
333
+ continue
334
+
335
+ new_lines.append(line)
336
+
337
+ p.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
338
+ return HealAction(issue=issue, success=True, details=f"Converted {converted} recipe lines from spaces to real tabs in Makefile")
339
+
340
+ if itype == "makefile_missing_phony" and file_path:
341
+ p = Path(file_path)
342
+ targets = issue.metadata.get("targets", [])
343
+ if dry_run:
344
+ return HealAction(issue=issue, success=True, details=f"[Dry-Run] Would prepend .PHONY: {' '.join(targets)} to Makefile")
345
+
346
+ content = p.read_text(encoding="utf-8", errors="replace")
347
+ phony_line = f".PHONY: {' '.join(targets)}\n\n"
348
+ p.write_text(phony_line + content, encoding="utf-8")
349
+ return HealAction(issue=issue, success=True, details=f"Injected .PHONY target declarations into Makefile")
350
+
351
+ if itype == "docker_add_to_copy" and file_path:
352
+ p = Path(file_path)
353
+ line_num = issue.metadata.get("line_num")
354
+ if dry_run:
355
+ return HealAction(issue=issue, success=True, details=f"[Dry-Run] Would replace ADD with COPY on line {line_num}")
356
+
357
+ lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
358
+ if line_num and 1 <= line_num <= len(lines):
359
+ lines[line_num - 1] = re.sub(r"^ADD\s+", "COPY ", lines[line_num - 1])
360
+ p.write_text("\n".join(lines) + "\n", encoding="utf-8")
361
+ return HealAction(issue=issue, success=True, details=f"Replaced 'ADD' with 'COPY' on line {line_num} of Dockerfile")
362
+
363
+ return HealAction(issue=issue, success=False, details=f"No automated repair for issue type '{itype}'")
364
+
@@ -0,0 +1,318 @@
1
+ # -*- coding: utf-8 -*-
2
+ """T-Zero Polyglot Configuration & Environment Files Healer.
3
+
4
+ Performs forensic repair and hygiene checks on configuration files:
5
+ - JSON repair engine (strips trailing commas, single quotes, unquoted keys, and JS comments).
6
+ - Gitignore & Security hygiene (verifies .env, *.pem, *.key are in .gitignore).
7
+ - Gitattributes LF/CRLF normalizer (* text=auto eol=lf).
8
+ - EditorConfig generator and validator.
9
+
10
+ Author: Toprak Ahmet Aydogmus (Siber Akademi)
11
+ Version: 10.4.0
12
+ """
13
+
14
+ import difflib
15
+ import json
16
+ import os
17
+ import re
18
+ import time
19
+ from dataclasses import dataclass
20
+ from pathlib import Path
21
+ from typing import Any, Dict, List, Optional, Set, Tuple
22
+
23
+ from .models import (
24
+ DiagnosticCategory,
25
+ DiagnosticIssue,
26
+ HealAction,
27
+ SeverityLevel,
28
+ )
29
+
30
+
31
+ class PolyglotConfigHealer:
32
+ """Specialized repair engine for JSON, YAML, Gitignore, and Gitattributes files."""
33
+
34
+ SENSITIVE_GITIGNORE_ENTRIES = [
35
+ ".env",
36
+ ".env.local",
37
+ "*.pem",
38
+ "*.key",
39
+ "*.pfx",
40
+ "*.p12",
41
+ "id_rsa",
42
+ "id_ed25519",
43
+ ".tzero/fixer_backups/",
44
+ ]
45
+
46
+ # -------------------------------------------------------------------------
47
+ # 1. JSON SURGICAL REPAIR ENGINE
48
+ # -------------------------------------------------------------------------
49
+ @classmethod
50
+ def heal_json_string(cls, content: str) -> Any:
51
+ """Helper to repair trailing commas and single quotes in JSON strings."""
52
+ @dataclass
53
+ class JsonHealResult:
54
+ original_content: str
55
+ repaired_content: str
56
+ changed: bool
57
+
58
+ changed, repaired = cls.repair_json_text(content)
59
+ return JsonHealResult(
60
+ original_content=content,
61
+ repaired_content=repaired,
62
+ changed=changed,
63
+ )
64
+
65
+ @classmethod
66
+ def repair_json_text(cls, text: str) -> Tuple[bool, str]:
67
+ """Surgically repair trailing commas, single quotes, and comments in JSON."""
68
+ repaired = text
69
+
70
+ # Strip line comments // ...
71
+ repaired = re.sub(r"^\s*//.*$", "", repaired, flags=re.MULTILINE)
72
+ # Strip block comments /* ... */
73
+ repaired = re.sub(r"/\*.*?\*/", "", repaired, flags=re.DOTALL)
74
+
75
+ # Convert single quotes to double quotes (around keys/values)
76
+ repaired = re.sub(r"\'([^\'\\]*)\'", r'"\1"', repaired)
77
+
78
+ # Strip trailing commas in objects: , } -> }
79
+ repaired = re.sub(r",\s*(\})", r"\1", repaired)
80
+ # Strip trailing commas in arrays: , ] -> ]
81
+ repaired = re.sub(r",\s*(\])", r"\1", repaired)
82
+
83
+ try:
84
+ json.loads(repaired)
85
+ return True, repaired
86
+ except Exception:
87
+ return False, text
88
+
89
+ @classmethod
90
+ def check_json_files(cls, workspace_dir: str = ".") -> List[DiagnosticIssue]:
91
+ """Inspect all JSON files in workspace for syntax errors and trailing commas."""
92
+ issues: List[DiagnosticIssue] = []
93
+ root = Path(workspace_dir).resolve()
94
+
95
+ for jf in root.glob("**/*.json"):
96
+ if not jf.is_file() or any(p in jf.parts for p in (".git", "node_modules", ".venv", "venv", "dist", "build")):
97
+ continue
98
+
99
+ try:
100
+ with open(jf, "r", encoding="utf-8") as f:
101
+ content = f.read()
102
+ json.loads(content)
103
+ except json.JSONDecodeError as jde:
104
+ can_heal, _ = cls.repair_json_text(content)
105
+ issues.append(DiagnosticIssue(
106
+ category=DiagnosticCategory.POLYGLOT.value,
107
+ severity=SeverityLevel.ERROR.value,
108
+ file_path=str(jf),
109
+ line=jde.lineno,
110
+ column=jde.colno,
111
+ error_code="TZ-JSON-SYNTAX-ERROR",
112
+ message=f"JSON syntax error in {jf.name}: {jde.msg}",
113
+ suggested_fix="Surgically heal trailing commas, comments, and quotes" if can_heal else "Fix JSON structure",
114
+ auto_fixable=can_heal,
115
+ metadata={"action": "repair_json_file", "path": str(jf)}
116
+ ))
117
+ except Exception:
118
+ continue
119
+
120
+ return issues
121
+
122
+ # -------------------------------------------------------------------------
123
+ # 2. GITIGNORE HYGIENE DOCTOR
124
+ # -------------------------------------------------------------------------
125
+ @classmethod
126
+ def check_gitignore_hygiene(cls, workspace_dir: str = ".") -> List[DiagnosticIssue]:
127
+ """Verify .gitignore exists and contains critical secret patterns (.env, *.key)."""
128
+ issues: List[DiagnosticIssue] = []
129
+ root = Path(workspace_dir).resolve()
130
+ gi_file = root / ".gitignore"
131
+
132
+ if not gi_file.is_file():
133
+ issues.append(DiagnosticIssue(
134
+ category=DiagnosticCategory.SECURITY.value,
135
+ severity=SeverityLevel.ERROR.value,
136
+ file_path=str(root),
137
+ line=None,
138
+ column=None,
139
+ error_code="TZ-GIT-MISSING-GITIGNORE",
140
+ message="No '.gitignore' file found in workspace root (risk of committing secrets)",
141
+ suggested_fix="Generate comprehensive .gitignore with .env and secret patterns",
142
+ auto_fixable=True,
143
+ metadata={"action": "create_gitignore", "path": str(gi_file)}
144
+ ))
145
+ return issues
146
+
147
+ try:
148
+ with open(gi_file, "r", encoding="utf-8", errors="replace") as f:
149
+ content = f.read()
150
+ lines = set(line.strip() for line in content.splitlines() if line.strip())
151
+
152
+ missing_entries = []
153
+ for entry in cls.SENSITIVE_GITIGNORE_ENTRIES:
154
+ if entry not in lines and not any(l.startswith(entry) for l in lines):
155
+ missing_entries.append(entry)
156
+
157
+ if missing_entries:
158
+ issues.append(DiagnosticIssue(
159
+ category=DiagnosticCategory.SECURITY.value,
160
+ severity=SeverityLevel.WARNING.value,
161
+ file_path=str(gi_file),
162
+ line=1,
163
+ column=0,
164
+ error_code="TZ-GIT-UNPROTECTED-SECRETS",
165
+ message=f".gitignore is missing critical secret patterns: {', '.join(missing_entries[:4])}",
166
+ suggested_fix=f"Append {', '.join(missing_entries)} to .gitignore",
167
+ auto_fixable=True,
168
+ metadata={"action": "append_gitignore_entries", "entries": missing_entries, "path": str(gi_file)}
169
+ ))
170
+ except Exception:
171
+ pass
172
+
173
+ return issues
174
+
175
+ # -------------------------------------------------------------------------
176
+ # 3. GITATTRIBUTES LF/CRLF NORMALIZER
177
+ # -------------------------------------------------------------------------
178
+ @classmethod
179
+ def check_gitattributes(cls, workspace_dir: str = ".") -> List[DiagnosticIssue]:
180
+ """Verify .gitattributes exists to prevent cross-platform CRLF/LF chaos."""
181
+ issues: List[DiagnosticIssue] = []
182
+ root = Path(workspace_dir).resolve()
183
+ ga_file = root / ".gitattributes"
184
+
185
+ if not ga_file.is_file():
186
+ issues.append(DiagnosticIssue(
187
+ category=DiagnosticCategory.ENVIRONMENT.value,
188
+ severity=SeverityLevel.INFO.value,
189
+ file_path=str(ga_file),
190
+ line=None,
191
+ column=None,
192
+ error_code="TZ-GIT-MISSING-GITATTRIBUTES",
193
+ message="No '.gitattributes' found; cross-platform line endings (CRLF vs LF) may drift",
194
+ suggested_fix="Generate standard .gitattributes ('* text=auto eol=lf')",
195
+ auto_fixable=True,
196
+ metadata={"action": "create_gitattributes", "path": str(ga_file)}
197
+ ))
198
+
199
+ return issues
200
+
201
+ @classmethod
202
+ def diagnose_workspace(cls, workspace_dir: str = ".") -> List[DiagnosticIssue]:
203
+ """Run all polyglot configuration and git hygiene audits."""
204
+ issues: List[DiagnosticIssue] = []
205
+ issues.extend(cls.check_json_files(workspace_dir))
206
+ issues.extend(cls.check_gitignore_hygiene(workspace_dir))
207
+ issues.extend(cls.check_gitattributes(workspace_dir))
208
+ return issues
209
+
210
+ @classmethod
211
+ def execute_config_heals(cls, issues: List[DiagnosticIssue], dry_run: bool = False) -> List[HealAction]:
212
+ """Execute repairs for JSON files, .gitignore, and .gitattributes."""
213
+ actions: List[HealAction] = []
214
+
215
+ for issue in issues:
216
+ action_type = issue.metadata.get("action")
217
+ file_p = issue.metadata.get("path")
218
+ if not issue.auto_fixable or not action_type or not file_p:
219
+ continue
220
+
221
+ t0 = time.time()
222
+ p = Path(file_p)
223
+
224
+ try:
225
+ # 1. Repair JSON file
226
+ if action_type == "repair_json_file" and p.is_file():
227
+ with open(p, "r", encoding="utf-8", errors="replace") as f:
228
+ content = f.read()
229
+ success, rep = cls.repair_json_text(content)
230
+ if success and rep != content:
231
+ diff = "".join(difflib.unified_diff(
232
+ content.splitlines(keepends=True),
233
+ rep.splitlines(keepends=True),
234
+ fromfile="before.json",
235
+ tofile="after.json"
236
+ ))
237
+ if not dry_run:
238
+ with open(p, "w", encoding="utf-8", newline="\n") as f:
239
+ f.write(rep)
240
+ actions.append(HealAction(
241
+ category=DiagnosticCategory.POLYGLOT.value,
242
+ action_type=action_type,
243
+ file_path=file_p,
244
+ description=f"Surgically repaired JSON syntax in {p.name}",
245
+ success=True,
246
+ diff=diff,
247
+ duration_ms=round((time.time() - t0) * 1000, 2)
248
+ ))
249
+
250
+ # 2. Create .gitignore
251
+ elif action_type == "create_gitignore":
252
+ entries = ["# T-Zero Autonomous Security Gitignore", "__pycache__/", "*.pyc", ".venv/", "venv/", "node_modules/", "dist/", "build/"] + cls.SENSITIVE_GITIGNORE_ENTRIES
253
+ content = "\n".join(entries) + "\n"
254
+ if not dry_run:
255
+ with open(p, "w", encoding="utf-8", newline="\n") as f:
256
+ f.write(content)
257
+ actions.append(HealAction(
258
+ category=DiagnosticCategory.SECURITY.value,
259
+ action_type=action_type,
260
+ file_path=file_p,
261
+ description="Generated secure .gitignore protecting .env and key files",
262
+ success=True,
263
+ duration_ms=round((time.time() - t0) * 1000, 2)
264
+ ))
265
+
266
+ # 3. Append to .gitignore
267
+ elif action_type == "append_gitignore_entries":
268
+ missing = issue.metadata.get("entries", [])
269
+ if missing:
270
+ append_str = "\n# T-Zero Security Guard Protections\n" + "\n".join(missing) + "\n"
271
+ if not dry_run:
272
+ with open(p, "a", encoding="utf-8") as f:
273
+ f.write(append_str)
274
+ actions.append(HealAction(
275
+ category=DiagnosticCategory.SECURITY.value,
276
+ action_type=action_type,
277
+ file_path=file_p,
278
+ description=f"Appended {len(missing)} security patterns ({', '.join(missing[:3])}) to .gitignore",
279
+ success=True,
280
+ duration_ms=round((time.time() - t0) * 1000, 2)
281
+ ))
282
+
283
+ # 4. Create .gitattributes
284
+ elif action_type == "create_gitattributes":
285
+ content = (
286
+ "# T-Zero Standard Gitattributes - Normalize LF Line Endings\n"
287
+ "* text=auto eol=lf\n"
288
+ "*.py text eol=lf\n"
289
+ "*.json text eol=lf\n"
290
+ "*.md text eol=lf\n"
291
+ "*.yml text eol=lf\n"
292
+ "*.yaml text eol=lf\n"
293
+ "*.sh text eol=lf\n"
294
+ "*.bat text eol=crlf\n"
295
+ "*.ps1 text eol=crlf\n"
296
+ )
297
+ if not dry_run:
298
+ with open(p, "w", encoding="utf-8", newline="\n") as f:
299
+ f.write(content)
300
+ actions.append(HealAction(
301
+ category=DiagnosticCategory.ENVIRONMENT.value,
302
+ action_type=action_type,
303
+ file_path=file_p,
304
+ description="Created .gitattributes to ensure cross-platform LF line ending consistency",
305
+ success=True,
306
+ duration_ms=round((time.time() - t0) * 1000, 2)
307
+ ))
308
+ except Exception as e:
309
+ actions.append(HealAction(
310
+ category=DiagnosticCategory.POLYGLOT.value,
311
+ action_type=action_type,
312
+ file_path=file_p,
313
+ description=f"Failed configuration remediation: {e}",
314
+ success=False,
315
+ duration_ms=round((time.time() - t0) * 1000, 2)
316
+ ))
317
+
318
+ return actions