codedna 0.9.4__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.
Files changed (94) hide show
  1. {codedna-0.9.4 → codedna-0.9.5}/PKG-INFO +1 -3
  2. {codedna-0.9.4 → codedna-0.9.5}/codedna/__init__.py +1 -1
  3. {codedna-0.9.4 → codedna-0.9.5}/codedna/analyzer.py +85 -35
  4. {codedna-0.9.4 → codedna-0.9.5}/pyproject.toml +1 -4
  5. {codedna-0.9.4 → codedna-0.9.5}/.gitignore +0 -0
  6. {codedna-0.9.4 → codedna-0.9.5}/LICENSE +0 -0
  7. {codedna-0.9.4 → codedna-0.9.5}/README.md +0 -0
  8. {codedna-0.9.4 → codedna-0.9.5}/codedna/ai.py +0 -0
  9. {codedna-0.9.4 → codedna-0.9.5}/codedna/ai_box.py +0 -0
  10. {codedna-0.9.4 → codedna-0.9.5}/codedna/ai_fingerprint.py +0 -0
  11. {codedna-0.9.4 → codedna-0.9.5}/codedna/api.py +0 -0
  12. {codedna-0.9.4 → codedna-0.9.5}/codedna/auth.py +0 -0
  13. {codedna-0.9.4 → codedna-0.9.5}/codedna/bus_factor.py +0 -0
  14. {codedna-0.9.4 → codedna-0.9.5}/codedna/cli.py +0 -0
  15. {codedna-0.9.4 → codedna-0.9.5}/codedna/db.py +0 -0
  16. {codedna-0.9.4 → codedna-0.9.5}/codedna/demo.py +0 -0
  17. {codedna-0.9.4 → codedna-0.9.5}/codedna/git_hook.py +0 -0
  18. {codedna-0.9.4 → codedna-0.9.5}/codedna/integrations/__init__.py +0 -0
  19. {codedna-0.9.4 → codedna-0.9.5}/codedna/integrations/github_bot.py +0 -0
  20. {codedna-0.9.4 → codedna-0.9.5}/codedna/integrations/jira.py +0 -0
  21. {codedna-0.9.4 → codedna-0.9.5}/codedna/integrations/lemonsqueezy.py +0 -0
  22. {codedna-0.9.4 → codedna-0.9.5}/codedna/interview.py +0 -0
  23. {codedna-0.9.4 → codedna-0.9.5}/codedna/natureco_account.py +0 -0
  24. {codedna-0.9.4 → codedna-0.9.5}/codedna/nl.py +0 -0
  25. {codedna-0.9.4 → codedna-0.9.5}/codedna/nl_tools.json +0 -0
  26. {codedna-0.9.4 → codedna-0.9.5}/codedna/onboarding.py +0 -0
  27. {codedna-0.9.4 → codedna-0.9.5}/codedna/plan.py +0 -0
  28. {codedna-0.9.4 → codedna-0.9.5}/codedna/protection.py +0 -0
  29. {codedna-0.9.4 → codedna-0.9.5}/codedna/rate_limit.py +0 -0
  30. {codedna-0.9.4 → codedna-0.9.5}/codedna/scorer.py +0 -0
  31. {codedna-0.9.4 → codedna-0.9.5}/codedna/sprint_health.py +0 -0
  32. {codedna-0.9.4 → codedna-0.9.5}/codedna/survey.py +0 -0
  33. {codedna-0.9.4 → codedna-0.9.5}/codedna/tech_debt.py +0 -0
  34. {codedna-0.9.4 → codedna-0.9.5}/codedna/webhooks.py +0 -0
  35. {codedna-0.9.4 → codedna-0.9.5}/dashboard/.env.local.example +0 -0
  36. {codedna-0.9.4 → codedna-0.9.5}/dashboard/.gitignore +0 -0
  37. {codedna-0.9.4 → codedna-0.9.5}/dashboard/Dockerfile +0 -0
  38. {codedna-0.9.4 → codedna-0.9.5}/dashboard/README.md +0 -0
  39. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/ai-compare/page.tsx +0 -0
  40. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/api/auth/login/route.ts +0 -0
  41. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/api/auth/logout/route.ts +0 -0
  42. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/api/auth/me/route.ts +0 -0
  43. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/api/auth/register/route.ts +0 -0
  44. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/api/billing/checkout/route.ts +0 -0
  45. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/billing/page.tsx +0 -0
  46. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/bus-factor/page.tsx +0 -0
  47. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/commits/page.tsx +0 -0
  48. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/debt/page.tsx +0 -0
  49. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/favicon.ico +0 -0
  50. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/feedback/page.tsx +0 -0
  51. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/files/page.tsx +0 -0
  52. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/fonts/GeistMonoVF.woff +0 -0
  53. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/fonts/GeistVF.woff +0 -0
  54. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/globals.css +0 -0
  55. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/interview/page.tsx +0 -0
  56. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/layout.tsx +0 -0
  57. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/login/page.tsx +0 -0
  58. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/onboarding/page.tsx +0 -0
  59. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/page.tsx +0 -0
  60. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/pricing/page.tsx +0 -0
  61. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/protected/page.tsx +0 -0
  62. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/register/page.tsx +0 -0
  63. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/repos/page.tsx +0 -0
  64. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/settings/integrations/page.tsx +0 -0
  65. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/sprints/page.tsx +0 -0
  66. {codedna-0.9.4 → codedna-0.9.5}/dashboard/app/trends/page.tsx +0 -0
  67. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/AuthProvider.tsx +0 -0
  68. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/ClientProviders.tsx +0 -0
  69. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/CommitTable.tsx +0 -0
  70. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/CostInfoTooltip.tsx +0 -0
  71. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/ErrorBanner.tsx +0 -0
  72. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/FeatureGate.tsx +0 -0
  73. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/FileHeatmap.tsx +0 -0
  74. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/LanguageSwitcher.tsx +0 -0
  75. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/NavClient.tsx +0 -0
  76. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/OverviewTitles.tsx +0 -0
  77. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/QuickInsights.tsx +0 -0
  78. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/RiskBadge.tsx +0 -0
  79. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/SummaryCards.tsx +0 -0
  80. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/UnderstandingChart.tsx +0 -0
  81. {codedna-0.9.4 → codedna-0.9.5}/dashboard/components/UpgradeBanner.tsx +0 -0
  82. {codedna-0.9.4 → codedna-0.9.5}/dashboard/lib/api.ts +0 -0
  83. {codedna-0.9.4 → codedna-0.9.5}/dashboard/lib/auth.ts +0 -0
  84. {codedna-0.9.4 → codedna-0.9.5}/dashboard/lib/i18n/en.ts +0 -0
  85. {codedna-0.9.4 → codedna-0.9.5}/dashboard/lib/i18n/index.tsx +0 -0
  86. {codedna-0.9.4 → codedna-0.9.5}/dashboard/lib/i18n/tr.ts +0 -0
  87. {codedna-0.9.4 → codedna-0.9.5}/dashboard/lib/plan.ts +0 -0
  88. {codedna-0.9.4 → codedna-0.9.5}/dashboard/next-env.d.ts +0 -0
  89. {codedna-0.9.4 → codedna-0.9.5}/dashboard/next.config.mjs +0 -0
  90. {codedna-0.9.4 → codedna-0.9.5}/dashboard/package-lock.json +0 -0
  91. {codedna-0.9.4 → codedna-0.9.5}/dashboard/package.json +0 -0
  92. {codedna-0.9.4 → codedna-0.9.5}/dashboard/postcss.config.mjs +0 -0
  93. {codedna-0.9.4 → codedna-0.9.5}/dashboard/tailwind.config.ts +0 -0
  94. {codedna-0.9.4 → 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.4
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'
@@ -1,4 +1,4 @@
1
1
  """CodeDNA — AI Code Transparency Tool."""
2
2
 
3
- __version__ = "0.9.4"
3
+ __version__ = "0.9.5"
4
4
  __app_name__ = "codedna"
@@ -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 (+0.20)"
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 (+0.15)"
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 (+0.30)"
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 (+0.25)"
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 _calculate_ai_probability(result: FileAnalysisResult) -> float:
376
- """
377
- Calculate a rule-based AI probability score (0.0 1.0).
378
-
379
- Rules:
380
- - comment_ratio > 0.3 → +0.20 (AI tends to over-comment)
381
- - avg_function_length > 50 → +0.15 (AI tends to produce large blocks)
382
- - single_commit_ratio > 0.7 → +0.30 (bulk paste indicator)
383
- - high complexity + single commit → +0.25
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
- score = 0.0
386
-
387
- # Rule 1: Excessive comment ratio (AI code tends to over-comment)
388
- if result.comment_ratio > 0.3:
389
- score += 0.20
390
-
391
- # Rule 2: Long functions (AI tends to produce large blocks)
392
- if result.avg_function_length > 50:
393
- score += 0.15
394
-
395
- # Rule 3: Large change in a single commit (bulk paste indicator)
396
- if result.single_commit_ratio > 0.7:
397
- score += 0.30
398
-
399
- # Rule 4: High complexity + single-commit
400
- if result.complexity_score > 10 and result.single_commit_ratio > 0.5:
401
- score += 0.25
402
-
403
- # Normalize to 0.0 – 1.0
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)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codedna"
3
- version = "0.9.4"
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