autoai-codetrust 0.1.0__tar.gz

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.
@@ -0,0 +1,7 @@
1
+ node_modules/
2
+ dist/
3
+ build/
4
+ *.egg-info/
5
+ __pycache__/
6
+ .pytest_cache/
7
+ *.pyc
@@ -0,0 +1,24 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install dependencies first (cache layer)
6
+ COPY pyproject.toml README.md ./
7
+ COPY src/ src/
8
+ RUN pip install --no-cache-dir . && \
9
+ pip install --no-cache-dir gunicorn
10
+
11
+ # Create data directory for SQLite
12
+ RUN mkdir -p /app/data
13
+
14
+ ENV CODETRUST_DB_PATH=/app/data/codetrust.db
15
+ ENV CODETRUST_HOST=0.0.0.0
16
+ ENV CODETRUST_PORT=8080
17
+
18
+ EXPOSE 8080
19
+
20
+ CMD ["gunicorn", "aicodetrustcore.api:app", \
21
+ "-k", "uvicorn.workers.UvicornWorker", \
22
+ "-b", "0.0.0.0:8080", \
23
+ "--workers", "2", \
24
+ "--timeout", "120"]
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: autoai-codetrust
3
+ Version: 0.1.0
4
+ Summary: AI code security scanner — catches vulnerabilities, hallucinated dependencies, and compliance issues in AI-generated code. Snyk, but built for the AI coding era.
5
+ Project-URL: Homepage, https://codetrust.dev
6
+ Project-URL: Repository, https://github.com/autoailabadmin/codetrust
7
+ Project-URL: Documentation, https://codetrust.dev/docs
8
+ Author-email: AutoAI Labs <info@autoailabs.co.uk>
9
+ License: Apache-2.0
10
+ Keywords: ai-code,ai-scanner,claude,code-quality,code-security,copilot,cursor,hallucination-detection,mcp,sast,security,snyk-alternative,vulnerability-scanner
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: click>=8.0
22
+ Requires-Dist: fastapi>=0.110.0
23
+ Requires-Dist: mcp>=1.0.0
24
+ Requires-Dist: python-multipart>=0.0.9
25
+ Requires-Dist: rich>=13.0
26
+ Requires-Dist: uvicorn[standard]>=0.29.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
30
+ Requires-Dist: pytest>=7.0; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # AICodeTrustScore
34
+
35
+ Continuous trust scoring for AI-generated code. Analyzes code from Cursor, Copilot, Claude, and other AI tools, assigning a 0-100 trust score with hallucination detection.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install autoai-aicodetrustcore
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```bash
46
+ # Score a file
47
+ aicodetrustcore score path/to/file.py
48
+
49
+ # Score a git diff
50
+ aicodetrustcore diff
51
+
52
+ # Full report for a directory
53
+ aicodetrustcore report path/to/project/
54
+
55
+ # Check for AI hallucinations
56
+ aicodetrustcore hallucination-check path/to/file.py
57
+
58
+ # Start MCP server
59
+ aicodetrustcore serve
60
+ ```
61
+
62
+ ## License
63
+
64
+ Apache-2.0
@@ -0,0 +1,32 @@
1
+ # AICodeTrustScore
2
+
3
+ Continuous trust scoring for AI-generated code. Analyzes code from Cursor, Copilot, Claude, and other AI tools, assigning a 0-100 trust score with hallucination detection.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install autoai-aicodetrustcore
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ # Score a file
15
+ aicodetrustcore score path/to/file.py
16
+
17
+ # Score a git diff
18
+ aicodetrustcore diff
19
+
20
+ # Full report for a directory
21
+ aicodetrustcore report path/to/project/
22
+
23
+ # Check for AI hallucinations
24
+ aicodetrustcore hallucination-check path/to/file.py
25
+
26
+ # Start MCP server
27
+ aicodetrustcore serve
28
+ ```
29
+
30
+ ## License
31
+
32
+ Apache-2.0
Binary file
@@ -0,0 +1,144 @@
1
+ name: 'CodeTrust AI Code Scanner'
2
+ description: 'Scan AI-generated code for security vulnerabilities, hallucinated dependencies, and quality issues. Snyk, but built for the AI coding era.'
3
+ author: 'AutoAI Labs'
4
+
5
+ branding:
6
+ icon: 'shield'
7
+ color: 'blue'
8
+
9
+ inputs:
10
+ api-key:
11
+ description: 'CodeTrust API key (get one at codetrust.dev)'
12
+ required: false
13
+ default: ''
14
+ scan-mode:
15
+ description: 'Scan mode: "diff" (only changed files) or "full" (entire repo)'
16
+ required: false
17
+ default: 'diff'
18
+ fail-on:
19
+ description: 'Fail the check if score is below this threshold (0-100). Set to 0 to never fail.'
20
+ required: false
21
+ default: '50'
22
+ languages:
23
+ description: 'Comma-separated languages to scan (python,typescript,javascript,go). Default: all.'
24
+ required: false
25
+ default: 'all'
26
+ api-url:
27
+ description: 'CodeTrust API URL (for self-hosted installations)'
28
+ required: false
29
+ default: 'https://codetrust-api.autoailabs.co.uk'
30
+
31
+ outputs:
32
+ score:
33
+ description: 'Overall trust score (0-100)'
34
+ value: ${{ steps.scan.outputs.score }}
35
+ category:
36
+ description: 'Trust category: ship_it, review, needs_work, dont_ship'
37
+ value: ${{ steps.scan.outputs.category }}
38
+ total-issues:
39
+ description: 'Total number of issues found'
40
+ value: ${{ steps.scan.outputs.total_issues }}
41
+ critical-issues:
42
+ description: 'Number of critical issues'
43
+ value: ${{ steps.scan.outputs.critical_issues }}
44
+ report-url:
45
+ description: 'URL to the full report on codetrust.dev'
46
+ value: ${{ steps.scan.outputs.report_url }}
47
+
48
+ runs:
49
+ using: 'composite'
50
+ steps:
51
+ - name: Set up Python
52
+ uses: actions/setup-python@v5
53
+ with:
54
+ python-version: '3.12'
55
+
56
+ - name: Install CodeTrust
57
+ shell: bash
58
+ run: pip install codetrust --quiet
59
+
60
+ - name: Run CodeTrust Scan
61
+ id: scan
62
+ shell: bash
63
+ env:
64
+ CODETRUST_API_KEY: ${{ inputs.api-key }}
65
+ CODETRUST_API_URL: ${{ inputs.api-url }}
66
+ run: |
67
+ set -e
68
+
69
+ SCAN_MODE="${{ inputs.scan-mode }}"
70
+ FAIL_THRESHOLD="${{ inputs.fail-on }}"
71
+
72
+ if [ "$SCAN_MODE" = "diff" ]; then
73
+ # Get the diff for this PR or push
74
+ if [ -n "${{ github.event.pull_request.base.sha }}" ]; then
75
+ DIFF=$(git diff ${{ github.event.pull_request.base.sha }}..HEAD)
76
+ else
77
+ DIFF=$(git diff HEAD~1..HEAD 2>/dev/null || git diff HEAD)
78
+ fi
79
+
80
+ if [ -z "$DIFF" ]; then
81
+ echo "No code changes detected."
82
+ echo "score=100" >> "$GITHUB_OUTPUT"
83
+ echo "category=ship_it" >> "$GITHUB_OUTPUT"
84
+ echo "total_issues=0" >> "$GITHUB_OUTPUT"
85
+ echo "critical_issues=0" >> "$GITHUB_OUTPUT"
86
+ exit 0
87
+ fi
88
+
89
+ # Run scan via CLI with JSON output
90
+ RESULT=$(echo "$DIFF" | codetrust diff --json-output 2>/dev/null || echo '{"score": 0, "trust_category": "dont_ship", "total_issues": -1}')
91
+ else
92
+ RESULT=$(codetrust report . --json-output 2>/dev/null || echo '{"score": 0, "trust_category": "dont_ship", "total_issues": -1}')
93
+ fi
94
+
95
+ # Parse results
96
+ SCORE=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(int(d.get('score',0)))")
97
+ CATEGORY=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('trust_category','unknown'))")
98
+ TOTAL=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('total_issues',0))")
99
+ CRITICAL=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len([i for i in d.get('issues',[]) if i.get('severity')=='critical']))")
100
+
101
+ echo "score=$SCORE" >> "$GITHUB_OUTPUT"
102
+ echo "category=$CATEGORY" >> "$GITHUB_OUTPUT"
103
+ echo "total_issues=$TOTAL" >> "$GITHUB_OUTPUT"
104
+ echo "critical_issues=$CRITICAL" >> "$GITHUB_OUTPUT"
105
+
106
+ # Print summary
107
+ echo "## CodeTrust Scan Results" >> "$GITHUB_STEP_SUMMARY"
108
+ echo "" >> "$GITHUB_STEP_SUMMARY"
109
+ echo "| Metric | Value |" >> "$GITHUB_STEP_SUMMARY"
110
+ echo "|--------|-------|" >> "$GITHUB_STEP_SUMMARY"
111
+ echo "| **Trust Score** | **$SCORE/100** |" >> "$GITHUB_STEP_SUMMARY"
112
+ echo "| Category | $CATEGORY |" >> "$GITHUB_STEP_SUMMARY"
113
+ echo "| Total Issues | $TOTAL |" >> "$GITHUB_STEP_SUMMARY"
114
+ echo "| Critical Issues | $CRITICAL |" >> "$GITHUB_STEP_SUMMARY"
115
+ echo "" >> "$GITHUB_STEP_SUMMARY"
116
+
117
+ # Print issues if any
118
+ if [ "$TOTAL" -gt 0 ] 2>/dev/null; then
119
+ echo "### Issues Found" >> "$GITHUB_STEP_SUMMARY"
120
+ echo "" >> "$GITHUB_STEP_SUMMARY"
121
+ echo "$RESULT" | python3 -c "
122
+ import sys, json
123
+ d = json.load(sys.stdin)
124
+ for i in d.get('issues', [])[:20]:
125
+ sev = i.get('severity', 'info').upper()
126
+ title = i.get('title', 'Unknown')
127
+ desc = i.get('description', '')
128
+ line = i.get('line_start', '?')
129
+ fp = i.get('file_path', '?')
130
+ suggestion = i.get('suggestion', '')
131
+ print(f'- **[{sev}]** {title} ({fp}:{line})')
132
+ if suggestion:
133
+ print(f' - Fix: {suggestion}')
134
+ " >> "$GITHUB_STEP_SUMMARY" 2>/dev/null || true
135
+ fi
136
+
137
+ echo "" >> "$GITHUB_STEP_SUMMARY"
138
+ echo "*Scanned by [CodeTrust](https://codetrust.dev) — AI code security by AutoAI Labs*" >> "$GITHUB_STEP_SUMMARY"
139
+
140
+ # Fail if below threshold
141
+ if [ "$SCORE" -lt "$FAIL_THRESHOLD" ] 2>/dev/null; then
142
+ echo "::error::CodeTrust score ($SCORE) is below threshold ($FAIL_THRESHOLD). Fix the issues above."
143
+ exit 1
144
+ fi
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "autoai-codetrust"
7
+ version = "0.1.0"
8
+ description = "AI code security scanner — catches vulnerabilities, hallucinated dependencies, and compliance issues in AI-generated code. Snyk, but built for the AI coding era."
9
+ readme = "README.md"
10
+ license = {text = "Apache-2.0"}
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ {name = "AutoAI Labs", email = "info@autoailabs.co.uk"},
14
+ ]
15
+ keywords = [
16
+ "ai-code", "code-security", "hallucination-detection", "ai-scanner",
17
+ "code-quality", "security", "sast", "copilot", "cursor", "claude",
18
+ "snyk-alternative", "vulnerability-scanner", "mcp",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: Apache Software License",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Topic :: Software Development :: Quality Assurance",
29
+ "Topic :: Security",
30
+ ]
31
+ dependencies = [
32
+ "mcp>=1.0.0",
33
+ "click>=8.0",
34
+ "rich>=13.0",
35
+ "fastapi>=0.110.0",
36
+ "uvicorn[standard]>=0.29.0",
37
+ "python-multipart>=0.0.9",
38
+ ]
39
+
40
+ [project.optional-dependencies]
41
+ dev = [
42
+ "pytest>=7.0",
43
+ "pytest-asyncio>=0.21",
44
+ "httpx>=0.27.0",
45
+ ]
46
+
47
+ [project.scripts]
48
+ codetrust = "aicodetrustcore.cli:main"
49
+ codetrust-api = "aicodetrustcore.api:main"
50
+ codetrust-mcp = "aicodetrustcore.server:main"
51
+
52
+ [project.urls]
53
+ Homepage = "https://codetrust.dev"
54
+ Repository = "https://github.com/autoailabadmin/codetrust"
55
+ Documentation = "https://codetrust.dev/docs"
56
+
57
+ [tool.hatch.build.targets.wheel]
58
+ packages = ["src/aicodetrustcore"]
@@ -0,0 +1,8 @@
1
+ """AICodeTrustScore -- Continuous trust scoring for AI-generated code.
2
+
3
+ Analyzes code produced by Cursor, Copilot, Claude, and other AI tools,
4
+ assigning a 0-100 trust score based on correctness, safety, test coverage,
5
+ dependency risk, consistency, and hallucination detection.
6
+ """
7
+
8
+ __version__ = "0.1.0"
@@ -0,0 +1,29 @@
1
+ """AICodeTrustScore analyzers -- each module detects a specific class of trust issue."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .correctness import CorrectnessAnalyzer
6
+ from .safety import SafetyAnalyzer
7
+ from .test_coverage import TestCoverageAnalyzer
8
+ from .dependency import DependencyAnalyzer
9
+ from .consistency import ConsistencyAnalyzer
10
+ from .hallucination import HallucinationAnalyzer
11
+
12
+ ALL_ANALYZERS = [
13
+ CorrectnessAnalyzer,
14
+ SafetyAnalyzer,
15
+ TestCoverageAnalyzer,
16
+ DependencyAnalyzer,
17
+ ConsistencyAnalyzer,
18
+ HallucinationAnalyzer,
19
+ ]
20
+
21
+ __all__ = [
22
+ "CorrectnessAnalyzer",
23
+ "SafetyAnalyzer",
24
+ "TestCoverageAnalyzer",
25
+ "DependencyAnalyzer",
26
+ "ConsistencyAnalyzer",
27
+ "HallucinationAnalyzer",
28
+ "ALL_ANALYZERS",
29
+ ]
@@ -0,0 +1,255 @@
1
+ """Consistency analyzer -- checks if AI code matches existing project style.
2
+
3
+ Checks:
4
+ - Naming convention mismatch with existing code
5
+ - Different error handling patterns than the project uses
6
+ - Import style inconsistency
7
+ - Comment style mismatch
8
+ - Architecture layer violations
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ from pathlib import Path
15
+
16
+ from ..types import TrustIssue, IssueCategory, Severity
17
+
18
+
19
+ _CODE_EXTENSIONS = {".py", ".ts", ".tsx", ".js", ".jsx", ".go"}
20
+
21
+
22
+ class ConsistencyAnalyzer:
23
+ """Detects style and pattern inconsistencies in AI-generated code."""
24
+
25
+ name = "consistency"
26
+
27
+ @classmethod
28
+ def analyze_file(cls, file_path: Path, content: str) -> list[TrustIssue]:
29
+ """Analyse a file for consistency issues."""
30
+ findings: list[TrustIssue] = []
31
+
32
+ if file_path.suffix not in _CODE_EXTENSIONS:
33
+ return findings
34
+
35
+ lines = content.splitlines()
36
+ rel_path = str(file_path)
37
+ is_python = file_path.suffix == ".py"
38
+ is_js_ts = file_path.suffix in {".ts", ".tsx", ".js", ".jsx"}
39
+
40
+ # Naming convention checks
41
+ if is_python:
42
+ findings.extend(cls._check_python_naming(rel_path, lines))
43
+ findings.extend(cls._check_python_import_style(rel_path, lines))
44
+ findings.extend(cls._check_python_string_style(rel_path, lines))
45
+
46
+ if is_js_ts:
47
+ findings.extend(cls._check_js_naming(rel_path, lines))
48
+ findings.extend(cls._check_js_import_style(rel_path, lines))
49
+
50
+ # Mixed tab/space indentation
51
+ findings.extend(cls._check_indentation(rel_path, lines))
52
+
53
+ return findings
54
+
55
+ @classmethod
56
+ def _check_python_naming(cls, rel_path: str, lines: list[str]) -> list[TrustIssue]:
57
+ """Check Python naming conventions (PEP 8)."""
58
+ findings: list[TrustIssue] = []
59
+ camel_case_var = re.compile(r'^\s*([a-z]+[A-Z]\w*)\s*=')
60
+
61
+ camel_count = 0
62
+ for i, line in enumerate(lines, 1):
63
+ m = camel_case_var.match(line)
64
+ if m:
65
+ var_name = m.group(1)
66
+ # Exclude common abbreviations
67
+ if not re.match(r'^(is|has|can|should|will)', var_name):
68
+ camel_count += 1
69
+ if camel_count <= 3: # Report first few only
70
+ findings.append(TrustIssue(
71
+ analyzer=cls.name,
72
+ category=IssueCategory.CONSISTENCY,
73
+ severity=Severity.LOW,
74
+ title=f"camelCase variable '{var_name}' in Python",
75
+ description="Python convention is snake_case for variables and functions (PEP 8).",
76
+ file_path=rel_path,
77
+ line_start=i,
78
+ suggestion=f"Rename to '{cls._to_snake_case(var_name)}'.",
79
+ code_snippet=line.strip()[:200],
80
+ ))
81
+
82
+ # Classes not in PascalCase
83
+ class_pattern = re.compile(r'^\s*class\s+(\w+)')
84
+ for i, line in enumerate(lines, 1):
85
+ m = class_pattern.match(line)
86
+ if m:
87
+ name = m.group(1)
88
+ if "_" in name and not name.startswith("_"):
89
+ findings.append(TrustIssue(
90
+ analyzer=cls.name,
91
+ category=IssueCategory.CONSISTENCY,
92
+ severity=Severity.LOW,
93
+ title=f"Class '{name}' uses snake_case instead of PascalCase",
94
+ description="Python convention is PascalCase for class names (PEP 8).",
95
+ file_path=rel_path,
96
+ line_start=i,
97
+ suggestion=f"Rename to PascalCase.",
98
+ code_snippet=line.strip()[:200],
99
+ ))
100
+
101
+ return findings
102
+
103
+ @classmethod
104
+ def _check_python_import_style(cls, rel_path: str, lines: list[str]) -> list[TrustIssue]:
105
+ """Check for inconsistent import styles."""
106
+ findings: list[TrustIssue] = []
107
+
108
+ has_wildcard = False
109
+ wildcard_line = 0
110
+ for i, line in enumerate(lines, 1):
111
+ stripped = line.strip()
112
+ if re.match(r'from\s+\w+\s+import\s+\*', stripped):
113
+ has_wildcard = True
114
+ wildcard_line = i
115
+
116
+ if has_wildcard:
117
+ findings.append(TrustIssue(
118
+ analyzer=cls.name,
119
+ category=IssueCategory.CONSISTENCY,
120
+ severity=Severity.MEDIUM,
121
+ title="Wildcard import (from x import *)",
122
+ description="Wildcard imports pollute the namespace and make it unclear what's available.",
123
+ file_path=rel_path,
124
+ line_start=wildcard_line,
125
+ suggestion="Import specific names: from module import Class1, func1",
126
+ ))
127
+
128
+ return findings
129
+
130
+ @classmethod
131
+ def _check_python_string_style(cls, rel_path: str, lines: list[str]) -> list[TrustIssue]:
132
+ """Check for mixed single/double quotes (inconsistency indicator)."""
133
+ findings: list[TrustIssue] = []
134
+ single_count = 0
135
+ double_count = 0
136
+
137
+ for line in lines:
138
+ stripped = line.strip()
139
+ if stripped.startswith("#"):
140
+ continue
141
+ # Count string literal starts (simple heuristic)
142
+ single_count += len(re.findall(r"(?<![\"\\])'(?!'')", stripped))
143
+ double_count += len(re.findall(r'(?<![\'\\])"(?!"")', stripped))
144
+
145
+ total = single_count + double_count
146
+ if total > 10: # Only check if there are enough strings
147
+ ratio = min(single_count, double_count) / max(single_count, double_count, 1)
148
+ if 0.3 < ratio < 0.7:
149
+ findings.append(TrustIssue(
150
+ analyzer=cls.name,
151
+ category=IssueCategory.CONSISTENCY,
152
+ severity=Severity.INFO,
153
+ title="Inconsistent string quote style",
154
+ description=f"Mixed use of single ({single_count}) and double ({double_count}) quotes. "
155
+ "Pick one style and use it consistently.",
156
+ file_path=rel_path,
157
+ line_start=1,
158
+ suggestion="Use a formatter like black (Python) or prettier (JS) to enforce consistent quotes.",
159
+ ))
160
+
161
+ return findings
162
+
163
+ @classmethod
164
+ def _check_js_naming(cls, rel_path: str, lines: list[str]) -> list[TrustIssue]:
165
+ """Check JS/TS naming conventions."""
166
+ findings: list[TrustIssue] = []
167
+ snake_case_var = re.compile(r'^\s*(?:const|let|var)\s+([a-z]+_[a-z_]+)\s*=')
168
+
169
+ snake_count = 0
170
+ for i, line in enumerate(lines, 1):
171
+ m = snake_case_var.match(line)
172
+ if m:
173
+ var_name = m.group(1)
174
+ snake_count += 1
175
+ if snake_count <= 3:
176
+ findings.append(TrustIssue(
177
+ analyzer=cls.name,
178
+ category=IssueCategory.CONSISTENCY,
179
+ severity=Severity.LOW,
180
+ title=f"snake_case variable '{var_name}' in JS/TS",
181
+ description="JavaScript/TypeScript convention is camelCase for variables.",
182
+ file_path=rel_path,
183
+ line_start=i,
184
+ suggestion=f"Rename to '{cls._to_camel_case(var_name)}'.",
185
+ code_snippet=line.strip()[:200],
186
+ ))
187
+
188
+ return findings
189
+
190
+ @classmethod
191
+ def _check_js_import_style(cls, rel_path: str, lines: list[str]) -> list[TrustIssue]:
192
+ """Check for mixed require/import in JS/TS."""
193
+ findings: list[TrustIssue] = []
194
+ has_import = False
195
+ has_require = False
196
+
197
+ for line in lines:
198
+ stripped = line.strip()
199
+ if re.match(r'import\s+', stripped):
200
+ has_import = True
201
+ if re.search(r'require\s*\(', stripped):
202
+ has_require = True
203
+
204
+ if has_import and has_require:
205
+ findings.append(TrustIssue(
206
+ analyzer=cls.name,
207
+ category=IssueCategory.CONSISTENCY,
208
+ severity=Severity.LOW,
209
+ title="Mixed import and require() statements",
210
+ description="File uses both ES module imports and CommonJS require() -- pick one style.",
211
+ file_path=rel_path,
212
+ line_start=1,
213
+ suggestion="Use ES modules (import/export) consistently in modern JS/TS projects.",
214
+ ))
215
+
216
+ return findings
217
+
218
+ @classmethod
219
+ def _check_indentation(cls, rel_path: str, lines: list[str]) -> list[TrustIssue]:
220
+ """Check for mixed tabs and spaces."""
221
+ findings: list[TrustIssue] = []
222
+ has_tabs = False
223
+ has_spaces = False
224
+
225
+ for line in lines:
226
+ if line.startswith("\t"):
227
+ has_tabs = True
228
+ elif line.startswith(" "):
229
+ has_spaces = True
230
+
231
+ if has_tabs and has_spaces:
232
+ findings.append(TrustIssue(
233
+ analyzer=cls.name,
234
+ category=IssueCategory.CONSISTENCY,
235
+ severity=Severity.MEDIUM,
236
+ title="Mixed tabs and spaces for indentation",
237
+ description="File uses both tabs and spaces for indentation -- this causes subtle bugs.",
238
+ file_path=rel_path,
239
+ line_start=1,
240
+ suggestion="Configure your editor to use consistent indentation (spaces recommended).",
241
+ ))
242
+
243
+ return findings
244
+
245
+ @staticmethod
246
+ def _to_snake_case(name: str) -> str:
247
+ """Convert camelCase to snake_case."""
248
+ s = re.sub(r'([A-Z])', r'_\1', name).lower()
249
+ return s.lstrip("_")
250
+
251
+ @staticmethod
252
+ def _to_camel_case(name: str) -> str:
253
+ """Convert snake_case to camelCase."""
254
+ parts = name.split("_")
255
+ return parts[0] + "".join(p.capitalize() for p in parts[1:])