codedna 0.8.2__tar.gz → 0.8.4__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.2 → codedna-0.8.4}/PKG-INFO +1 -1
  2. {codedna-0.8.2 → codedna-0.8.4}/codedna/__init__.py +1 -1
  3. {codedna-0.8.2 → codedna-0.8.4}/codedna/cli.py +44 -11
  4. {codedna-0.8.2 → codedna-0.8.4}/codedna/db.py +19 -9
  5. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/ai-compare/page.tsx +1 -2
  6. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/api/billing/checkout/route.ts +1 -1
  7. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/billing/page.tsx +3 -3
  8. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/layout.tsx +1 -1
  9. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/protected/page.tsx +1 -1
  10. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/ErrorBanner.tsx +4 -5
  11. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/NavClient.tsx +9 -9
  12. {codedna-0.8.2 → codedna-0.8.4}/dashboard/lib/api.ts +0 -4
  13. {codedna-0.8.2 → codedna-0.8.4}/dashboard/lib/auth.ts +0 -41
  14. {codedna-0.8.2 → codedna-0.8.4}/dashboard/lib/plan.ts +14 -9
  15. {codedna-0.8.2 → codedna-0.8.4}/pyproject.toml +1 -1
  16. {codedna-0.8.2 → codedna-0.8.4}/.gitignore +0 -0
  17. {codedna-0.8.2 → codedna-0.8.4}/LICENSE +0 -0
  18. {codedna-0.8.2 → codedna-0.8.4}/README.md +0 -0
  19. {codedna-0.8.2 → codedna-0.8.4}/codedna/ai.py +0 -0
  20. {codedna-0.8.2 → codedna-0.8.4}/codedna/ai_box.py +0 -0
  21. {codedna-0.8.2 → codedna-0.8.4}/codedna/ai_fingerprint.py +0 -0
  22. {codedna-0.8.2 → codedna-0.8.4}/codedna/analyzer.py +0 -0
  23. {codedna-0.8.2 → codedna-0.8.4}/codedna/api.py +0 -0
  24. {codedna-0.8.2 → codedna-0.8.4}/codedna/auth.py +0 -0
  25. {codedna-0.8.2 → codedna-0.8.4}/codedna/bus_factor.py +0 -0
  26. {codedna-0.8.2 → codedna-0.8.4}/codedna/demo.py +0 -0
  27. {codedna-0.8.2 → codedna-0.8.4}/codedna/git_hook.py +0 -0
  28. {codedna-0.8.2 → codedna-0.8.4}/codedna/integrations/__init__.py +0 -0
  29. {codedna-0.8.2 → codedna-0.8.4}/codedna/integrations/github_bot.py +0 -0
  30. {codedna-0.8.2 → codedna-0.8.4}/codedna/integrations/jira.py +0 -0
  31. {codedna-0.8.2 → codedna-0.8.4}/codedna/integrations/lemonsqueezy.py +0 -0
  32. {codedna-0.8.2 → codedna-0.8.4}/codedna/interview.py +0 -0
  33. {codedna-0.8.2 → codedna-0.8.4}/codedna/onboarding.py +0 -0
  34. {codedna-0.8.2 → codedna-0.8.4}/codedna/plan.py +0 -0
  35. {codedna-0.8.2 → codedna-0.8.4}/codedna/protection.py +0 -0
  36. {codedna-0.8.2 → codedna-0.8.4}/codedna/rate_limit.py +0 -0
  37. {codedna-0.8.2 → codedna-0.8.4}/codedna/scorer.py +0 -0
  38. {codedna-0.8.2 → codedna-0.8.4}/codedna/sprint_health.py +0 -0
  39. {codedna-0.8.2 → codedna-0.8.4}/codedna/survey.py +0 -0
  40. {codedna-0.8.2 → codedna-0.8.4}/codedna/tech_debt.py +0 -0
  41. {codedna-0.8.2 → codedna-0.8.4}/dashboard/.env.local.example +0 -0
  42. {codedna-0.8.2 → codedna-0.8.4}/dashboard/.gitignore +0 -0
  43. {codedna-0.8.2 → codedna-0.8.4}/dashboard/README.md +0 -0
  44. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/api/auth/login/route.ts +0 -0
  45. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/api/auth/logout/route.ts +0 -0
  46. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/api/auth/me/route.ts +0 -0
  47. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/api/auth/register/route.ts +0 -0
  48. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/bus-factor/page.tsx +0 -0
  49. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/commits/page.tsx +0 -0
  50. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/debt/page.tsx +0 -0
  51. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/favicon.ico +0 -0
  52. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/files/page.tsx +0 -0
  53. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/fonts/GeistMonoVF.woff +0 -0
  54. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/fonts/GeistVF.woff +0 -0
  55. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/globals.css +0 -0
  56. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/interview/page.tsx +0 -0
  57. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/login/page.tsx +0 -0
  58. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/onboarding/page.tsx +0 -0
  59. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/page.tsx +0 -0
  60. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/pricing/page.tsx +0 -0
  61. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/register/page.tsx +0 -0
  62. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/settings/integrations/page.tsx +0 -0
  63. {codedna-0.8.2 → codedna-0.8.4}/dashboard/app/sprints/page.tsx +0 -0
  64. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/AuthProvider.tsx +0 -0
  65. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/ClientProviders.tsx +0 -0
  66. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/CommitTable.tsx +0 -0
  67. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/CostInfoTooltip.tsx +0 -0
  68. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/FeatureGate.tsx +0 -0
  69. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/FileHeatmap.tsx +0 -0
  70. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/LanguageSwitcher.tsx +0 -0
  71. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/OverviewTitles.tsx +0 -0
  72. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/QuickInsights.tsx +0 -0
  73. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/RiskBadge.tsx +0 -0
  74. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/SummaryCards.tsx +0 -0
  75. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/UnderstandingChart.tsx +0 -0
  76. {codedna-0.8.2 → codedna-0.8.4}/dashboard/components/UpgradeBanner.tsx +0 -0
  77. {codedna-0.8.2 → codedna-0.8.4}/dashboard/lib/i18n/en.ts +0 -0
  78. {codedna-0.8.2 → codedna-0.8.4}/dashboard/lib/i18n/index.tsx +0 -0
  79. {codedna-0.8.2 → codedna-0.8.4}/dashboard/lib/i18n/tr.ts +0 -0
  80. {codedna-0.8.2 → codedna-0.8.4}/dashboard/next-env.d.ts +0 -0
  81. {codedna-0.8.2 → codedna-0.8.4}/dashboard/next.config.mjs +0 -0
  82. {codedna-0.8.2 → codedna-0.8.4}/dashboard/package-lock.json +0 -0
  83. {codedna-0.8.2 → codedna-0.8.4}/dashboard/package.json +0 -0
  84. {codedna-0.8.2 → codedna-0.8.4}/dashboard/postcss.config.mjs +0 -0
  85. {codedna-0.8.2 → codedna-0.8.4}/dashboard/tailwind.config.ts +0 -0
  86. {codedna-0.8.2 → codedna-0.8.4}/dashboard/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codedna
3
- Version: 0.8.2
3
+ Version: 0.8.4
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
@@ -1,4 +1,4 @@
1
1
  """CodeDNA — AI Code Transparency Tool."""
2
2
 
3
- __version__ = "0.8.2"
3
+ __version__ = "0.8.4"
4
4
  __app_name__ = "codedna"
@@ -217,6 +217,19 @@ def scan(
217
217
  f"Risk: [bold {risk_color}]{risk_label}[/bold {risk_color}]\n"
218
218
  )
219
219
 
220
+ # AI analysis box
221
+ try:
222
+ from codedna.ai_box import print_ai_box
223
+ summary = (
224
+ f"CodeDNA scan: {len(results)} files\n"
225
+ f"Avg AI probability: {avg_ai:.0f}%\n"
226
+ f"Risk level: {risk_label}\n"
227
+ f"Files: {', '.join(str(root / s.file_path) for s in results[:10])}"
228
+ )
229
+ print_ai_box("codedna scan", summary)
230
+ except Exception:
231
+ pass
232
+
220
233
 
221
234
  # ---------------------------------------------------------------------------
222
235
  # codedna status
@@ -283,8 +296,8 @@ def status(
283
296
  )
284
297
 
285
298
  # Show summary
299
+ avg_ai = sum(s.ai_probability for s in results) / len(results) if results else 0.0
286
300
  if results:
287
- avg_ai = sum(s.ai_probability for s in results) / len(results)
288
301
  risk_label, risk_color = _risk_label(avg_ai * 100)
289
302
 
290
303
  understanding_display = (
@@ -316,13 +329,32 @@ def status(
316
329
  console.print(f"[bold red]{warning}[/bold red]")
317
330
  except Exception:
318
331
  pass # Never breaks the hook
332
+ return
319
333
 
320
- else:
334
+ if not results:
321
335
  console.print(
322
336
  f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim]\n"
323
337
  "[dim]No supported code files found in this commit.[/dim]"
324
338
  )
325
339
 
340
+ # AI analysis box
341
+ try:
342
+ from codedna.ai_box import print_ai_box
343
+ if results:
344
+ status_summary = (
345
+ f"Commit: {commit_hash[:8]}\n"
346
+ f"Author: {author}\n"
347
+ f"Message: {message}\n"
348
+ f"Files changed: {len(results)}\n"
349
+ f"Avg AI probability: {avg_ai*100:.0f}%\n"
350
+ f"Files: {', '.join(s.file_path for s in results[:5])}"
351
+ )
352
+ else:
353
+ status_summary = f"Commit: {commit_hash[:8]}\nNo supported code files."
354
+ print_ai_box("codedna status", status_summary)
355
+ except Exception:
356
+ pass
357
+
326
358
 
327
359
  # ---------------------------------------------------------------------------
328
360
  # codedna history
@@ -1093,8 +1125,7 @@ def bus_factor(
1093
1125
  if not is_feature_available("bus_factor"):
1094
1126
  console.print(
1095
1127
  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"
1128
+ "[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n\n"
1098
1129
  "[dim]To upgrade:[/dim] [cyan]codedna plan activate <LICENSE_KEY>[/cyan]",
1099
1130
  border_style="yellow",
1100
1131
  padding=(1, 2),
@@ -1311,8 +1342,7 @@ def sprint_create(
1311
1342
  if not is_feature_available("sprint_health"):
1312
1343
  console.print(
1313
1344
  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"
1345
+ "[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n\n"
1316
1346
  "[dim]To upgrade:[/dim] [cyan]codedna plan activate <LICENSE_KEY>[/cyan]",
1317
1347
  border_style="yellow",
1318
1348
  padding=(1, 2),
@@ -1719,7 +1749,7 @@ def uninstall(
1719
1749
  """Remove CodeDNA hook."""
1720
1750
  root = repo or find_git_root()
1721
1751
  if uninstall_hook(root):
1722
- console.print("[green]✓[/green] CodeDNA uninstalled.")
1752
+ console.print("[green]✓[/green] CodeDNA hook removed.")
1723
1753
  else:
1724
1754
  raise typer.Exit(1)
1725
1755
 
@@ -1850,11 +1880,11 @@ def doctor(
1850
1880
  deps = [
1851
1881
  ("typer", "CLI framework"),
1852
1882
  ("rich", "Terminal UI"),
1853
- ("gitpython", "Git integration"),
1883
+ ("git", "Git integration"),
1854
1884
  ("fastapi", "REST API"),
1855
1885
  ("uvicorn", "ASGI server"),
1856
1886
  ("pydantic", "Data validation"),
1857
- ("pyjwt", "JWT auth"),
1887
+ ("jwt", "JWT auth"),
1858
1888
  ("bcrypt", "Password hashing"),
1859
1889
  ]
1860
1890
  for mod_name, desc in deps:
@@ -2086,12 +2116,14 @@ def update(
2086
2116
  def demo(
2087
2117
  reset: bool = typer.Option(False, "--reset", help="Clear all demo data"),
2088
2118
  data_only: bool = typer.Option(False, "--data-only", help="Seed data only, don't start dashboard"),
2119
+ repo: Optional[Path] = typer.Option(None, "--repo", "-r", help="Git repo directory"),
2089
2120
  ) -> None:
2090
2121
  """Load demo data into the database (idempotent) and open the dashboard."""
2091
2122
  from codedna.demo import seed_demo_data, clear_demo_data, is_demo_active
2092
2123
  from codedna.db import get_db_path
2093
2124
 
2094
- db_path = get_db_path()
2125
+ root = repo or find_git_root() or Path.cwd()
2126
+ db_path = get_db_path(root)
2095
2127
 
2096
2128
  console.print()
2097
2129
  if reset:
@@ -2175,6 +2207,7 @@ def demo(
2175
2207
  @app.command(name="security-check")
2176
2208
  def security_check(
2177
2209
  path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project root to scan (defaults to cwd)"),
2210
+ repo: Optional[Path] = typer.Option(None, "--repo", "-r", help="Git repo directory"),
2178
2211
  strict: bool = typer.Option(False, "--strict", help="Exit 1 on any warning"),
2179
2212
  ) -> None:
2180
2213
  """Scan the project for personal paths, secrets, and missing .gitignore rules.
@@ -2188,7 +2221,7 @@ def security_check(
2188
2221
  from codedna.git_hook import find_git_root
2189
2222
  import re
2190
2223
 
2191
- root = path or find_git_root() or Path.cwd()
2224
+ root = repo or path or find_git_root() or Path.cwd()
2192
2225
  if not root.exists():
2193
2226
  console.print(f" [red]✗[/red] Path not found: {root}")
2194
2227
  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.2"
3
+ version = "0.8.4"
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
File without changes