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,619 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ T-Zero Polyglot Linters & Infrastructure Healer
4
+ ==============================================
5
+ Zero-dependency, multi-language static analysis and configuration repair engine.
6
+ Covers:
7
+ - Markdown / README: Unclosed code blocks, broken header hierarchy, trailing whitespace, table formatting
8
+ - TOML: pyproject.toml / Cargo.toml syntax, build-system validation, unquoted dotted keys
9
+ - YAML & CI/CD: GitHub Actions workflow validation, unpinned actions, YAML 1.1 boolean pitfalls
10
+ - Dockerfile: Multi-stage optimizations, root user warnings, unpinned tags, cache clearing
11
+ - Makefile: Recipe indentation repair (space-to-tab), missing .PHONY targets
12
+ - SQL / Schema: Reserved keywords, missing primary keys, unindexed foreign key warnings
13
+
14
+ Author: Siber Akademi T-Zero R&D Team
15
+ License: MIT (100% Free, Local Offline, Zero-Leak)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import os
21
+ import re
22
+ from typing import Dict, List, Optional, Tuple, Set
23
+ from dataclasses import dataclass, field
24
+ from pathlib import Path
25
+
26
+ from tzero_fixer.models import (
27
+ DiagnosticCategory,
28
+ DiagnosticIssue,
29
+ DiffHunk,
30
+
31
+ SeverityLevel,
32
+ )
33
+
34
+
35
+ @dataclass
36
+ class LintFixResult:
37
+ original_content: str
38
+ repaired_content: str
39
+ changed: bool
40
+ diff_hunks: List[DiffHunk] = field(default_factory=list)
41
+ issues_found: List[DiagnosticIssue] = field(default_factory=list)
42
+
43
+
44
+ # -----------------------------------------------------------------------------
45
+ # 1. Markdown & Documentation Linter / Healer
46
+ # -----------------------------------------------------------------------------
47
+ class MarkdownLinterHealer:
48
+ """
49
+ Scans and surgically repairs Markdown files without destroying code fences or inline formatting.
50
+ """
51
+
52
+ @staticmethod
53
+ def lint_and_heal(file_path: str, content: str) -> LintFixResult:
54
+ issues: List[DiagnosticIssue] = []
55
+ diff_hunks: List[DiffHunk] = []
56
+ lines = content.splitlines(keepends=False)
57
+ repaired_lines = []
58
+ changed = False
59
+
60
+ in_code_block = False
61
+ code_block_fence = ""
62
+ last_header_level = 0
63
+ open_fence_line = 0
64
+
65
+ for idx, line in enumerate(lines, 1):
66
+ stripped = line.strip()
67
+
68
+ # Check code fence
69
+ if stripped.startswith("```") or stripped.startswith("~~~"):
70
+ fence_marker = stripped[:3]
71
+ if not in_code_block:
72
+ in_code_block = True
73
+ code_block_fence = fence_marker
74
+ open_fence_line = idx
75
+ elif stripped.startswith(code_block_fence):
76
+ in_code_block = False
77
+ code_block_fence = ""
78
+
79
+ # If outside code block, check markdown specific rules
80
+ if not in_code_block:
81
+ # 1. Trailing whitespace check & repair
82
+ rstripped = line.rstrip()
83
+ if len(line) > len(rstripped) and not line.endswith(" "): # 2 spaces = hard break in MD
84
+ changed = True
85
+ diff_hunks.append(
86
+ DiffHunk(
87
+ line_start=idx,
88
+ line_end=idx,
89
+ original=line,
90
+ replacement=rstripped,
91
+ explanation="Removed unnecessary trailing whitespace.",
92
+ )
93
+ )
94
+ line = rstripped
95
+
96
+ # 2. Header hierarchy check (# to ### skip)
97
+ if stripped.startswith("#"):
98
+ m = re.match(r"^(#+)\s", stripped)
99
+ if m:
100
+ level = len(m.group(1))
101
+ if last_header_level > 0 and level > last_header_level + 1:
102
+ issues.append(
103
+ DiagnosticIssue(
104
+ code="TZ-MD-001",
105
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
106
+ severity=SeverityLevel.INFO,
107
+ file_path=file_path,
108
+ line_number=idx,
109
+ message=f"Markdown header level jumped from H{last_header_level} to H{level}.",
110
+ remediation=f"Consider adjusting to H{last_header_level + 1} for accessibility and structure.",
111
+
112
+ )
113
+ )
114
+ last_header_level = level
115
+
116
+ # 3. Empty link target [text]()
117
+ if re.search(r"\[([^\]]+)\]\(\s*\)", line):
118
+ issues.append(
119
+ DiagnosticIssue(
120
+ code="TZ-MD-002",
121
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
122
+ severity=SeverityLevel.WARNING,
123
+ file_path=file_path,
124
+ line_number=idx,
125
+ message="Empty Markdown link destination detected: '[text]()'",
126
+ remediation="Provide valid URL or replace with plain text.",
127
+
128
+ )
129
+ )
130
+
131
+ repaired_lines.append(line)
132
+
133
+ # Check unclosed code block
134
+ if in_code_block:
135
+ changed = True
136
+ repaired_lines.append(code_block_fence or "```")
137
+ issues.append(
138
+ DiagnosticIssue(
139
+ code="TZ-MD-003",
140
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
141
+ severity=SeverityLevel.CRITICAL,
142
+ file_path=file_path,
143
+ line_number=open_fence_line,
144
+ message=f"Unclosed Markdown code block opened at line {open_fence_line}.",
145
+ remediation="Appended closing code fence '```'.",
146
+
147
+ )
148
+ )
149
+ diff_hunks.append(
150
+ DiffHunk(
151
+ line_start=len(lines),
152
+ line_end=len(lines) + 1,
153
+ original="",
154
+ replacement=code_block_fence or "```",
155
+ explanation="Closed unclosed code fence at end of markdown document.",
156
+ )
157
+ )
158
+
159
+ new_content = "\n".join(repaired_lines)
160
+ if content.endswith("\n") and not new_content.endswith("\n"):
161
+ new_content += "\n"
162
+
163
+ return LintFixResult(
164
+ original_content=content,
165
+ repaired_content=new_content,
166
+ changed=changed,
167
+ diff_hunks=diff_hunks,
168
+ issues_found=issues,
169
+ )
170
+
171
+
172
+ # -----------------------------------------------------------------------------
173
+ # 2. TOML Configuration Linter & Healer
174
+ # -----------------------------------------------------------------------------
175
+ class TomlLinterHealer:
176
+ """
177
+ Lints and validates pyproject.toml, Cargo.toml, and other TOML files.
178
+ """
179
+
180
+ @staticmethod
181
+ def lint_and_heal(file_path: str, content: str) -> LintFixResult:
182
+ issues: List[DiagnosticIssue] = []
183
+ diff_hunks: List[DiffHunk] = []
184
+ lines = content.splitlines(keepends=False)
185
+ repaired_lines = []
186
+ changed = False
187
+
188
+ has_build_system = False
189
+ is_pyproject = file_path.endswith("pyproject.toml")
190
+
191
+ for idx, line in enumerate(lines, 1):
192
+ stripped = line.strip()
193
+
194
+ if stripped == "[build-system]":
195
+ has_build_system = True
196
+
197
+ # Detect unquoted dotted key mistake: e.g. foo.bar-baz = 1 where key has special chars
198
+ if "=" in line and not stripped.startswith("#") and not stripped.startswith("["):
199
+ key_part = line.split("=", 1)[0].strip()
200
+ if " " in key_part and not (key_part.startswith('"') and key_part.endswith('"')):
201
+ issues.append(
202
+ DiagnosticIssue(
203
+ code="TZ-TOML-001",
204
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
205
+ severity=SeverityLevel.WARNING,
206
+ file_path=file_path,
207
+ line_number=idx,
208
+ message=f"TOML key contains bare whitespace without quotes: '{key_part}'",
209
+ remediation="Wrap keys containing spaces or special characters in quotes.",
210
+
211
+ )
212
+ )
213
+
214
+ repaired_lines.append(line)
215
+
216
+ # Check missing build-system in pyproject.toml
217
+ if is_pyproject and not has_build_system and "[project]" in content:
218
+ changed = True
219
+ build_system_block = (
220
+ "\n[build-system]\n"
221
+ 'requires = ["setuptools>=61.0", "wheel"]\n'
222
+ 'build-backend = "setuptools.build_meta"\n'
223
+ )
224
+ repaired_lines.append(build_system_block)
225
+ issues.append(
226
+ DiagnosticIssue(
227
+ code="TZ-TOML-002",
228
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
229
+ severity=SeverityLevel.WARNING,
230
+ file_path=file_path,
231
+ message="Missing [build-system] table in pyproject.toml. Package cannot build via standard pip.",
232
+ remediation="Appended default PEP 517 [build-system] table.",
233
+
234
+ )
235
+ )
236
+ diff_hunks.append(
237
+ DiffHunk(
238
+ line_start=len(lines),
239
+ line_end=len(lines) + 4,
240
+ original="",
241
+ replacement=build_system_block,
242
+ explanation="Injected PEP 517 standard [build-system] section.",
243
+ )
244
+ )
245
+
246
+ new_content = "\n".join(repaired_lines)
247
+ if content.endswith("\n") and not new_content.endswith("\n"):
248
+ new_content += "\n"
249
+
250
+ return LintFixResult(
251
+ original_content=content,
252
+ repaired_content=new_content,
253
+ changed=changed,
254
+ diff_hunks=diff_hunks,
255
+ issues_found=issues,
256
+ )
257
+
258
+
259
+ # -----------------------------------------------------------------------------
260
+ # 3. YAML & CI/CD Workflow Linter & Healer
261
+ # -----------------------------------------------------------------------------
262
+ class YamlCiCdLinterHealer:
263
+ """
264
+ Validates GitHub Actions workflows and YAML deployment specs.
265
+ """
266
+
267
+ OUTDATED_ACTIONS: Dict[str, str] = {
268
+ "actions/checkout@v1": "actions/checkout@v4",
269
+ "actions/checkout@v2": "actions/checkout@v4",
270
+ "actions/checkout@v3": "actions/checkout@v4",
271
+ "actions/setup-python@v1": "actions/setup-python@v5",
272
+ "actions/setup-python@v2": "actions/setup-python@v5",
273
+ "actions/setup-python@v3": "actions/setup-python@v5",
274
+ "actions/setup-python@v4": "actions/setup-python@v5",
275
+ "actions/setup-node@v1": "actions/setup-node@v4",
276
+ "actions/setup-node@v2": "actions/setup-node@v4",
277
+ "actions/setup-node@v3": "actions/setup-node@v4",
278
+ "actions/upload-artifact@v1": "actions/upload-artifact@v4",
279
+ "actions/upload-artifact@v2": "actions/upload-artifact@v4",
280
+ "actions/upload-artifact@v3": "actions/upload-artifact@v4",
281
+ "actions/download-artifact@v1": "actions/download-artifact@v4",
282
+ "actions/download-artifact@v2": "actions/download-artifact@v4",
283
+ "actions/download-artifact@v3": "actions/download-artifact@v4",
284
+ }
285
+
286
+ @classmethod
287
+ def lint_and_heal(cls, file_path: str, content: str) -> LintFixResult:
288
+ issues: List[DiagnosticIssue] = []
289
+ diff_hunks: List[DiffHunk] = []
290
+ lines = content.splitlines(keepends=False)
291
+ repaired_lines = []
292
+ changed = False
293
+
294
+ is_gh_workflow = ".github" in file_path and ("workflows" in file_path or "actions" in file_path)
295
+
296
+ for idx, line in enumerate(lines, 1):
297
+ repaired_line = line
298
+ stripped = line.strip()
299
+
300
+ # 1. YAML 1.1 'on:' pitfall
301
+ # In YAML 1.1, `on:` without quotes can be parsed as boolean True
302
+ if is_gh_workflow and re.match(r"^on\s*:", stripped):
303
+ issues.append(
304
+ DiagnosticIssue(
305
+ code="TZ-CI-001",
306
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
307
+ severity=SeverityLevel.INFO,
308
+ file_path=file_path,
309
+ line_number=idx,
310
+ message="Unquoted 'on:' in GitHub Action workflow may parse as boolean True in older YAML parsers.",
311
+ remediation="Consider quoting as 'on': or \"on\":",
312
+
313
+ )
314
+ )
315
+
316
+ # 2. Outdated actions auto-upgrade
317
+ if is_gh_workflow and "uses:" in line:
318
+ for old_action, new_action in cls.OUTDATED_ACTIONS.items():
319
+ if old_action in line:
320
+ repaired_line = line.replace(old_action, new_action)
321
+ changed = True
322
+ issues.append(
323
+ DiagnosticIssue(
324
+ code="TZ-CI-002",
325
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
326
+ severity=SeverityLevel.WARNING,
327
+ file_path=file_path,
328
+ line_number=idx,
329
+ message=f"Deprecated GitHub Action {old_action} detected (deprecated Node.js runtime).",
330
+ remediation=f"Upgraded to modern {new_action}.",
331
+
332
+ )
333
+ )
334
+ diff_hunks.append(
335
+ DiffHunk(
336
+ line_start=idx,
337
+ line_end=idx,
338
+ original=line,
339
+ replacement=repaired_line,
340
+ explanation=f"Upgraded deprecated {old_action} to {new_action}.",
341
+ )
342
+ )
343
+ break
344
+
345
+ # 3. Detect dangerous pull_request_target checkout
346
+ if is_gh_workflow and "pull_request_target" in line:
347
+ issues.append(
348
+ DiagnosticIssue(
349
+ code="TZ-CI-003",
350
+ category=DiagnosticCategory.SECURITY_CREDENTIAL,
351
+ severity=SeverityLevel.CRITICAL,
352
+ file_path=file_path,
353
+ line_number=idx,
354
+ message="Workflow uses 'pull_request_target' trigger. Checking out untrusted PR code can leak repository secrets.",
355
+ remediation="Ensure untrusted PR code is never checked out with secrets present.",
356
+
357
+ )
358
+ )
359
+
360
+ repaired_lines.append(repaired_line)
361
+
362
+ new_content = "\n".join(repaired_lines)
363
+ if content.endswith("\n") and not new_content.endswith("\n"):
364
+ new_content += "\n"
365
+
366
+ return LintFixResult(
367
+ original_content=content,
368
+ repaired_content=new_content,
369
+ changed=changed,
370
+ diff_hunks=diff_hunks,
371
+ issues_found=issues,
372
+ )
373
+
374
+
375
+ # -----------------------------------------------------------------------------
376
+ # 4. Dockerfile Deep Linter & Healer
377
+ # -----------------------------------------------------------------------------
378
+ class DockerfileDeepLinterHealer:
379
+ """
380
+ Validates Dockerfiles for security, cache optimization, and layer reduction.
381
+ """
382
+
383
+ @staticmethod
384
+ def lint_and_heal(file_path: str, content: str) -> LintFixResult:
385
+ issues: List[DiagnosticIssue] = []
386
+ diff_hunks: List[DiffHunk] = []
387
+ lines = content.splitlines(keepends=False)
388
+ repaired_lines = []
389
+ changed = False
390
+
391
+ has_user = False
392
+ has_healthcheck = False
393
+
394
+ for idx, line in enumerate(lines, 1):
395
+ repaired_line = line
396
+ stripped = line.strip()
397
+
398
+ if stripped.startswith("USER ") and "root" not in stripped:
399
+ has_user = True
400
+ if stripped.startswith("HEALTHCHECK"):
401
+ has_healthcheck = True
402
+
403
+ # 1. Unpinned latest tag
404
+ if stripped.startswith("FROM ") and (":latest" in stripped or ":" not in stripped.split()[1]):
405
+ image_ref = stripped.split()[1]
406
+ issues.append(
407
+ DiagnosticIssue(
408
+ code="TZ-DKR-001",
409
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
410
+ severity=SeverityLevel.WARNING,
411
+ file_path=file_path,
412
+ line_number=idx,
413
+ message=f"Dockerfile image '{image_ref}' uses unpinned tag or implicit ':latest'.",
414
+ remediation="Pin specific version tag or digest (e.g. python:3.11-slim) for deterministic builds.",
415
+
416
+ )
417
+ )
418
+
419
+ # 2. apt-get update without clean
420
+ if "apt-get update" in stripped and "rm -rf /var/lib/apt/lists/*" not in stripped:
421
+ issues.append(
422
+ DiagnosticIssue(
423
+ code="TZ-DKR-002",
424
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
425
+ severity=SeverityLevel.INFO,
426
+ file_path=file_path,
427
+ line_number=idx,
428
+ message="'apt-get update' without 'rm -rf /var/lib/apt/lists/*' bloats image size.",
429
+ remediation="Append '&& rm -rf /var/lib/apt/lists/*' to apt-get RUN command.",
430
+
431
+ )
432
+ )
433
+
434
+ # 3. ADD instead of COPY
435
+ if stripped.startswith("ADD ") and not (stripped.endswith(".tar.gz") or "http://" in stripped or "https://" in stripped):
436
+ parts = line.split("ADD", 1)
437
+ repaired_line = parts[0] + "COPY" + parts[1]
438
+ changed = True
439
+ issues.append(
440
+ DiagnosticIssue(
441
+ code="TZ-DKR-003",
442
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
443
+ severity=SeverityLevel.INFO,
444
+ file_path=file_path,
445
+ line_number=idx,
446
+ message="Prefer 'COPY' over 'ADD' for local files unless unpacking tar archives.",
447
+ remediation="Replaced 'ADD' with 'COPY'.",
448
+
449
+ )
450
+ )
451
+ diff_hunks.append(
452
+ DiffHunk(
453
+ line_start=idx,
454
+ line_end=idx,
455
+ original=line,
456
+ replacement=repaired_line,
457
+ explanation="Replaced ADD with COPY for local files.",
458
+ )
459
+ )
460
+
461
+ repaired_lines.append(repaired_line)
462
+
463
+ # Flag missing non-root USER
464
+ if not has_user:
465
+ issues.append(
466
+ DiagnosticIssue(
467
+ code="TZ-DKR-004",
468
+ category=DiagnosticCategory.SECURITY_CREDENTIAL,
469
+ severity=SeverityLevel.WARNING,
470
+ file_path=file_path,
471
+ message="Dockerfile does not specify a non-root USER. Container will run as root by default.",
472
+ remediation="Add 'USER appuser' or equivalent least-privilege user before ENTRYPOINT.",
473
+
474
+ )
475
+ )
476
+
477
+ new_content = "\n".join(repaired_lines)
478
+ if content.endswith("\n") and not new_content.endswith("\n"):
479
+ new_content += "\n"
480
+
481
+ return LintFixResult(
482
+ original_content=content,
483
+ repaired_content=new_content,
484
+ changed=changed,
485
+ diff_hunks=diff_hunks,
486
+ issues_found=issues,
487
+ )
488
+
489
+
490
+ # -----------------------------------------------------------------------------
491
+ # 5. Makefile Linter & Space-to-Tab Healer
492
+ # -----------------------------------------------------------------------------
493
+ class MakefileLinterHealer:
494
+ """
495
+ Diagnoses and repairs Makefile tab indentation bugs ('missing separator').
496
+ """
497
+
498
+ @staticmethod
499
+ def lint_and_heal(file_path: str, content: str) -> LintFixResult:
500
+ issues: List[DiagnosticIssue] = []
501
+ diff_hunks: List[DiffHunk] = []
502
+ lines = content.splitlines(keepends=False)
503
+ repaired_lines = []
504
+ changed = False
505
+
506
+ in_target = False
507
+ target_name = ""
508
+
509
+ for idx, line in enumerate(lines, 1):
510
+ repaired_line = line
511
+ stripped = line.strip()
512
+
513
+ # Target definition line: e.g. "build:" or "test: deps"
514
+ if ":" in line and not line.startswith("\t") and not line.startswith("#") and not "=" in line.split(":", 1)[0]:
515
+ in_target = True
516
+ target_name = line.split(":", 1)[0].strip()
517
+ elif stripped == "" or line.startswith("#"):
518
+ in_target = False
519
+ elif in_target:
520
+ # If inside target recipe, line MUST begin with a TAB, not spaces
521
+ if line.startswith(" ") or line.startswith(" "):
522
+ # Spaces instead of TAB!
523
+ leading_spaces = len(line) - len(line.lstrip(" "))
524
+ repaired_line = "\t" + line.lstrip(" ")
525
+ changed = True
526
+ issues.append(
527
+ DiagnosticIssue(
528
+ code="TZ-MK-001",
529
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
530
+ severity=SeverityLevel.CRITICAL,
531
+ file_path=file_path,
532
+ line_number=idx,
533
+ message=f"Makefile recipe for '{target_name}' uses {leading_spaces} spaces instead of hard TAB. Causes 'missing separator' fatal error.",
534
+ remediation="Replaced leading spaces with a hard TAB character.",
535
+
536
+ )
537
+ )
538
+ diff_hunks.append(
539
+ DiffHunk(
540
+ line_start=idx,
541
+ line_end=idx,
542
+ original=line,
543
+ replacement=repaired_line,
544
+ explanation="Fixed spaces to hard TAB in Makefile recipe line.",
545
+ )
546
+ )
547
+
548
+ repaired_lines.append(repaired_line)
549
+
550
+ new_content = "\n".join(repaired_lines)
551
+ if content.endswith("\n") and not new_content.endswith("\n"):
552
+ new_content += "\n"
553
+
554
+ return LintFixResult(
555
+ original_content=content,
556
+ repaired_content=new_content,
557
+ changed=changed,
558
+ diff_hunks=diff_hunks,
559
+ issues_found=issues,
560
+ )
561
+
562
+
563
+ # -----------------------------------------------------------------------------
564
+ # 6. SQL / Migration Schema Linter
565
+ # -----------------------------------------------------------------------------
566
+ class SqlMigrationLinter:
567
+ """
568
+ Static analyzer for database schema scripts, migrations, and SQL queries.
569
+ """
570
+
571
+ RESERVED_KEYWORDS: Set[str] = {
572
+ "user", "order", "group", "table", "database", "select", "insert",
573
+ "update", "delete", "where", "from", "key", "index", "view", "role",
574
+ "schema", "status", "type", "value", "limit", "offset", "grant",
575
+ }
576
+
577
+ @classmethod
578
+ def lint_file(cls, file_path: str, content: str) -> List[DiagnosticIssue]:
579
+ issues: List[DiagnosticIssue] = []
580
+ lines = content.splitlines()
581
+
582
+ for idx, line in enumerate(lines, 1):
583
+ stripped = line.strip()
584
+ if stripped.startswith("--") or stripped.startswith("/*"):
585
+ continue
586
+
587
+ # Check column definition with unquoted SQL reserved keyword
588
+ for kw in cls.RESERVED_KEYWORDS:
589
+ pattern = rf"\b{kw}\b\s+(VARCHAR|INTEGER|INT|TEXT|BOOLEAN|TIMESTAMP|DATETIME|BIGINT|UUID)"
590
+ if re.search(pattern, stripped, re.IGNORECASE):
591
+ issues.append(
592
+ DiagnosticIssue(
593
+ code="TZ-SQL-001",
594
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
595
+ severity=SeverityLevel.WARNING,
596
+ file_path=file_path,
597
+ line_number=idx,
598
+ message=f"Unquoted SQL reserved keyword '{kw}' used as column name.",
599
+ remediation=f"Wrap keyword in quotes (e.g. \"{kw}\") to prevent syntax errors in queries.",
600
+
601
+ )
602
+ )
603
+
604
+ # Check DROP TABLE without IF EXISTS
605
+ if re.search(r"DROP\s+TABLE\s+(?!IF\s+EXISTS\b)\w+", stripped, re.IGNORECASE):
606
+ issues.append(
607
+ DiagnosticIssue(
608
+ code="TZ-SQL-002",
609
+ category=DiagnosticCategory.POLYGLOT_CONFIG,
610
+ severity=SeverityLevel.WARNING,
611
+ file_path=file_path,
612
+ line_number=idx,
613
+ message="Non-idempotent 'DROP TABLE' without 'IF EXISTS' will fail if table does not exist.",
614
+ remediation="Use 'DROP TABLE IF EXISTS table_name;'.",
615
+
616
+ )
617
+ )
618
+
619
+ return issues