codedna 0.8.1__tar.gz → 0.8.3__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 (86) hide show
  1. {codedna-0.8.1 → codedna-0.8.3}/PKG-INFO +27 -18
  2. {codedna-0.8.1 → codedna-0.8.3}/README.md +26 -17
  3. {codedna-0.8.1 → codedna-0.8.3}/codedna/__init__.py +1 -1
  4. {codedna-0.8.1 → codedna-0.8.3}/codedna/cli.py +12 -10
  5. {codedna-0.8.1 → codedna-0.8.3}/codedna/db.py +19 -9
  6. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/ai-compare/page.tsx +1 -2
  7. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/billing/checkout/route.ts +1 -1
  8. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/billing/page.tsx +3 -3
  9. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/layout.tsx +1 -1
  10. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/protected/page.tsx +1 -1
  11. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/ErrorBanner.tsx +4 -5
  12. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/NavClient.tsx +9 -9
  13. {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/api.ts +0 -4
  14. {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/auth.ts +0 -41
  15. {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/plan.ts +14 -9
  16. {codedna-0.8.1 → codedna-0.8.3}/pyproject.toml +1 -1
  17. {codedna-0.8.1 → codedna-0.8.3}/.gitignore +0 -0
  18. {codedna-0.8.1 → codedna-0.8.3}/LICENSE +0 -0
  19. {codedna-0.8.1 → codedna-0.8.3}/codedna/ai.py +0 -0
  20. {codedna-0.8.1 → codedna-0.8.3}/codedna/ai_box.py +0 -0
  21. {codedna-0.8.1 → codedna-0.8.3}/codedna/ai_fingerprint.py +0 -0
  22. {codedna-0.8.1 → codedna-0.8.3}/codedna/analyzer.py +0 -0
  23. {codedna-0.8.1 → codedna-0.8.3}/codedna/api.py +0 -0
  24. {codedna-0.8.1 → codedna-0.8.3}/codedna/auth.py +0 -0
  25. {codedna-0.8.1 → codedna-0.8.3}/codedna/bus_factor.py +0 -0
  26. {codedna-0.8.1 → codedna-0.8.3}/codedna/demo.py +0 -0
  27. {codedna-0.8.1 → codedna-0.8.3}/codedna/git_hook.py +0 -0
  28. {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/__init__.py +0 -0
  29. {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/github_bot.py +0 -0
  30. {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/jira.py +0 -0
  31. {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/lemonsqueezy.py +0 -0
  32. {codedna-0.8.1 → codedna-0.8.3}/codedna/interview.py +0 -0
  33. {codedna-0.8.1 → codedna-0.8.3}/codedna/onboarding.py +0 -0
  34. {codedna-0.8.1 → codedna-0.8.3}/codedna/plan.py +0 -0
  35. {codedna-0.8.1 → codedna-0.8.3}/codedna/protection.py +0 -0
  36. {codedna-0.8.1 → codedna-0.8.3}/codedna/rate_limit.py +0 -0
  37. {codedna-0.8.1 → codedna-0.8.3}/codedna/scorer.py +0 -0
  38. {codedna-0.8.1 → codedna-0.8.3}/codedna/sprint_health.py +0 -0
  39. {codedna-0.8.1 → codedna-0.8.3}/codedna/survey.py +0 -0
  40. {codedna-0.8.1 → codedna-0.8.3}/codedna/tech_debt.py +0 -0
  41. {codedna-0.8.1 → codedna-0.8.3}/dashboard/.env.local.example +0 -0
  42. {codedna-0.8.1 → codedna-0.8.3}/dashboard/.gitignore +0 -0
  43. {codedna-0.8.1 → codedna-0.8.3}/dashboard/README.md +0 -0
  44. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/login/route.ts +0 -0
  45. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/logout/route.ts +0 -0
  46. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/me/route.ts +0 -0
  47. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/register/route.ts +0 -0
  48. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/bus-factor/page.tsx +0 -0
  49. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/commits/page.tsx +0 -0
  50. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/debt/page.tsx +0 -0
  51. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/favicon.ico +0 -0
  52. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/files/page.tsx +0 -0
  53. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/fonts/GeistMonoVF.woff +0 -0
  54. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/fonts/GeistVF.woff +0 -0
  55. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/globals.css +0 -0
  56. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/interview/page.tsx +0 -0
  57. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/login/page.tsx +0 -0
  58. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/onboarding/page.tsx +0 -0
  59. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/page.tsx +0 -0
  60. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/pricing/page.tsx +0 -0
  61. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/register/page.tsx +0 -0
  62. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/settings/integrations/page.tsx +0 -0
  63. {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/sprints/page.tsx +0 -0
  64. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/AuthProvider.tsx +0 -0
  65. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/ClientProviders.tsx +0 -0
  66. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/CommitTable.tsx +0 -0
  67. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/CostInfoTooltip.tsx +0 -0
  68. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/FeatureGate.tsx +0 -0
  69. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/FileHeatmap.tsx +0 -0
  70. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/LanguageSwitcher.tsx +0 -0
  71. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/OverviewTitles.tsx +0 -0
  72. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/QuickInsights.tsx +0 -0
  73. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/RiskBadge.tsx +0 -0
  74. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/SummaryCards.tsx +0 -0
  75. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/UnderstandingChart.tsx +0 -0
  76. {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/UpgradeBanner.tsx +0 -0
  77. {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/i18n/en.ts +0 -0
  78. {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/i18n/index.tsx +0 -0
  79. {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/i18n/tr.ts +0 -0
  80. {codedna-0.8.1 → codedna-0.8.3}/dashboard/next-env.d.ts +0 -0
  81. {codedna-0.8.1 → codedna-0.8.3}/dashboard/next.config.mjs +0 -0
  82. {codedna-0.8.1 → codedna-0.8.3}/dashboard/package-lock.json +0 -0
  83. {codedna-0.8.1 → codedna-0.8.3}/dashboard/package.json +0 -0
  84. {codedna-0.8.1 → codedna-0.8.3}/dashboard/postcss.config.mjs +0 -0
  85. {codedna-0.8.1 → codedna-0.8.3}/dashboard/tailwind.config.ts +0 -0
  86. {codedna-0.8.1 → codedna-0.8.3}/dashboard/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codedna
3
- Version: 0.8.1
3
+ Version: 0.8.3
4
4
  Summary: AI Code Transparency Tool - detect AI-written code and measure developer understanding
5
5
  Project-URL: Homepage, https://codedna.dev
6
6
  Project-URL: Repository, https://github.com/natureco-official/codedna
@@ -179,7 +179,7 @@ pip install -e .
179
179
  ### Verify Installation
180
180
 
181
181
  ```bash
182
- codedna --version # should show "codedna 0.3.x"
182
+ codedna --version # should show "codedna 0.8.x"
183
183
  codedna doctor # full system health check
184
184
  ```
185
185
 
@@ -244,9 +244,9 @@ codedna interview-list # List interviews
244
244
  codedna interview-score # Score interview
245
245
  codedna bus-factor # Ownership analysis (Team+)
246
246
  codedna debt # Technical debt (Team+)
247
- codedna sprint-olustur # Create sprint
248
- codedna sprint-sagligi # Sprint health
249
- codedna sprint-gecmisi # Sprint history
247
+ codedna sprint-create # Create sprint
248
+ codedna sprint-health # Sprint health
249
+ codedna sprint-history # Sprint history
250
250
  ```
251
251
 
252
252
  ### 🌐 Infrastructure
@@ -539,23 +539,32 @@ MIT License - Copyright (c) 2026 NatureCo
539
539
 
540
540
  ## 🗺️ Roadmap
541
541
 
542
- ### v0.3.0 (Q3 2026)
543
- - [ ] VSCode extension (real-time)
544
- - [ ] Go/Rust/Java support
545
- - [ ] Slack bot
546
- - [ ] Jira integration
547
-
548
- ### v0.4.0 (Q4 2026)
549
- - [ ] Team analytics dashboard
550
- - [ ] AI coach (suggests learning resources)
551
- - [ ] Custom AI fingerprinting
552
- - [ ] Cloud sync (optional)
542
+ ### ✅ Completed (v0.8.0+)
543
+ - [x] Full English translation (CLI, API, Dashboard)
544
+ - [x] Web Dashboard shipped inside PyPI package (`pip install codedna` → `codedna dashboard`)
545
+ - [x] Demo mode for quick evaluation
546
+ - [x] VS Code Marketplace extension
547
+ - [x] Jira integration + webhook
548
+ - [x] Technical debt & bus factor analysis
549
+ - [x] Sprint health & onboarding tracking
550
+ - [x] AI tool comparison (Copilot vs Cursor vs Claude)
551
+ - [x] Interview tool for candidate assessment
552
+ - [x] Protected modules & understanding thresholds
553
+ - [x] Lemon Squeezy billing integration
554
+
555
+ ### v0.9.0 (Next)
556
+ - [ ] Go/Rust/Java Tree-sitter support
557
+ - [ ] Slack/Discord webhook notifications
558
+ - [ ] AI coach — personalized learning suggestions
559
+ - [ ] Custom AI fingerprinting rules
560
+ - [ ] Enhanced multi-repo dashboard
553
561
 
554
562
  ### v1.0.0 (2027)
555
- - [ ] Self-hosted option
563
+ - [ ] Self-hosted option (Docker)
556
564
  - [ ] Enterprise SSO (SAML, OIDC)
557
565
  - [ ] SOC 2 compliance
558
- - [ ] Custom training data
566
+ - [ ] Cloud sync (optional)
567
+ - [ ] Team analytics dashboard with role-based views
559
568
 
560
569
  ---
561
570
 
@@ -127,7 +127,7 @@ pip install -e .
127
127
  ### Verify Installation
128
128
 
129
129
  ```bash
130
- codedna --version # should show "codedna 0.3.x"
130
+ codedna --version # should show "codedna 0.8.x"
131
131
  codedna doctor # full system health check
132
132
  ```
133
133
 
@@ -192,9 +192,9 @@ codedna interview-list # List interviews
192
192
  codedna interview-score # Score interview
193
193
  codedna bus-factor # Ownership analysis (Team+)
194
194
  codedna debt # Technical debt (Team+)
195
- codedna sprint-olustur # Create sprint
196
- codedna sprint-sagligi # Sprint health
197
- codedna sprint-gecmisi # Sprint history
195
+ codedna sprint-create # Create sprint
196
+ codedna sprint-health # Sprint health
197
+ codedna sprint-history # Sprint history
198
198
  ```
199
199
 
200
200
  ### 🌐 Infrastructure
@@ -487,23 +487,32 @@ MIT License - Copyright (c) 2026 NatureCo
487
487
 
488
488
  ## 🗺️ Roadmap
489
489
 
490
- ### v0.3.0 (Q3 2026)
491
- - [ ] VSCode extension (real-time)
492
- - [ ] Go/Rust/Java support
493
- - [ ] Slack bot
494
- - [ ] Jira integration
495
-
496
- ### v0.4.0 (Q4 2026)
497
- - [ ] Team analytics dashboard
498
- - [ ] AI coach (suggests learning resources)
499
- - [ ] Custom AI fingerprinting
500
- - [ ] Cloud sync (optional)
490
+ ### ✅ Completed (v0.8.0+)
491
+ - [x] Full English translation (CLI, API, Dashboard)
492
+ - [x] Web Dashboard shipped inside PyPI package (`pip install codedna` → `codedna dashboard`)
493
+ - [x] Demo mode for quick evaluation
494
+ - [x] VS Code Marketplace extension
495
+ - [x] Jira integration + webhook
496
+ - [x] Technical debt & bus factor analysis
497
+ - [x] Sprint health & onboarding tracking
498
+ - [x] AI tool comparison (Copilot vs Cursor vs Claude)
499
+ - [x] Interview tool for candidate assessment
500
+ - [x] Protected modules & understanding thresholds
501
+ - [x] Lemon Squeezy billing integration
502
+
503
+ ### v0.9.0 (Next)
504
+ - [ ] Go/Rust/Java Tree-sitter support
505
+ - [ ] Slack/Discord webhook notifications
506
+ - [ ] AI coach — personalized learning suggestions
507
+ - [ ] Custom AI fingerprinting rules
508
+ - [ ] Enhanced multi-repo dashboard
501
509
 
502
510
  ### v1.0.0 (2027)
503
- - [ ] Self-hosted option
511
+ - [ ] Self-hosted option (Docker)
504
512
  - [ ] Enterprise SSO (SAML, OIDC)
505
513
  - [ ] SOC 2 compliance
506
- - [ ] Custom training data
514
+ - [ ] Cloud sync (optional)
515
+ - [ ] Team analytics dashboard with role-based views
507
516
 
508
517
  ---
509
518
 
@@ -1,4 +1,4 @@
1
1
  """CodeDNA — AI Code Transparency Tool."""
2
2
 
3
- __version__ = "0.8.1"
3
+ __version__ = "0.8.3"
4
4
  __app_name__ = "codedna"
@@ -316,8 +316,9 @@ def status(
316
316
  console.print(f"[bold red]{warning}[/bold red]")
317
317
  except Exception:
318
318
  pass # Never breaks the hook
319
+ return
319
320
 
320
- else:
321
+ if not results:
321
322
  console.print(
322
323
  f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim]\n"
323
324
  "[dim]No supported code files found in this commit.[/dim]"
@@ -1093,8 +1094,7 @@ def bus_factor(
1093
1094
  if not is_feature_available("bus_factor"):
1094
1095
  console.print(
1095
1096
  Panel(
1096
- "[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n"
1097
- "[dim]This feature is available on Team plan.[/dim]\n\n"
1097
+ "[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n\n"
1098
1098
  "[dim]To upgrade:[/dim] [cyan]codedna plan activate <LICENSE_KEY>[/cyan]",
1099
1099
  border_style="yellow",
1100
1100
  padding=(1, 2),
@@ -1311,8 +1311,7 @@ def sprint_create(
1311
1311
  if not is_feature_available("sprint_health"):
1312
1312
  console.print(
1313
1313
  Panel(
1314
- "[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n"
1315
- "[dim]This feature is available on Team plan.[/dim]\n\n"
1314
+ "[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n\n"
1316
1315
  "[dim]To upgrade:[/dim] [cyan]codedna plan activate <LICENSE_KEY>[/cyan]",
1317
1316
  border_style="yellow",
1318
1317
  padding=(1, 2),
@@ -1719,7 +1718,7 @@ def uninstall(
1719
1718
  """Remove CodeDNA hook."""
1720
1719
  root = repo or find_git_root()
1721
1720
  if uninstall_hook(root):
1722
- console.print("[green]✓[/green] CodeDNA uninstalled.")
1721
+ console.print("[green]✓[/green] CodeDNA hook removed.")
1723
1722
  else:
1724
1723
  raise typer.Exit(1)
1725
1724
 
@@ -1850,11 +1849,11 @@ def doctor(
1850
1849
  deps = [
1851
1850
  ("typer", "CLI framework"),
1852
1851
  ("rich", "Terminal UI"),
1853
- ("gitpython", "Git integration"),
1852
+ ("git", "Git integration"),
1854
1853
  ("fastapi", "REST API"),
1855
1854
  ("uvicorn", "ASGI server"),
1856
1855
  ("pydantic", "Data validation"),
1857
- ("pyjwt", "JWT auth"),
1856
+ ("jwt", "JWT auth"),
1858
1857
  ("bcrypt", "Password hashing"),
1859
1858
  ]
1860
1859
  for mod_name, desc in deps:
@@ -2086,12 +2085,14 @@ def update(
2086
2085
  def demo(
2087
2086
  reset: bool = typer.Option(False, "--reset", help="Clear all demo data"),
2088
2087
  data_only: bool = typer.Option(False, "--data-only", help="Seed data only, don't start dashboard"),
2088
+ repo: Optional[Path] = typer.Option(None, "--repo", "-r", help="Git repo directory"),
2089
2089
  ) -> None:
2090
2090
  """Load demo data into the database (idempotent) and open the dashboard."""
2091
2091
  from codedna.demo import seed_demo_data, clear_demo_data, is_demo_active
2092
2092
  from codedna.db import get_db_path
2093
2093
 
2094
- db_path = get_db_path()
2094
+ root = repo or find_git_root() or Path.cwd()
2095
+ db_path = get_db_path(root)
2095
2096
 
2096
2097
  console.print()
2097
2098
  if reset:
@@ -2175,6 +2176,7 @@ def demo(
2175
2176
  @app.command(name="security-check")
2176
2177
  def security_check(
2177
2178
  path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project root to scan (defaults to cwd)"),
2179
+ repo: Optional[Path] = typer.Option(None, "--repo", "-r", help="Git repo directory"),
2178
2180
  strict: bool = typer.Option(False, "--strict", help="Exit 1 on any warning"),
2179
2181
  ) -> None:
2180
2182
  """Scan the project for personal paths, secrets, and missing .gitignore rules.
@@ -2188,7 +2190,7 @@ def security_check(
2188
2190
  from codedna.git_hook import find_git_root
2189
2191
  import re
2190
2192
 
2191
- root = path or find_git_root() or Path.cwd()
2193
+ root = repo or path or find_git_root() or Path.cwd()
2192
2194
  if not root.exists():
2193
2195
  console.print(f" [red]✗[/red] Path not found: {root}")
2194
2196
  raise typer.Exit(1)
@@ -119,15 +119,25 @@ def save_commit(
119
119
  ) -> None:
120
120
  """Save or update a commit record."""
121
121
  with get_connection(db_path) as conn:
122
- conn.execute(
123
- """
124
- INSERT INTO commits (commit_hash, author, timestamp, files_changed, understanding_score)
125
- VALUES (?, ?, ?, ?, ?)
126
- ON CONFLICT(commit_hash) DO UPDATE SET
127
- understanding_score = excluded.understanding_score
128
- """,
129
- (commit_hash, author, timestamp, files_changed, understanding_score),
130
- )
122
+ if understanding_score is not None:
123
+ conn.execute(
124
+ """
125
+ INSERT INTO commits (commit_hash, author, timestamp, files_changed, understanding_score)
126
+ VALUES (?, ?, ?, ?, ?)
127
+ ON CONFLICT(commit_hash) DO UPDATE SET
128
+ understanding_score = excluded.understanding_score
129
+ """,
130
+ (commit_hash, author, timestamp, files_changed, understanding_score),
131
+ )
132
+ else:
133
+ conn.execute(
134
+ """
135
+ INSERT INTO commits (commit_hash, author, timestamp, files_changed, understanding_score)
136
+ VALUES (?, ?, ?, ?, NULL)
137
+ ON CONFLICT(commit_hash) DO NOTHING
138
+ """,
139
+ (commit_hash, author, timestamp, files_changed),
140
+ )
131
141
 
132
142
 
133
143
  def save_file_score(
@@ -6,7 +6,7 @@ import { useTranslation } from "@/lib/i18n";
6
6
  import { getCurrentPlan } from "@/lib/plan";
7
7
  import { FeatureGate } from "@/components/FeatureGate";
8
8
  import { ErrorBanner } from "@/components/ErrorBanner";
9
- import { CostInfoTooltip } from "@/components/CostInfoTooltip";
9
+
10
10
 
11
11
  const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
12
12
 
@@ -77,7 +77,6 @@ export default function AIComparePage() {
77
77
  <div className="flex items-start gap-2 bg-amber-500/10 border border-amber-500/20 rounded-xl px-4 py-3">
78
78
  <span className="text-amber-400 mt-0.5">⚠️</span>
79
79
  <p className="text-xs text-amber-400/80">{t("ai_compare_disclaimer")}</p>
80
- <CostInfoTooltip />
81
80
  </div>
82
81
 
83
82
  {error && <ErrorBanner />}
@@ -40,7 +40,7 @@ export async function POST(req: NextRequest) {
40
40
  }
41
41
 
42
42
  return NextResponse.json(data);
43
- } catch (e) {
43
+ } catch {
44
44
  return NextResponse.json(
45
45
  { detail: "Cannot connect to API." },
46
46
  { status: 503 }
@@ -107,7 +107,7 @@ export default function BillingPage() {
107
107
  .then((d) => {
108
108
  if (d) setSubscription({ plan: d.plan, subscription_status: d.subscription_status, lemonsqueezy_customer_id: null });
109
109
  })
110
- .catch(() => {});
110
+ .catch(() => setCheckoutError("Could not load subscription data."));
111
111
  }, [user]);
112
112
 
113
113
  const handleUpgrade = async (plan: Plan) => {
@@ -145,8 +145,8 @@ export default function BillingPage() {
145
145
  } else {
146
146
  alert("Customer portal coming soon. For now, use: codedna plan demo");
147
147
  }
148
- } catch (e) {
149
- alert("Error: " + (e instanceof Error ? e.message : "Unknown"));
148
+ } catch {
149
+ alert("Error: Could not connect to customer portal.");
150
150
  } finally {
151
151
  setCancelLoading(false);
152
152
  }
@@ -42,7 +42,7 @@ export default function RootLayout({
42
42
  <div className="max-w-7xl mx-auto px-6 py-4 text-xs text-gray-700 flex items-center justify-between">
43
43
  <span>🧬 CodeDNA</span>
44
44
  <a
45
- href="http://localhost:8000/docs"
45
+ href={`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"}/docs`}
46
46
  target="_blank"
47
47
  rel="noopener noreferrer"
48
48
  className="hover:text-gray-500 transition-colors"
@@ -114,7 +114,7 @@ export default function ProtectedPage() {
114
114
  const violations = modules.filter((m) => m.status === "VIOLATION");
115
115
 
116
116
  return (
117
- <FeatureGate feature="bus_factor" plan={plan}>
117
+ <FeatureGate feature="protect_modules" plan={plan}>
118
118
  <div className="space-y-6">
119
119
  <div>
120
120
  <h1 className="text-2xl font-bold text-white">🛡️ {t("protect_title")}</h1>
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { useTranslation } from "@/lib/i18n";
4
4
 
5
- /** Banner shown when API connection fails */
6
5
  export function ErrorBanner({ message }: { message?: string }) {
7
6
  const { t } = useTranslation();
8
7
 
@@ -11,11 +10,11 @@ export function ErrorBanner({ message }: { message?: string }) {
11
10
  <span className="text-red-400 text-lg mt-0.5">⚠️</span>
12
11
  <div>
13
12
  <p className="text-red-400 font-semibold text-sm">
14
- {t("error_api").split("—")[0].trim()}
15
- </p>
16
- <p className="text-red-300/70 text-xs mt-0.5">
17
- {message ?? t("error_api")}
13
+ {t("error_api")}
18
14
  </p>
15
+ {message && (
16
+ <p className="text-red-300/70 text-xs mt-0.5">{message}</p>
17
+ )}
19
18
  </div>
20
19
  </div>
21
20
  );
@@ -21,16 +21,16 @@ export function NavClient() {
21
21
  <Link href="/" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_dashboard")}</Link>
22
22
  <Link href="/files" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_files")}</Link>
23
23
  <Link href="/commits" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_commits")}</Link>
24
- <Link href="/bus-factor" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🚌</Link>
25
- <Link href="/debt" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">💰</Link>
26
- <Link href="/sprints" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🏃</Link>
27
- <Link href="/ai-compare" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🤖</Link>
28
- <Link href="/onboarding" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🚀</Link>
29
- <Link href="/settings/integrations" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">⚙️</Link>
30
- <Link href="/protected" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🛡️</Link>
31
- <Link href="/interview" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🎯</Link>
24
+ <Link href="/bus-factor" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Bus Factor">🚌</Link>
25
+ <Link href="/debt" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Technical Debt">💰</Link>
26
+ <Link href="/sprints" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Sprints">🏃</Link>
27
+ <Link href="/ai-compare" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="AI Comparison">🤖</Link>
28
+ <Link href="/onboarding" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Onboarding">🚀</Link>
29
+ <Link href="/settings/integrations" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Integrations">⚙️</Link>
30
+ <Link href="/protected" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Protected Modules">🛡️</Link>
31
+ <Link href="/interview" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Interview">🎯</Link>
32
32
  <Link href="/pricing" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_pricing")}</Link>
33
- <a href="http://localhost:8000/docs" target="_blank" rel="noopener noreferrer" className="text-gray-600 hover:text-gray-400 transition-colors text-xs">API ↗</a>
33
+ <a href={`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"}/docs`} target="_blank" rel="noopener noreferrer" className="text-gray-600 hover:text-gray-400 transition-colors text-xs">API ↗</a>
34
34
 
35
35
  {/* Auth state */}
36
36
  {user ? (
@@ -95,10 +95,6 @@ async function apiFetch<T>(path: string): Promise<T> {
95
95
  return res.json() as Promise<T>;
96
96
  }
97
97
 
98
- export async function getHealth(): Promise<HealthResponse> {
99
- return apiFetch<HealthResponse>("/health");
100
- }
101
-
102
98
  export async function getRepoSummary(): Promise<RepoSummary> {
103
99
  return apiFetch<RepoSummary>("/repo/summary");
104
100
  }
@@ -9,8 +9,6 @@
9
9
  * - The client side only knows whether the user is logged in
10
10
  */
11
11
 
12
- const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
13
-
14
12
  export interface UserData {
15
13
  user_id: number;
16
14
  email: string;
@@ -18,12 +16,6 @@ export interface UserData {
18
16
  subscription_status: string;
19
17
  }
20
18
 
21
- export interface SubscriptionData {
22
- plan: string;
23
- subscription_status: string;
24
- lemonsqueezy_customer_id: string | null;
25
- }
26
-
27
19
  /** Log in — via Next.js API route (sets httpOnly cookie) */
28
20
  export async function login(
29
21
  email: string,
@@ -95,37 +87,4 @@ export async function getCurrentUser(): Promise<UserData | null> {
95
87
  }
96
88
  }
97
89
 
98
- /** Request checkout URL directly from FastAPI */
99
- export async function getCheckoutUrl(
100
- plan: string,
101
- token: string
102
- ): Promise<string | null> {
103
- try {
104
- const res = await fetch(`${API_URL}/billing/checkout`, {
105
- method: "POST",
106
- headers: {
107
- "Content-Type": "application/json",
108
- Authorization: `Bearer ${token}`,
109
- },
110
- body: JSON.stringify({ plan }),
111
- });
112
- if (!res.ok) return null;
113
- const data = await res.json();
114
- return data.checkout_url ?? null;
115
- } catch {
116
- return null;
117
- }
118
- }
119
90
 
120
- /** Fetch subscription status */
121
- export async function getSubscriptionStatus(token: string): Promise<SubscriptionData | null> {
122
- try {
123
- const res = await fetch(`${API_URL}/billing/subscription`, {
124
- headers: { Authorization: `Bearer ${token}` },
125
- });
126
- if (!res.ok) return null;
127
- return res.json();
128
- } catch {
129
- return null;
130
- }
131
- }
@@ -5,17 +5,18 @@
5
5
  export type Plan = "free" | "pro" | "team" | "enterprise";
6
6
 
7
7
  export interface PlanLimits {
8
- max_repos: number; // -1 = unlimited
9
- max_files_scan: number; // -1 = unlimited
10
- history_days: number; // -1 = unlimited
8
+ max_repos: number;
9
+ max_files_scan: number;
10
+ history_days: number;
11
11
  dashboard_access: boolean;
12
12
  github_actions: boolean;
13
13
  slack_notify: boolean;
14
- bus_factor: boolean; // Phase 5
15
- sprint_health: boolean; // Phase 6
16
- ai_comparison: boolean; // Phase 7
17
- team_members: number; // -1 = unlimited
18
- interview_tool: boolean; // Phase 8
14
+ bus_factor: boolean;
15
+ sprint_health: boolean;
16
+ ai_comparison: boolean;
17
+ team_members: number;
18
+ interview_tool: boolean;
19
+ protect_modules: boolean;
19
20
  }
20
21
 
21
22
  export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
@@ -23,7 +24,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
23
24
  max_repos: 1,
24
25
  max_files_scan: 50,
25
26
  history_days: 7,
26
- dashboard_access: false,
27
+ dashboard_access: true,
27
28
  github_actions: false,
28
29
  slack_notify: false,
29
30
  bus_factor: false,
@@ -31,6 +32,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
31
32
  ai_comparison: false,
32
33
  team_members: 1,
33
34
  interview_tool: false,
35
+ protect_modules: false,
34
36
  },
35
37
  pro: {
36
38
  max_repos: -1,
@@ -44,6 +46,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
44
46
  ai_comparison: false,
45
47
  team_members: 1,
46
48
  interview_tool: false,
49
+ protect_modules: false,
47
50
  },
48
51
  team: {
49
52
  max_repos: -1,
@@ -57,6 +60,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
57
60
  ai_comparison: false,
58
61
  team_members: 10,
59
62
  interview_tool: false,
63
+ protect_modules: true,
60
64
  },
61
65
  enterprise: {
62
66
  max_repos: -1,
@@ -70,6 +74,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
70
74
  ai_comparison: true,
71
75
  team_members: -1,
72
76
  interview_tool: true,
77
+ protect_modules: true,
73
78
  },
74
79
  };
75
80
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codedna"
3
- version = "0.8.1"
3
+ version = "0.8.3"
4
4
  description = "AI Code Transparency Tool - detect AI-written code and measure developer understanding"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
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