codedna 0.9.3__tar.gz → 0.9.5__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.
- {codedna-0.9.3 → codedna-0.9.5}/PKG-INFO +1 -3
- {codedna-0.9.3 → codedna-0.9.5}/codedna/__init__.py +1 -1
- {codedna-0.9.3 → codedna-0.9.5}/codedna/analyzer.py +85 -35
- {codedna-0.9.3 → codedna-0.9.5}/codedna/cli.py +8 -1
- {codedna-0.9.3 → codedna-0.9.5}/pyproject.toml +1 -4
- {codedna-0.9.3 → codedna-0.9.5}/.gitignore +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/LICENSE +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/README.md +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/ai.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/ai_box.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/ai_fingerprint.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/api.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/auth.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/bus_factor.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/db.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/demo.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/git_hook.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/integrations/__init__.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/integrations/github_bot.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/integrations/jira.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/integrations/lemonsqueezy.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/interview.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/natureco_account.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/nl.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/nl_tools.json +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/onboarding.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/plan.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/protection.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/rate_limit.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/scorer.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/sprint_health.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/survey.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/tech_debt.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/codedna/webhooks.py +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/.env.local.example +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/.gitignore +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/Dockerfile +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/README.md +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/ai-compare/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/api/auth/login/route.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/api/auth/logout/route.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/api/auth/me/route.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/api/auth/register/route.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/api/billing/checkout/route.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/billing/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/bus-factor/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/commits/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/debt/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/favicon.ico +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/feedback/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/files/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/fonts/GeistMonoVF.woff +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/fonts/GeistVF.woff +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/globals.css +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/interview/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/layout.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/login/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/onboarding/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/pricing/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/protected/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/register/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/repos/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/settings/integrations/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/sprints/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/app/trends/page.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/AuthProvider.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/ClientProviders.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/CommitTable.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/CostInfoTooltip.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/ErrorBanner.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/FeatureGate.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/FileHeatmap.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/LanguageSwitcher.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/NavClient.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/OverviewTitles.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/QuickInsights.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/RiskBadge.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/SummaryCards.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/UnderstandingChart.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/components/UpgradeBanner.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/lib/api.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/lib/auth.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/lib/i18n/en.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/lib/i18n/index.tsx +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/lib/i18n/tr.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/lib/plan.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/next-env.d.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/next.config.mjs +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/package-lock.json +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/package.json +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/postcss.config.mjs +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/tailwind.config.ts +0 -0
- {codedna-0.9.3 → codedna-0.9.5}/dashboard/tsconfig.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codedna
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.5
|
|
4
4
|
Summary: AI Code Transparency Tool — detect AI-written code, analyze commit quality, track understanding debt, sprint health, bus factor, and more
|
|
5
5
|
Project-URL: Homepage, https://codedna.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/natureco-official/codedna
|
|
@@ -41,8 +41,6 @@ Requires-Dist: tree-sitter-typescript>=0.23.2
|
|
|
41
41
|
Requires-Dist: tree-sitter>=0.25.2
|
|
42
42
|
Requires-Dist: typer>=0.26.7
|
|
43
43
|
Requires-Dist: uvicorn[standard]>=0.49.0
|
|
44
|
-
Provides-Extra: dashboard
|
|
45
|
-
Requires-Dist: next-cmd>=0.5.0; extra == 'dashboard'
|
|
46
44
|
Provides-Extra: dev
|
|
47
45
|
Requires-Dist: black>=26.5.1; extra == 'dev'
|
|
48
46
|
Requires-Dist: pytest-cov>=7.1.0; extra == 'dev'
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import math
|
|
5
6
|
import re
|
|
6
7
|
from dataclasses import dataclass, field
|
|
7
8
|
from pathlib import Path
|
|
@@ -75,6 +76,7 @@ class FileAnalysisResult:
|
|
|
75
76
|
comment_ratio: float = 0.0
|
|
76
77
|
avg_function_length: float = 0.0
|
|
77
78
|
single_commit_ratio: float = 0.0
|
|
79
|
+
content_ai_signal: float = 0.0 # content-based AI signal (code patterns, 0–1)
|
|
78
80
|
total_lines: int = 0
|
|
79
81
|
function_count: int = 0
|
|
80
82
|
unsupported: bool = False # kept for internal use
|
|
@@ -243,8 +245,8 @@ def analyze_file(
|
|
|
243
245
|
# Cyclomatic complexity (whole file)
|
|
244
246
|
result.complexity_score = _calculate_cyclomatic_complexity(tree.root_node)
|
|
245
247
|
|
|
246
|
-
# Calculate AI probability
|
|
247
|
-
result.ai_probability = _calculate_ai_probability(result)
|
|
248
|
+
# Calculate AI probability (continuous + content-based)
|
|
249
|
+
result.ai_probability = _calculate_ai_probability(result, source)
|
|
248
250
|
|
|
249
251
|
# Generate explanation
|
|
250
252
|
result.explanation = explain_ai_score(result)
|
|
@@ -339,28 +341,35 @@ def explain_ai_score(result: FileAnalysisResult) -> list[str]:
|
|
|
339
341
|
"""
|
|
340
342
|
reasons: list[str] = []
|
|
341
343
|
|
|
344
|
+
if result.content_ai_signal >= 0.15:
|
|
345
|
+
pct = int(result.content_ai_signal * 100)
|
|
346
|
+
reasons.append(
|
|
347
|
+
f"Code carries AI-style patterns ({pct}% content signal) — e.g. type hints, "
|
|
348
|
+
f"defensive boilerplate, 'step' comments, emoji"
|
|
349
|
+
)
|
|
350
|
+
|
|
342
351
|
if result.comment_ratio > 0.3:
|
|
343
352
|
pct = int(result.comment_ratio * 100)
|
|
344
353
|
reasons.append(
|
|
345
|
-
f"High comment ratio ({pct}%) — AI-generated code tends to over-comment
|
|
354
|
+
f"High comment ratio ({pct}%) — AI-generated code tends to over-comment"
|
|
346
355
|
)
|
|
347
356
|
|
|
348
357
|
if result.avg_function_length > 50:
|
|
349
358
|
reasons.append(
|
|
350
359
|
f"Long avg. function length ({result.avg_function_length:.0f} lines) — "
|
|
351
|
-
f"AI tends to produce larger blocks
|
|
360
|
+
f"AI tends to produce larger blocks"
|
|
352
361
|
)
|
|
353
362
|
|
|
354
363
|
if result.single_commit_ratio > 0.7:
|
|
355
364
|
pct = int(result.single_commit_ratio * 100)
|
|
356
365
|
reasons.append(
|
|
357
|
-
f"High single-commit ratio ({pct}%) — bulk paste indicator
|
|
366
|
+
f"High single-commit ratio ({pct}%) — bulk paste indicator"
|
|
358
367
|
)
|
|
359
368
|
|
|
360
369
|
if result.complexity_score > 10 and result.single_commit_ratio > 0.5:
|
|
361
370
|
reasons.append(
|
|
362
371
|
f"High complexity ({result.complexity_score:.0f}) combined with bulk change — "
|
|
363
|
-
f"complex AI-generated code pattern
|
|
372
|
+
f"complex AI-generated code pattern"
|
|
364
373
|
)
|
|
365
374
|
|
|
366
375
|
if result.function_count == 0:
|
|
@@ -372,33 +381,74 @@ def explain_ai_score(result: FileAnalysisResult) -> list[str]:
|
|
|
372
381
|
return reasons
|
|
373
382
|
|
|
374
383
|
|
|
375
|
-
def
|
|
376
|
-
"""
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
+
def _smoothstep(x: float, lo: float, hi: float) -> float:
|
|
385
|
+
"""Continuous 0→1 ramp between lo and hi (replaces hard thresholds so scores
|
|
386
|
+
are file-specific and granular instead of clustering on a few binned values)."""
|
|
387
|
+
if hi <= lo:
|
|
388
|
+
return 1.0 if x >= hi else 0.0
|
|
389
|
+
t = max(0.0, min(1.0, (x - lo) / (hi - lo)))
|
|
390
|
+
return t * t * (3.0 - 2.0 * t)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# Content-based AI-authorship patterns (regex, weight). These look at what the code
|
|
394
|
+
# actually *is* — the stylistic fingerprints AI assistants tend to leave — rather than
|
|
395
|
+
# only git history. Density of matches feeds the score directly.
|
|
396
|
+
_AI_CONTENT_PATTERNS: list[tuple[str, float]] = [
|
|
397
|
+
(r"#\s*(Step\s*\d|Initialize|Return the|Create the|Loop through|Iterate over|Handle the|Validate|Check if|Set up|Ensure|First,|Finally,)", 1.0),
|
|
398
|
+
(r"//\s*(Step\s*\d|Initialize|Return the|Create the|Handle the|Validate|Check if|Set up|Ensure)", 1.0),
|
|
399
|
+
(r'"""', 0.25), # docstrings (AI documents everything)
|
|
400
|
+
(r"->\s*[A-Za-z_\[\"']", 0.45), # return type hints
|
|
401
|
+
(r":\s*(str|int|float|bool|list|dict|tuple|Optional|Any|Dict|List|Union)\b", 0.35), # param type hints
|
|
402
|
+
(r"^\s*try:\s*$", 0.4), # defensive try/except everywhere
|
|
403
|
+
(r"if\s+\w+\s+is\s+None", 0.4), # explicit None guards
|
|
404
|
+
(r"except\s+Exception\b", 0.35), # broad catch-alls
|
|
405
|
+
(r"[\U0001F300-\U0001FAFF☀-➿←-⇿✓✗]", 0.6), # emoji/✓/✗/→ in code
|
|
406
|
+
(r"\b(helper|handler|wrapper|manager|processor|validator)\b", 0.2), # generic role names
|
|
407
|
+
(r"console\.(log|error)\(.*(✓|✗|→|✅|❌|🧬|⚠)", 0.5),
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _content_ai_signal(source: str) -> float:
|
|
412
|
+
"""A content-based AI signal in [0,1], independent of git history: how strongly
|
|
413
|
+
the code carries the stylistic fingerprints AI assistants tend to leave."""
|
|
414
|
+
lines = source.splitlines()
|
|
415
|
+
n = max(len(lines), 1)
|
|
416
|
+
total = 0.0
|
|
417
|
+
for pattern, weight in _AI_CONTENT_PATTERNS:
|
|
418
|
+
try:
|
|
419
|
+
matches = sum(1 for line in lines if re.search(pattern, line))
|
|
420
|
+
except re.error:
|
|
421
|
+
continue
|
|
422
|
+
total += (matches / n) * weight
|
|
423
|
+
# Saturating density → [0,1]; small densities still register, high ones plateau.
|
|
424
|
+
return 1.0 - math.exp(-4.0 * total)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _calculate_ai_probability(result: FileAnalysisResult, source: str = "") -> float:
|
|
428
|
+
"""Continuous, content-aware AI probability score (0.0 – 1.0).
|
|
429
|
+
|
|
430
|
+
Combines graded structural signals (comments, function size, cyclomatic
|
|
431
|
+
complexity), git-history signals (bulk single-commit paste) and a
|
|
432
|
+
content-based fingerprint of AI code style. Unlike the previous four binary
|
|
433
|
+
rules, every input is continuous so scores reflect each file individually.
|
|
384
434
|
"""
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
#
|
|
392
|
-
if
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
#
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
return min(score, 1.0)
|
|
435
|
+
# Graded structural / history signals — smooth ramps, not on/off thresholds.
|
|
436
|
+
comment = _smoothstep(result.comment_ratio, 0.12, 0.45)
|
|
437
|
+
func_len = _smoothstep(result.avg_function_length, 25.0, 90.0)
|
|
438
|
+
bulk = _smoothstep(result.single_commit_ratio, 0.35, 0.90)
|
|
439
|
+
complexity = _smoothstep(result.complexity_score, 6.0, 25.0)
|
|
440
|
+
|
|
441
|
+
# Content fingerprint — the actual code style (strongest single signal).
|
|
442
|
+
content = _content_ai_signal(source) if source else 0.0
|
|
443
|
+
result.content_ai_signal = round(content, 4)
|
|
444
|
+
|
|
445
|
+
# Weighted blend (weights sum to 1.0). Content carries the most weight because
|
|
446
|
+
# it inspects what the code *is*, not just how it landed in git.
|
|
447
|
+
score = (
|
|
448
|
+
0.14 * comment
|
|
449
|
+
+ 0.12 * func_len
|
|
450
|
+
+ 0.20 * bulk
|
|
451
|
+
+ 0.12 * (complexity * bulk) # complexity matters most when bulk-committed
|
|
452
|
+
+ 0.42 * content
|
|
453
|
+
)
|
|
454
|
+
return round(min(max(score, 0.0), 1.0), 4)
|
|
@@ -2134,7 +2134,14 @@ def ask(
|
|
|
2134
2134
|
dry_run: bool = typer.Option(False, "--dry-run", help="Show the command without running it."),
|
|
2135
2135
|
) -> None:
|
|
2136
2136
|
"""Run a CodeDNA command from a natural-language request (powered by Needle)."""
|
|
2137
|
-
|
|
2137
|
+
try:
|
|
2138
|
+
from codedna.nl import route_command
|
|
2139
|
+
except ImportError as e:
|
|
2140
|
+
# The natural-language router needs the Needle model, which isn't part of
|
|
2141
|
+
# the published package. Degrade gracefully instead of crashing.
|
|
2142
|
+
console.print("[yellow]The natural-language [bold]ask[/bold] command needs the Needle model, which isn't available in this install.[/yellow]")
|
|
2143
|
+
console.print("[dim]Use the direct commands instead — e.g. [bold]codedna scan[/bold]. Run [bold]codedna --help[/bold] for the full list.[/dim]")
|
|
2144
|
+
raise typer.Exit(1) from e
|
|
2138
2145
|
route_command(" ".join(query), dry_run=dry_run)
|
|
2139
2146
|
|
|
2140
2147
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codedna"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.5"
|
|
4
4
|
description = "AI Code Transparency Tool — detect AI-written code, analyze commit quality, track understanding debt, sprint health, bus factor, and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -47,9 +47,6 @@ dependencies = [
|
|
|
47
47
|
]
|
|
48
48
|
|
|
49
49
|
[project.optional-dependencies]
|
|
50
|
-
dashboard = [
|
|
51
|
-
"next-cmd>=0.5.0", # Next.js process management
|
|
52
|
-
]
|
|
53
50
|
nl = [
|
|
54
51
|
"huggingface_hub",
|
|
55
52
|
"pyarrow>=14,<18",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|