codedna 0.2.3__py3-none-any.whl → 0.2.4__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """CodeDNA — AI Kod Şeffaflık Aracı."""
2
2
 
3
- __version__ = "0.2.3"
3
+ __version__ = "0.2.4"
4
4
  __app_name__ = "codedna"
codedna/ai.py ADDED
@@ -0,0 +1,221 @@
1
+ """
2
+ AI Analiz Katmanı - Kullanici komut sonuclarini AI ile yorumlar.
3
+
4
+ Desteklenen provider'lar:
5
+ - Anthropic (Claude)
6
+ - MiniMax
7
+ - OpenAI
8
+
9
+ Key sadece analiz icin kullanilir, sohbet/tamamlama yok.
10
+ """
11
+ import base64
12
+ import json
13
+ import os
14
+ import urllib.error
15
+ import urllib.request
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+ from typing import Optional
19
+
20
+ AI_CONFIG_PATH = Path.home() / ".codedna" / "ai_config.json"
21
+
22
+ # Provider listesi
23
+ PROVIDERS = ["anthropic", "minimax", "openai"]
24
+
25
+ # Default modeller
26
+ DEFAULT_MODELS = {
27
+ "anthropic": "claude-sonnet-4-20250514",
28
+ "minimax": "MiniMax-M2.5",
29
+ "openai": "gpt-4o-mini",
30
+ }
31
+
32
+ # Provider API endpoint
33
+ API_ENDPOINTS = {
34
+ "anthropic": "https://api.anthropic.com/v1/messages",
35
+ "minimax": "https://api.minimax.io/v1/text/chatcompletion_v2",
36
+ "openai": "https://api.openai.com/v1/chat/completions",
37
+ }
38
+
39
+
40
+ @dataclass
41
+ class AIConfig:
42
+ """AI analiz konfigurasyonu."""
43
+ provider: str
44
+ api_key: str
45
+ model: str
46
+ enabled: bool = True
47
+
48
+ @classmethod
49
+ def load(cls) -> Optional["AIConfig"]:
50
+ """Config dosyasindan yukle."""
51
+ if not AI_CONFIG_PATH.exists():
52
+ return None
53
+ try:
54
+ data = json.loads(AI_CONFIG_PATH.read_text())
55
+ return cls(
56
+ provider=data.get("provider", "anthropic"),
57
+ api_key=data.get("api_key", ""),
58
+ model=data.get("model", DEFAULT_MODELS.get(data.get("provider", "anthropic"), "")),
59
+ enabled=data.get("enabled", True),
60
+ )
61
+ except Exception:
62
+ return None
63
+
64
+ def save(self) -> None:
65
+ """Config'i dosyaya yaz."""
66
+ AI_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
67
+ data = {
68
+ "provider": self.provider,
69
+ "api_key": self.api_key,
70
+ "model": self.model,
71
+ "enabled": self.enabled,
72
+ }
73
+ AI_CONFIG_PATH.write_text(json.dumps(data, ensure_ascii=False, indent=2))
74
+ try:
75
+ os.chmod(AI_CONFIG_PATH, 0o600)
76
+ except OSError:
77
+ pass
78
+
79
+ @classmethod
80
+ def clear(cls) -> None:
81
+ """Config'i sil."""
82
+ if AI_CONFIG_PATH.exists():
83
+ AI_CONFIG_PATH.unlink()
84
+
85
+
86
+ def _call_anthropic(api_key: str, model: str, prompt: str) -> str:
87
+ """Anthropic Claude API cagirisi."""
88
+ payload = {
89
+ "model": model,
90
+ "max_tokens": 500,
91
+ "messages": [
92
+ {"role": "user", "content": prompt}
93
+ ],
94
+ }
95
+ req = urllib.request.Request(
96
+ API_ENDPOINTS["anthropic"],
97
+ data=json.dumps(payload).encode(),
98
+ headers={
99
+ "x-api-key": api_key,
100
+ "anthropic-version": "2023-06-01",
101
+ "Content-Type": "application/json",
102
+ },
103
+ method="POST",
104
+ )
105
+ with urllib.request.urlopen(req, timeout=30) as resp:
106
+ result = json.loads(resp.read())
107
+ # Response: {content: [{text: "..."}], ...}
108
+ return result.get("content", [{}])[0].get("text", "")
109
+
110
+
111
+ def _call_minimax(api_key: str, model: str, prompt: str) -> str:
112
+ """MiniMax API cagirisi."""
113
+ payload = {
114
+ "model": model,
115
+ "messages": [
116
+ {"role": "system", "content": "Sen CodeDNA'nin AI analiz asistanisin. Kullanici komut ciktilarini yorumlarsin. Sohbet etme, sadece analiz ve oneriler sun."},
117
+ {"role": "user", "content": prompt}
118
+ ],
119
+ }
120
+ req = urllib.request.Request(
121
+ API_ENDPOINTS["minimax"],
122
+ data=json.dumps(payload).encode(),
123
+ headers={
124
+ "Authorization": f"Bearer {api_key}",
125
+ "Content-Type": "application/json",
126
+ },
127
+ method="POST",
128
+ )
129
+ with urllib.request.urlopen(req, timeout=30) as resp:
130
+ result = json.loads(resp.read())
131
+ return result.get("choices", [{}])[0].get("message", {}).get("content", "")
132
+
133
+
134
+ def _call_openai(api_key: str, model: str, prompt: str) -> str:
135
+ """OpenAI API cagirisi."""
136
+ payload = {
137
+ "model": model,
138
+ "max_tokens": 500,
139
+ "messages": [
140
+ {"role": "system", "content": "Sen CodeDNA'nin AI analiz asistanisin. Kullanici komut ciktilarini yorumlarsin. Sohbet etme, sadece analiz ve oneriler sun."},
141
+ {"role": "user", "content": prompt}
142
+ ],
143
+ }
144
+ req = urllib.request.Request(
145
+ API_ENDPOINTS["openai"],
146
+ data=json.dumps(payload).encode(),
147
+ headers={
148
+ "Authorization": f"Bearer {api_key}",
149
+ "Content-Type": "application/json",
150
+ },
151
+ method="POST",
152
+ )
153
+ with urllib.request.urlopen(req, timeout=30) as resp:
154
+ result = json.loads(resp.read())
155
+ return result.get("choices", [{}])[0].get("message", {}).get("content", "")
156
+
157
+
158
+ def ai_analyze(command: str, output: str, config: Optional[AIConfig] = None) -> Optional[str]:
159
+ """
160
+ Komut ciktisini AI ile analiz et.
161
+
162
+ Returns:
163
+ Analiz metni veya None (config yoksa / hata durumunda)
164
+ """
165
+ if config is None:
166
+ config = AIConfig.load()
167
+ if config is None or not config.enabled or not config.api_key:
168
+ return None
169
+
170
+ # Plan kontrolu - sadece Pro+ kullanicilar AI analiz kullanabilir
171
+ try:
172
+ from codedna.plan import get_current_plan, Plan as PlanEnum
173
+ plan = get_current_plan()
174
+ if plan == PlanEnum.FREE:
175
+ return None # Free planda AI analiz yok
176
+ except Exception:
177
+ return None # Plan yuklenemedi, gosterme
178
+
179
+ # Prompt
180
+ prompt = f"""Sen CodeDNA'nin AI analiz katmanisin.
181
+
182
+ KOMUT: {command}
183
+ CIKTI: {output[:2000]}
184
+
185
+ Bu ciktiyi 3-4 cumlede analiz et:
186
+ 1. Sonuc ne anlama geliyor? (iyi/kotu/notr)
187
+ 2. Somut oneriler (numbered list, max 3)
188
+ 3. Varsa sorun ve cozumu
189
+
190
+ Kurallar:
191
+ - Sohbet etme, sadece yorum
192
+ - Maksimum 100 kelime
193
+ - Turkce yanit ver
194
+ - Emoji kullanabilirsin (ama az)"""
195
+
196
+ try:
197
+ if config.provider == "anthropic":
198
+ return _call_anthropic(config.api_key, config.model, prompt)
199
+ elif config.provider == "minimax":
200
+ return _call_minimax(config.api_key, config.model, prompt)
201
+ elif config.provider == "openai":
202
+ return _call_openai(config.api_key, config.model, prompt)
203
+ else:
204
+ return None
205
+ except Exception as e:
206
+ return f"AI analiz hatasi: {type(e).__name__}"
207
+
208
+
209
+ def is_enabled() -> bool:
210
+ """AI analiz aktif mi? (Sadece Pro+ uyeler)"""
211
+ config = AIConfig.load()
212
+ if config is None or not config.enabled or not config.api_key:
213
+ return False
214
+ try:
215
+ from codedna.plan import get_current_plan, Plan as PlanEnum
216
+ plan = get_current_plan()
217
+ if plan == PlanEnum.FREE:
218
+ return False
219
+ except Exception:
220
+ return False
221
+ return True
codedna/cli.py CHANGED
@@ -45,7 +45,7 @@ def _version_callback(value: bool) -> None:
45
45
  from importlib.metadata import version as _v
46
46
  ver = _v("codedna")
47
47
  except Exception:
48
- ver = "0.2.2"
48
+ ver = "0.2.3"
49
49
  console.print(f"codedna {ver}")
50
50
  raise typer.Exit()
51
51
 
@@ -125,7 +125,7 @@ def init(
125
125
  # ---------------------------------------------------------------------------
126
126
  @app.command()
127
127
  def doctor() -> None:
128
- """Sistem sağlık kontrolü — bağımlılıkları, auth, DB, hook kontrol eder."""
128
+ """Sistem sağlık kontrolü."""
129
129
  console.print()
130
130
  console.print(
131
131
  Panel.fit(
@@ -138,36 +138,31 @@ def doctor() -> None:
138
138
 
139
139
  kontroller = []
140
140
 
141
- # 1. Python versiyonu
142
141
  py_ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
143
142
  py_ok = sys.version_info >= (3, 10)
144
- kontroller.append(("Python versiyonu", f"v{py_ver}", py_ok, f"v{py_ver} (>=3.10 gerekli)" if py_ok else f"v{py_ver} ESKI (>=3.10 gerekli)"))
143
+ kontroller.append(("Python versiyonu", f"v{py_ver}", py_ok, f"v{py_ver} (>=3.10)" if py_ok else "ESKI"))
145
144
 
146
- # 2. Git kurulu mu
147
145
  import shutil
148
146
  git_var = shutil.which("git")
149
147
  git_ok = git_var is not None
150
- kontroller.append(("Git binary", git_var or "yok", git_ok, f"bulundu: {git_var}" if git_ok else "Git kurulu değil!"))
148
+ kontroller.append(("Git binary", git_var or "yok", git_ok, f"bulundu" if git_ok else "yok"))
151
149
 
152
- # 3. Tree-sitter
153
150
  try:
154
151
  import tree_sitter
155
152
  ts_ok = True
156
153
  try:
157
154
  ts_msg = f"v{tree_sitter.__version__}"
158
155
  except AttributeError:
159
- # Yeni versiyonlarda __version__ yok, paketten al
160
156
  try:
161
157
  from importlib.metadata import version as _v
162
158
  ts_msg = f"v{_v('tree-sitter')}"
163
159
  except Exception:
164
- ts_msg = "yüklü (versiyon bilinmiyor)"
160
+ ts_msg = "yüklü"
165
161
  except ImportError:
166
162
  ts_ok = False
167
163
  ts_msg = "yok"
168
- kontroller.append(("Tree-sitter", ts_msg, ts_ok, ts_msg if ts_ok else "tree-sitter yüklü değil"))
164
+ kontroller.append(("Tree-sitter", ts_msg, ts_ok, ts_msg if ts_ok else "yüklü değil"))
169
165
 
170
- # 4. FastAPI
171
166
  try:
172
167
  import fastapi
173
168
  fa_ok = True
@@ -175,40 +170,35 @@ def doctor() -> None:
175
170
  except ImportError:
176
171
  fa_ok = False
177
172
  fa_msg = "yok"
178
- kontroller.append(("FastAPI", fa_msg, fa_ok, fa_msg if fa_ok else "fastapi yüklü değil"))
173
+ kontroller.append(("FastAPI", fa_msg, fa_ok, fa_msg if fa_ok else "yüklü değil"))
179
174
 
180
- # 5. Auth DB
181
175
  try:
182
176
  from codedna.auth import _auth_db
183
177
  db_path = _auth_db()
184
178
  db_ok = db_path.exists()
185
- kontroller.append(("Auth veritabanı", str(db_path), db_ok, f"var ({db_path.stat().st_size} bytes)" if db_ok else "yok, codedna plan demo deneyin"))
179
+ kontroller.append(("Auth veritabanı", str(db_path), db_ok, f"var ({db_path.stat().st_size} bytes)" if db_ok else "yok"))
186
180
  except Exception as e:
187
181
  kontroller.append(("Auth veritabanı", "hata", False, str(e)))
188
182
 
189
- # 6. Plan
190
183
  try:
191
184
  from codedna.plan import get_current_plan
192
185
  plan = get_current_plan()
193
- plan_ok = True
194
- kontroller.append(("Plan", plan.value.upper(), plan_ok, f"plan: {plan.value}"))
186
+ kontroller.append(("Plan", plan.value.upper(), True, f"plan: {plan.value}"))
195
187
  except Exception as e:
196
188
  kontroller.append(("Plan", "hata", False, str(e)))
197
189
 
198
- # 7. Git hook (eğer git repo'daysak)
199
190
  try:
200
191
  from codedna.git_hook import find_git_root
201
192
  git_root = find_git_root()
202
193
  if git_root:
203
194
  hook_path = git_root / ".git" / "hooks" / "post-commit"
204
195
  hook_ok = hook_path.exists()
205
- kontroller.append(("Post-commit hook", str(hook_path) if hook_ok else "yok", hook_ok, "kurulmus" if hook_ok else "codedna init ile kurulabilir"))
196
+ kontroller.append(("Post-commit hook", "var" if hook_ok else "yok", hook_ok, "kurulmus" if hook_ok else "codedna init gerekli"))
206
197
  else:
207
198
  kontroller.append(("Post-commit hook", "—", True, "git repo disinda"))
208
199
  except Exception as e:
209
200
  kontroller.append(("Post-commit hook", "hata", False, str(e)))
210
201
 
211
- # Tablo oluştur
212
202
  tablo = Table(title="[bold]Sistem Durumu[/bold]", show_header=True, header_style="bold magenta", box=None)
213
203
  tablo.add_column("Bileşen", style="cyan", no_wrap=True)
214
204
  tablo.add_column("Durum", justify="center")
@@ -217,11 +207,9 @@ def doctor() -> None:
217
207
  tum_ok = True
218
208
  for ad, deger, ok, detay in kontroller:
219
209
  if ok:
220
- emoji = "[green]✓[/green]"
221
- renk = "green"
210
+ emoji, renk = "[green]✓[/green]", "green"
222
211
  else:
223
- emoji = "[red]✗[/red]"
224
- renk = "red"
212
+ emoji, renk = "[red]✗[/red]", "red"
225
213
  tum_ok = False
226
214
  tablo.add_row(ad, f"{emoji} [{renk}]{deger}[/{renk}]", detay)
227
215
 
@@ -230,9 +218,133 @@ def doctor() -> None:
230
218
  if tum_ok:
231
219
  console.print("[bold green]✓ Tüm sistem sağlıklı![/bold green]")
232
220
  else:
233
- console.print("[bold yellow]⚠ Bazı bileşenlerde sorun var. Yukarıdaki detaylara bakın.[/bold yellow]")
221
+ console.print("[bold yellow]⚠ Bazı bileşenlerde sorun var.[/bold yellow]")
222
+ raise typer.Exit(1)
223
+
224
+
225
+ # ---------------------------------------------------------------------------
226
+ # codedna setup-ai — AI analiz katmanı (Pro+ plan gerekli)
227
+ # ---------------------------------------------------------------------------
228
+ @app.command(name="setup-ai")
229
+ def setup_ai(
230
+ clear: bool = typer.Option(False, "--clear", help="AI konfigürasyonunu sil"),
231
+ show: bool = typer.Option(False, "--show", help="Mevcut konfigürasyonu göster"),
232
+ enable_flag: bool = typer.Option(False, "--on", help="AI analizi etkinleştir"),
233
+ disable_flag: bool = typer.Option(False, "--off", help="AI analizi devre dışı bırak"),
234
+ ) -> None:
235
+ """AI analiz katmanı için API key ayarla (Anthropic, MiniMax, OpenAI). Pro+ plan gerekli."""
236
+ import getpass
237
+ from codedna.ai import AIConfig, AI_CONFIG_PATH, DEFAULT_MODELS, PROVIDERS
238
+
239
+ console.print()
240
+ console.print(
241
+ Panel.fit(
242
+ "[bold cyan]🧬 CodeDNA AI Setup[/bold cyan]\n"
243
+ "[dim]AI analiz katmanı — Pro+ plan gerekli[/dim]",
244
+ border_style="cyan",
245
+ )
246
+ )
247
+ console.print()
248
+
249
+ if clear:
250
+ AIConfig.clear()
251
+ console.print("[bold green]✓[/bold green] AI konfigürasyonu silindi.")
252
+ return
253
+
254
+ if show:
255
+ cfg = AIConfig.load()
256
+ if not cfg:
257
+ console.print("[yellow]AI konfigürasyonu yok.[/yellow]")
258
+ else:
259
+ masked = cfg.api_key[:8] + "..." + cfg.api_key[-4:] if len(cfg.api_key) > 12 else "***"
260
+ console.print(f"[bold]Provider:[/bold] {cfg.provider}")
261
+ console.print(f"[bold]Model:[/bold] {cfg.model}")
262
+ console.print(f"[bold]API Key:[/bold] {masked}")
263
+ console.print(f"[bold]Aktif:[/bold] {'Evet' if cfg.enabled else 'Hayır'}")
264
+ console.print(f"[bold]Config:[/bold] {AI_CONFIG_PATH}")
265
+ return
266
+
267
+ if enable_flag or disable_flag:
268
+ cfg = AIConfig.load()
269
+ if not cfg:
270
+ console.print("[red]Önce key ayarlayın: codedna setup-ai[/red]")
271
+ raise typer.Exit(1)
272
+ cfg.enabled = enable_flag
273
+ cfg.save()
274
+ status = "aktif" if enable_flag else "devre dışı"
275
+ console.print(f"[bold green]✓[/bold green] AI analiz {status}.")
276
+ return
277
+
278
+ # Plan kontrolu
279
+ try:
280
+ from codedna.plan import get_current_plan, Plan as PlanEnum
281
+ plan = get_current_plan()
282
+ if plan == PlanEnum.FREE:
283
+ console.print(
284
+ Panel.fit(
285
+ "[bold yellow]🔒 AI analiz Pro+ plan gerektirir.[/bold yellow]\n\n"
286
+ f"Mevcut plan: [cyan]{plan.value.upper()}[/cyan]\n\n"
287
+ "Demo: [cyan]codedna plan demo pro[/cyan]",
288
+ border_style="yellow",
289
+ )
290
+ )
291
+ raise typer.Exit(1)
292
+ except Exception as e:
293
+ console.print(f"[red]Plan hatası: {e}[/red]")
294
+ raise typer.Exit(1)
295
+
296
+ # Wizard
297
+ console.print("[bold]Provider seçin:[/bold]")
298
+ for i, p in enumerate(PROVIDERS, 1):
299
+ default = DEFAULT_MODELS.get(p, "")
300
+ console.print(f" [cyan]{i}[/cyan]. {p} [dim]({default})[/dim]")
301
+ console.print()
302
+ secim = typer.prompt("Numara", type=int, default=1)
303
+ if secim < 1 or secim > len(PROVIDERS):
304
+ console.print("[red]Geçersiz seçim.[/red]")
305
+ raise typer.Exit(1)
306
+ provider = PROVIDERS[secim - 1]
307
+ default_model = DEFAULT_MODELS[provider]
308
+
309
+ console.print()
310
+ console.print(f"[dim]API key girin ({provider}):[/dim]")
311
+ api_key = getpass.getpass("Key: ").strip()
312
+ if not api_key:
313
+ console.print("[red]Key boş olamaz.[/red]")
314
+ raise typer.Exit(1)
315
+
316
+ console.print()
317
+ model = typer.prompt(f"Model [varsayilan: {default_model}]", default=default_model)
318
+
319
+ console.print()
320
+ console.print("[dim]API bağlantısı test ediliyor...[/dim]")
321
+ try:
322
+ from codedna.ai import ai_analyze
323
+ test_config = AIConfig(provider=provider, api_key=api_key, model=model)
324
+ result = ai_analyze("test", "Bu bir test komutudur.", test_config)
325
+ if result and not result.startswith("AI analiz hatasi"):
326
+ console.print(f"[bold green]✓[/bold green] Bağlantı başarılı!")
327
+ else:
328
+ console.print(f"[bold red]✗[/bold red] Test başarısız: {result}")
329
+ raise typer.Exit(1)
330
+ except Exception as e:
331
+ console.print(f"[bold red]✗[/bold red] Hata: {e}")
234
332
  raise typer.Exit(1)
235
333
 
334
+ config = AIConfig(provider=provider, api_key=api_key, model=model, enabled=True)
335
+ config.save()
336
+ console.print()
337
+ console.print(
338
+ Panel.fit(
339
+ f"[bold green]✓ AI analiz aktif![/bold green]\n\n"
340
+ f"Provider: {provider}\n"
341
+ f"Model: {model}\n"
342
+ f"Config: {AI_CONFIG_PATH}\n\n"
343
+ "[dim]Her komut çıktısından sonra AI analiz kutusu görünecek.[/dim]",
344
+ border_style="green",
345
+ )
346
+ )
347
+
236
348
 
237
349
  @app.command()
238
350
  def scan(
@@ -399,7 +511,7 @@ def status(
399
511
  Panel(
400
512
  f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim] [dim]{mesaj}[/dim]\n"
401
513
  f"[bold]Yazar:[/bold] {yazar}\n"
402
- f"[bold]Değişen kod dosyaları:[/bold] {len(sonuclar)}\n"
514
+ f"[bold]Değişen dosyalar:[/bold] {len(sonuclar)}\n"
403
515
  f"[bold]Ort. AI olasılığı:[/bold] [bold {risk_renk}]%{ort_ai*100:.0f} ({risk_etiketi})[/bold {risk_renk}]\n"
404
516
  f"[bold]Anlama skoru:[/bold] {anlama_goster}",
405
517
  title="[bold cyan]🧬 CodeDNA — Commit Skoru[/bold cyan]",
@@ -408,11 +520,6 @@ def status(
408
520
  )
409
521
  )
410
522
  console.print("[dim]Commit skoru kaydedildi.[/dim]\n")
411
- else:
412
- console.print(
413
- f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim]\n"
414
- "[dim]Bu commit'te desteklenen kod dosyası bulunamadı.[/dim]"
415
- )
416
523
 
417
524
  # Post-commit hook: korumalı modül ihlallerini kontrol et ve uyar (bloklama YOK)
418
525
  if hook:
@@ -424,6 +531,12 @@ def status(
424
531
  except Exception:
425
532
  pass # Hook'u asla bozmaz
426
533
 
534
+ else:
535
+ console.print(
536
+ f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim]\n"
537
+ "[dim]Bu commit'te desteklenen kod dosyası bulunamadı.[/dim]"
538
+ )
539
+
427
540
 
428
541
  # ---------------------------------------------------------------------------
429
542
  # codedna history
@@ -457,7 +570,7 @@ def history(
457
570
  tablo.add_column("Commit", style="dim", min_width=10)
458
571
  tablo.add_column("Yazar", min_width=15)
459
572
  tablo.add_column("Tarih", min_width=17)
460
- tablo.add_column("Kod Dosyası", justify="right", min_width=11)
573
+ tablo.add_column("Dosya", justify="right", min_width=6)
461
574
  tablo.add_column("Anlama", justify="center", min_width=12)
462
575
 
463
576
  for satir in satirlar:
@@ -1589,54 +1702,27 @@ def sprint_gecmisi(
1589
1702
  def plan(
1590
1703
  komut: Optional[str] = typer.Argument(
1591
1704
  None,
1592
- help="'activate', 'demo' veya doğrudan lisans anahtarı",
1705
+ help="'activate' veya doğrudan lisans anahtarı",
1593
1706
  ),
1594
1707
  anahtar: Optional[str] = typer.Argument(
1595
1708
  None,
1596
- help="Lisans anahtarı (activate/demo ile birlikte kullanılır)",
1709
+ help="Lisans anahtarı (activate ile birlikte kullanılır)",
1597
1710
  ),
1598
1711
  ) -> None:
1599
- """Mevcut planı göster, demo lisans aktif et veya lisans anahtarı ile plan aktif et.
1712
+ """Mevcut planı göster veya lisans anahtarı ile plan aktif et.
1600
1713
 
1601
1714
  Kullanım:
1602
1715
  codedna plan # mevcut planı göster
1603
- codedna plan activate <KEY> # lisans aktif et (CDNA-PRO/TEAM/ENT-...)
1604
- codedna plan demo # Enterprise demo lisans aktif et (hızlı test)
1605
- codedna plan demo pro # Pro demo lisans aktif et
1716
+ codedna plan activate <KEY> # lisans aktif et
1606
1717
  codedna plan <KEY> # kısa yol
1607
1718
  """
1608
1719
  from codedna.plan import (
1609
1720
  Plan as PlanEnum,
1610
1721
  get_current_plan,
1611
1722
  activate_license,
1612
- activate_demo_license,
1613
1723
  get_plan_limits,
1614
1724
  )
1615
1725
 
1616
- # "demo [plan]" syntax'ı
1617
- if komut == "demo":
1618
- demo_plan_str = (anahtar or "enterprise").lower()
1619
- try:
1620
- demo_plan = PlanEnum(demo_plan_str)
1621
- except ValueError:
1622
- console.print(f"[bold red]Hata:[/bold red] Geçersiz plan: {demo_plan_str!r}")
1623
- console.print("Geçerli planlar: free, pro, team, enterprise")
1624
- raise typer.Exit(1)
1625
- aktif_plan = activate_demo_license(demo_plan)
1626
- console.print(
1627
- Panel(
1628
- f"[bold green]✓ Demo lisans aktif edildi![/bold green]\n\n"
1629
- f"[bold]Plan:[/bold] [cyan]{aktif_plan.value.upper()}[/cyan]\n"
1630
- f"[bold yellow]⚠️ Bu bir DEMO lisanstır — production için gerçek key gerekir.[/bold yellow]\n\n"
1631
- f"Gerçek lisans için:\n"
1632
- f"[dim]codedna plan activate CDNA-{demo_plan_str.upper()[:3]}-XXXX-XXXX-XXXX[/dim]",
1633
- title="[bold cyan]🧬 CodeDNA — Demo Plan[/bold cyan]",
1634
- border_style="cyan",
1635
- padding=(1, 2),
1636
- )
1637
- )
1638
- return
1639
-
1640
1726
  # "activate <KEY>" veya doğrudan "<KEY>" syntax'ı destekle
1641
1727
  lisans_anahtari: Optional[str] = None
1642
1728
  if komut == "activate" and anahtar:
@@ -1830,233 +1916,6 @@ def uninstall(
1830
1916
  raise typer.Exit(1)
1831
1917
 
1832
1918
 
1833
- # ---------------------------------------------------------------------------
1834
- # codedna natureco (Pro+ özellik — NatureCo CLI entegrasyonu)
1835
- # ---------------------------------------------------------------------------
1836
- @app.command()
1837
- def natureco(
1838
- komut: Optional[str] = typer.Argument(
1839
- None,
1840
- help="'connect', 'sync', 'auto-scan', 'status' veya doğrudan dosya yolu",
1841
- ),
1842
- dosya: Optional[str] = typer.Argument(
1843
- None,
1844
- help="Dosya yolu (sync/auto-scan için)",
1845
- ),
1846
- ) -> None:
1847
- """NatureCo CLI ile entegre ol (Pro+ plan gerekir).
1848
-
1849
- NatureCo CLI kod ürettiğinde otomatik CodeDNA analizi yap.
1850
-
1851
- Kullanım:
1852
- codedna natureco connect # NatureCo CLI'ya bağlan
1853
- codedna natureco status # Bağlantı durumu
1854
- codedna natureco sync <file> # Dosyayı tarat
1855
- codedna natureco auto-scan # Post-hook: commit'te otomatik scan
1856
- codedna natureco disconnect # Bağlantıyı kes
1857
- """
1858
- from codedna.plan import get_current_plan, Plan as PlanEnum, is_feature_available
1859
-
1860
- # Pro+ plan gerekli
1861
- if not is_feature_available("natureco_integration"):
1862
- plan = get_current_plan()
1863
- console.print(
1864
- Panel(
1865
- f"[bold yellow]🔒 NatureCo entegrasyonu Pro+ plan gerektirir.[/bold yellow]\n\n"
1866
- f"Mevcut planınız: [cyan]{plan.value.upper()}[/cyan]\n\n"
1867
- f"Demo için:\n"
1868
- f"[dim]codedna plan demo pro[/dim]\n\n"
1869
- f"Upgrade:\n"
1870
- f"[dim]https://codedna.dev/pricing[/dim]",
1871
- title="[bold cyan]🧬 CodeDNA — NatureCo Integration[/bold cyan]",
1872
- border_style="yellow",
1873
- padding=(1, 2),
1874
- )
1875
- )
1876
- raise typer.Exit(1)
1877
-
1878
- if not komut or komut == "status":
1879
- return _natureco_status()
1880
- elif komut == "connect":
1881
- return _natureco_connect()
1882
- elif komut == "disconnect":
1883
- return _natureco_disconnect()
1884
- elif komut == "sync":
1885
- return _natureco_sync(dosya)
1886
- elif komut == "auto-scan":
1887
- return _natureco_auto_scan()
1888
- else:
1889
- console.print(f"[bold red]Bilinmeyen komut:[/bold red] {komut!r}")
1890
- console.print("Geçerli: connect, status, sync, auto-scan, disconnect")
1891
- raise typer.Exit(1)
1892
-
1893
-
1894
- def _natureco_config_path() -> Path:
1895
- """NatureCo entegrasyon ayar dosyası yolu."""
1896
- return Path.home() / ".codedna" / "natureco.json"
1897
-
1898
-
1899
- def _natureco_status() -> None:
1900
- """Bağlantı durumunu göster."""
1901
- config_path = _natureco_config_path()
1902
- if config_path.exists():
1903
- import json as _json
1904
- data = _json.loads(config_path.read_text())
1905
- console.print(
1906
- Panel(
1907
- f"[bold green]✓ NatureCo CLI bağlı[/bold green]\n\n"
1908
- f"[bold]Hook:[/bold] {'[green]Aktif[/green]' if data.get('auto_scan') else '[yellow]Pasif[/yellow]'}\n"
1909
- f"[bold]Son sync:[/bold] {data.get('last_sync', 'Hiç')}\n"
1910
- f"[bold]Toplam dosya:[/bold] {data.get('total_files', 0)}\n"
1911
- f"[bold]Bağlandı:[/bold] {data.get('connected_at', 'Bilinmiyor')}",
1912
- title="[bold cyan]🧬 CodeDNA ↔ NatureCo[/bold cyan]",
1913
- border_style="green",
1914
- padding=(1, 2),
1915
- )
1916
- )
1917
- else:
1918
- console.print(
1919
- Panel(
1920
- "[bold yellow]NatureCo CLI henüz bağlı değil.[/bold yellow]\n\n"
1921
- "Bağlamak için:\n"
1922
- "[dim]codedna natureco connect[/dim]",
1923
- title="[bold cyan]🧬 CodeDNA ↔ NatureCo[/bold cyan]",
1924
- border_style="yellow",
1925
- padding=(1, 2),
1926
- )
1927
- )
1928
-
1929
-
1930
- def _natureco_connect() -> None:
1931
- """NatureCo CLI ile bağlantı kur."""
1932
- import json as _json
1933
- import time as _time
1934
-
1935
- config_path = _natureco_config_path()
1936
- config_path.parent.mkdir(parents=True, exist_ok=True)
1937
-
1938
- # NatureCo CLI yüklü mü kontrol et
1939
- nc_path = None
1940
- for p in ["/usr/local/bin/natureco", "/opt/homebrew/bin/natureco",
1941
- str(Path.home() / ".npm-global/bin/natureco")]:
1942
- if Path(p).exists():
1943
- nc_path = p
1944
- break
1945
-
1946
- # PATH'te ara
1947
- if not nc_path:
1948
- import shutil
1949
- nc_path = shutil.which("natureco")
1950
-
1951
- if nc_path:
1952
- console.print(f"[green]✓[/green] NatureCo CLI bulundu: [dim]{nc_path}[/dim]")
1953
- else:
1954
- console.print("[yellow]⚠[/yellow] NatureCo CLI PATH'te bulunamadı")
1955
- console.print("[dim] Yine de entegrasyon kuruldu — NatureCo CLI kurulunca çalışacak[/dim]")
1956
-
1957
- config = {
1958
- "connected": True,
1959
- "natureco_cli_path": nc_path,
1960
- "auto_scan": True, # Varsayılan açık
1961
- "last_sync": None,
1962
- "total_files": 0,
1963
- "connected_at": _time.strftime("%Y-%m-%d %H:%M:%S"),
1964
- }
1965
- config_path.write_text(_json.dumps(config, indent=2))
1966
-
1967
- console.print(
1968
- Panel(
1969
- "[bold green]✓ NatureCo CLI bağlantısı kuruldu![/bold green]\n\n"
1970
- "Artık NatureCo CLI ile üretilen her kod otomatik taranacak.\n\n"
1971
- "[bold]Sonraki adımlar:[/bold]\n"
1972
- "1. NatureCo CLI'da kod üretin (`natureco code`)\n"
1973
- "2. CodeDNA otomatik scan yapar\n"
1974
- "3. `codedna natureco status` ile durumu kontrol edin",
1975
- title="[bold green]🧬 CodeDNA ↔ NatureCo Bağlandı[/bold green]",
1976
- border_style="green",
1977
- padding=(1, 2),
1978
- )
1979
- )
1980
-
1981
-
1982
- def _natureco_disconnect() -> None:
1983
- """Bağlantıyı kes."""
1984
- config_path = _natureco_config_path()
1985
- if config_path.exists():
1986
- config_path.unlink()
1987
- console.print("[green]✓[/green] NatureCo bağlantısı kesildi.")
1988
- else:
1989
- console.print("[yellow]Zaten bağlı değil.[/yellow]")
1990
-
1991
-
1992
- def _natureco_sync(dosya: Optional[str]) -> None:
1993
- """Bir dosyayı CodeDNA ile tara."""
1994
- import json as _json
1995
- import time as _time
1996
-
1997
- if not dosya:
1998
- console.print("[bold red]Hata:[/bold red] Dosya yolu gerekli")
1999
- console.print("Kullanım: [dim]codedna natureco sync <dosya>[/dim]")
2000
- raise typer.Exit(1)
2001
-
2002
- file_path = Path(dosya)
2003
- if not file_path.exists():
2004
- console.print(f"[bold red]Hata:[/bold red] Dosya bulunamadı: {dosya}")
2005
- raise typer.Exit(1)
2006
-
2007
- # Repo kökünü bul
2008
- kok = find_git_root() or Path.cwd()
2009
- db_yolu = _get_db(kok)
2010
-
2011
- # Scoring yap (analyzer kullanarak)
2012
- from codedna.analyzer import analyze_file
2013
-
2014
- console.print(f"[dim]Taranıyor:[/dim] {file_path.name}")
2015
- result = analyze_file(file_path, single_commit_ratio=0.0)
2016
-
2017
- if result:
2018
- console.print(
2019
- Panel(
2020
- f"[bold green]✓ Tarama tamamlandı[/bold green]\n\n"
2021
- f"[bold]Dosya:[/bold] {file_path.name}\n"
2022
- f"[bold]AI olasılığı:[/bold] [cyan]%{result.ai_probability * 100:.0f}[/cyan]\n"
2023
- f"[bold]Karmaşıklık:[/bold] {result.complexity_score:.1f}\n"
2024
- f"[bold]Yorum oranı:[/bold] {result.comment_ratio * 100:.0f}%\n",
2025
- title="[bold cyan]🧬 Tarama Sonucu[/bold cyan]",
2026
- border_style="cyan",
2027
- padding=(1, 2),
2028
- )
2029
- )
2030
-
2031
- # Config'i güncelle
2032
- config_path = _natureco_config_path()
2033
- if config_path.exists():
2034
- cfg = _json.loads(config_path.read_text())
2035
- cfg["last_sync"] = _time.strftime("%Y-%m-%d %H:%M:%S")
2036
- cfg["total_files"] = cfg.get("total_files", 0) + 1
2037
- config_path.write_text(_json.dumps(cfg, indent=2))
2038
- else:
2039
- console.print("[yellow]Dosya analiz edilemedi.[/yellow]")
2040
-
2041
-
2042
- def _natureco_auto_scan() -> None:
2043
- """Auto-scan hook'unu aktif/pasif yap."""
2044
- import json as _json
2045
-
2046
- config_path = _natureco_config_path()
2047
- if not config_path.exists():
2048
- console.print("[bold red]Hata:[/bold red] Önce connect gerekli")
2049
- console.print("[dim]codedna natureco connect[/dim]")
2050
- raise typer.Exit(1)
2051
-
2052
- cfg = _json.loads(config_path.read_text())
2053
- cfg["auto_scan"] = not cfg.get("auto_scan", False)
2054
- config_path.write_text(_json.dumps(cfg, indent=2))
2055
-
2056
- state = "[green]aktif[/green]" if cfg["auto_scan"] else "[yellow]pasif[/yellow]"
2057
- console.print(f"[green]✓[/green] Auto-scan {state}")
2058
-
2059
-
2060
1919
  # ---------------------------------------------------------------------------
2061
1920
  # Yardımcı fonksiyonlar
2062
1921
  # ---------------------------------------------------------------------------
@@ -2092,7 +1951,7 @@ def main() -> None:
2092
1951
  from importlib.metadata import version as _v
2093
1952
  ver = _v("codedna")
2094
1953
  except Exception:
2095
- ver = __version__ if "__version__" in globals() else "0.2.2"
1954
+ ver = "0.2.3"
2096
1955
  console.print(f"codedna {ver}")
2097
1956
  sys.exit(0)
2098
1957
  app()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codedna
3
- Version: 0.2.3
3
+ Version: 0.2.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,10 +1,11 @@
1
- codedna/__init__.py,sha256=I93t4cw-PxxwVUEIk2pnYu5V9ej7UBy0wRoPrrwBJKE,93
1
+ codedna/__init__.py,sha256=7G00zlcjHJcAUQDtrKy_XkZJhBV3iF7ce-miIlhZJXA,93
2
+ codedna/ai.py,sha256=h7cwQ6ArGEHWOoMDOY7v9ARSRm_7dHyszGdRYfFiE9Q,6701
2
3
  codedna/ai_fingerprint.py,sha256=xUiBF3jSjTqtu1xGAhbBbDx6PktQ7u_8KcSEz78fSSU,8107
3
4
  codedna/analyzer.py,sha256=GgKd_2TCC1ZmroV6RApj8LPGAZsvrgqw9zYWdz3c0bI,7490
4
5
  codedna/api.py,sha256=-X4GlQtuCqLPxerMc96KqDDtLM7XOXGCz9Hfhd9kgCM,50828
5
6
  codedna/auth.py,sha256=0rPQTRX7I-6Kxg3xzYfZSiZIYJ5b7ERgbHqj6yIzL5s,11917
6
7
  codedna/bus_factor.py,sha256=mgI4rRufMJmrLqTKibbrbHa3HrTaZdV9U80BMUZNRCw,8007
7
- codedna/cli.py,sha256=nJKSg2QbjQH5T3rmg1clzF_4wYfkptaaap4suTnH0_Y,77584
8
+ codedna/cli.py,sha256=bImIUXtNRnIlJrgswzhe9Wj8ZXr2bOS339l9SwUNJv4,72189
8
9
  codedna/db.py,sha256=o2-kxPQRKnXkw_p-jeiwjH0ZJXvquFUwel75_Eh5z20,11098
9
10
  codedna/git_hook.py,sha256=ZyLUStLFE94bKhMTMlDFkowisDPH1PS5wm7GBG6heEw,6382
10
11
  codedna/interview.py,sha256=YVQeTx1zqdlRE1-Qe8IgLuEQz5-OwvesmMY3vTm-_xk,8661
@@ -20,8 +21,8 @@ codedna/integrations/__init__.py,sha256=f_K_JICrNyGALXzZ8YFCauWfZejOBJRlTeDTyX_2
20
21
  codedna/integrations/github_bot.py,sha256=CdZ6VvqpkdGYrfgCoDI02vZka6ky231MwFgGcOfVVek,7841
21
22
  codedna/integrations/jira.py,sha256=1SPcBjJ4SrHmnyimWpwM38jxnRh6BhQ-1rN5h1HF4W8,5334
22
23
  codedna/integrations/lemonsqueezy.py,sha256=VtZq71YRHPfZzTiulQ4rWjks3lBLlPpke6XVRzRnha8,7611
23
- codedna-0.2.3.dist-info/METADATA,sha256=UZtlmRBoFpXplesjbd5Ww-fq8WbVVIpMWPNQz4x-c5I,18957
24
- codedna-0.2.3.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
25
- codedna-0.2.3.dist-info/entry_points.txt,sha256=0pXR9iZgAwjwYbEGO3MSrlmYcrhMw6QXntKqq5KjWgM,45
26
- codedna-0.2.3.dist-info/licenses/LICENSE,sha256=-XHiSAtpzZ_2gcli9hDggy41vDYoZrAqvLhiJ_5L35I,1065
27
- codedna-0.2.3.dist-info/RECORD,,
24
+ codedna-0.2.4.dist-info/METADATA,sha256=VWKXNQBBkrbM4hbbCsj1rtfgKvzQykAHXrXea8_sC3U,18957
25
+ codedna-0.2.4.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
26
+ codedna-0.2.4.dist-info/entry_points.txt,sha256=0pXR9iZgAwjwYbEGO3MSrlmYcrhMw6QXntKqq5KjWgM,45
27
+ codedna-0.2.4.dist-info/licenses/LICENSE,sha256=-XHiSAtpzZ_2gcli9hDggy41vDYoZrAqvLhiJ_5L35I,1065
28
+ codedna-0.2.4.dist-info/RECORD,,