codedna 0.2.2__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 +1 -1
- codedna/ai.py +221 -0
- codedna/cli.py +261 -265
- {codedna-0.2.2.dist-info → codedna-0.2.4.dist-info}/METADATA +1 -1
- {codedna-0.2.2.dist-info → codedna-0.2.4.dist-info}/RECORD +8 -7
- {codedna-0.2.2.dist-info → codedna-0.2.4.dist-info}/WHEEL +0 -0
- {codedna-0.2.2.dist-info → codedna-0.2.4.dist-info}/entry_points.txt +0 -0
- {codedna-0.2.2.dist-info → codedna-0.2.4.dist-info}/licenses/LICENSE +0 -0
codedna/__init__.py
CHANGED
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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import sys
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Optional
|
|
@@ -37,6 +38,18 @@ app = typer.Typer(
|
|
|
37
38
|
console = Console()
|
|
38
39
|
|
|
39
40
|
|
|
41
|
+
def _version_callback(value: bool) -> None:
|
|
42
|
+
"""--version bayrağı için callback."""
|
|
43
|
+
if value:
|
|
44
|
+
try:
|
|
45
|
+
from importlib.metadata import version as _v
|
|
46
|
+
ver = _v("codedna")
|
|
47
|
+
except Exception:
|
|
48
|
+
ver = "0.2.3"
|
|
49
|
+
console.print(f"codedna {ver}")
|
|
50
|
+
raise typer.Exit()
|
|
51
|
+
|
|
52
|
+
|
|
40
53
|
def _get_db(repo_path: Optional[Path] = None) -> Path:
|
|
41
54
|
"""Repo'ya özel veritabanı yolunu döndür."""
|
|
42
55
|
kok = repo_path or find_git_root() or Path.cwd()
|
|
@@ -106,6 +119,233 @@ def init(
|
|
|
106
119
|
# ---------------------------------------------------------------------------
|
|
107
120
|
# codedna scan
|
|
108
121
|
# ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
# codedna doctor
|
|
125
|
+
# ---------------------------------------------------------------------------
|
|
126
|
+
@app.command()
|
|
127
|
+
def doctor() -> None:
|
|
128
|
+
"""Sistem sağlık kontrolü."""
|
|
129
|
+
console.print()
|
|
130
|
+
console.print(
|
|
131
|
+
Panel.fit(
|
|
132
|
+
"[bold cyan]🧬 CodeDNA Doctor[/bold cyan]\n"
|
|
133
|
+
"[dim]Sistem sağlık kontrolü...[/dim]",
|
|
134
|
+
border_style="cyan",
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
console.print()
|
|
138
|
+
|
|
139
|
+
kontroller = []
|
|
140
|
+
|
|
141
|
+
py_ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
142
|
+
py_ok = sys.version_info >= (3, 10)
|
|
143
|
+
kontroller.append(("Python versiyonu", f"v{py_ver}", py_ok, f"v{py_ver} (>=3.10)" if py_ok else "ESKI"))
|
|
144
|
+
|
|
145
|
+
import shutil
|
|
146
|
+
git_var = shutil.which("git")
|
|
147
|
+
git_ok = git_var is not None
|
|
148
|
+
kontroller.append(("Git binary", git_var or "yok", git_ok, f"bulundu" if git_ok else "yok"))
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
import tree_sitter
|
|
152
|
+
ts_ok = True
|
|
153
|
+
try:
|
|
154
|
+
ts_msg = f"v{tree_sitter.__version__}"
|
|
155
|
+
except AttributeError:
|
|
156
|
+
try:
|
|
157
|
+
from importlib.metadata import version as _v
|
|
158
|
+
ts_msg = f"v{_v('tree-sitter')}"
|
|
159
|
+
except Exception:
|
|
160
|
+
ts_msg = "yüklü"
|
|
161
|
+
except ImportError:
|
|
162
|
+
ts_ok = False
|
|
163
|
+
ts_msg = "yok"
|
|
164
|
+
kontroller.append(("Tree-sitter", ts_msg, ts_ok, ts_msg if ts_ok else "yüklü değil"))
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
import fastapi
|
|
168
|
+
fa_ok = True
|
|
169
|
+
fa_msg = f"v{fastapi.__version__}"
|
|
170
|
+
except ImportError:
|
|
171
|
+
fa_ok = False
|
|
172
|
+
fa_msg = "yok"
|
|
173
|
+
kontroller.append(("FastAPI", fa_msg, fa_ok, fa_msg if fa_ok else "yüklü değil"))
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
from codedna.auth import _auth_db
|
|
177
|
+
db_path = _auth_db()
|
|
178
|
+
db_ok = db_path.exists()
|
|
179
|
+
kontroller.append(("Auth veritabanı", str(db_path), db_ok, f"var ({db_path.stat().st_size} bytes)" if db_ok else "yok"))
|
|
180
|
+
except Exception as e:
|
|
181
|
+
kontroller.append(("Auth veritabanı", "hata", False, str(e)))
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
from codedna.plan import get_current_plan
|
|
185
|
+
plan = get_current_plan()
|
|
186
|
+
kontroller.append(("Plan", plan.value.upper(), True, f"plan: {plan.value}"))
|
|
187
|
+
except Exception as e:
|
|
188
|
+
kontroller.append(("Plan", "hata", False, str(e)))
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
from codedna.git_hook import find_git_root
|
|
192
|
+
git_root = find_git_root()
|
|
193
|
+
if git_root:
|
|
194
|
+
hook_path = git_root / ".git" / "hooks" / "post-commit"
|
|
195
|
+
hook_ok = hook_path.exists()
|
|
196
|
+
kontroller.append(("Post-commit hook", "var" if hook_ok else "yok", hook_ok, "kurulmus" if hook_ok else "codedna init gerekli"))
|
|
197
|
+
else:
|
|
198
|
+
kontroller.append(("Post-commit hook", "—", True, "git repo disinda"))
|
|
199
|
+
except Exception as e:
|
|
200
|
+
kontroller.append(("Post-commit hook", "hata", False, str(e)))
|
|
201
|
+
|
|
202
|
+
tablo = Table(title="[bold]Sistem Durumu[/bold]", show_header=True, header_style="bold magenta", box=None)
|
|
203
|
+
tablo.add_column("Bileşen", style="cyan", no_wrap=True)
|
|
204
|
+
tablo.add_column("Durum", justify="center")
|
|
205
|
+
tablo.add_column("Detay", style="dim")
|
|
206
|
+
|
|
207
|
+
tum_ok = True
|
|
208
|
+
for ad, deger, ok, detay in kontroller:
|
|
209
|
+
if ok:
|
|
210
|
+
emoji, renk = "[green]✓[/green]", "green"
|
|
211
|
+
else:
|
|
212
|
+
emoji, renk = "[red]✗[/red]", "red"
|
|
213
|
+
tum_ok = False
|
|
214
|
+
tablo.add_row(ad, f"{emoji} [{renk}]{deger}[/{renk}]", detay)
|
|
215
|
+
|
|
216
|
+
console.print(tablo)
|
|
217
|
+
console.print()
|
|
218
|
+
if tum_ok:
|
|
219
|
+
console.print("[bold green]✓ Tüm sistem sağlıklı![/bold green]")
|
|
220
|
+
else:
|
|
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}")
|
|
332
|
+
raise typer.Exit(1)
|
|
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
|
+
|
|
348
|
+
|
|
109
349
|
@app.command()
|
|
110
350
|
def scan(
|
|
111
351
|
repo: Optional[Path] = typer.Option(None, "--repo", "-r", help="Git repo dizini"),
|
|
@@ -271,7 +511,7 @@ def status(
|
|
|
271
511
|
Panel(
|
|
272
512
|
f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim] [dim]{mesaj}[/dim]\n"
|
|
273
513
|
f"[bold]Yazar:[/bold] {yazar}\n"
|
|
274
|
-
f"[bold]Değişen
|
|
514
|
+
f"[bold]Değişen dosyalar:[/bold] {len(sonuclar)}\n"
|
|
275
515
|
f"[bold]Ort. AI olasılığı:[/bold] [bold {risk_renk}]%{ort_ai*100:.0f} ({risk_etiketi})[/bold {risk_renk}]\n"
|
|
276
516
|
f"[bold]Anlama skoru:[/bold] {anlama_goster}",
|
|
277
517
|
title="[bold cyan]🧬 CodeDNA — Commit Skoru[/bold cyan]",
|
|
@@ -280,11 +520,6 @@ def status(
|
|
|
280
520
|
)
|
|
281
521
|
)
|
|
282
522
|
console.print("[dim]Commit skoru kaydedildi.[/dim]\n")
|
|
283
|
-
else:
|
|
284
|
-
console.print(
|
|
285
|
-
f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim]\n"
|
|
286
|
-
"[dim]Bu commit'te desteklenen kod dosyası bulunamadı.[/dim]"
|
|
287
|
-
)
|
|
288
523
|
|
|
289
524
|
# Post-commit hook: korumalı modül ihlallerini kontrol et ve uyar (bloklama YOK)
|
|
290
525
|
if hook:
|
|
@@ -296,6 +531,12 @@ def status(
|
|
|
296
531
|
except Exception:
|
|
297
532
|
pass # Hook'u asla bozmaz
|
|
298
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
|
+
|
|
299
540
|
|
|
300
541
|
# ---------------------------------------------------------------------------
|
|
301
542
|
# codedna history
|
|
@@ -329,7 +570,7 @@ def history(
|
|
|
329
570
|
tablo.add_column("Commit", style="dim", min_width=10)
|
|
330
571
|
tablo.add_column("Yazar", min_width=15)
|
|
331
572
|
tablo.add_column("Tarih", min_width=17)
|
|
332
|
-
tablo.add_column("
|
|
573
|
+
tablo.add_column("Dosya", justify="right", min_width=6)
|
|
333
574
|
tablo.add_column("Anlama", justify="center", min_width=12)
|
|
334
575
|
|
|
335
576
|
for satir in satirlar:
|
|
@@ -1461,54 +1702,27 @@ def sprint_gecmisi(
|
|
|
1461
1702
|
def plan(
|
|
1462
1703
|
komut: Optional[str] = typer.Argument(
|
|
1463
1704
|
None,
|
|
1464
|
-
help="'activate'
|
|
1705
|
+
help="'activate' veya doğrudan lisans anahtarı",
|
|
1465
1706
|
),
|
|
1466
1707
|
anahtar: Optional[str] = typer.Argument(
|
|
1467
1708
|
None,
|
|
1468
|
-
help="Lisans anahtarı (activate
|
|
1709
|
+
help="Lisans anahtarı (activate ile birlikte kullanılır)",
|
|
1469
1710
|
),
|
|
1470
1711
|
) -> None:
|
|
1471
|
-
"""Mevcut planı göster
|
|
1712
|
+
"""Mevcut planı göster veya lisans anahtarı ile plan aktif et.
|
|
1472
1713
|
|
|
1473
1714
|
Kullanım:
|
|
1474
1715
|
codedna plan # mevcut planı göster
|
|
1475
|
-
codedna plan activate <KEY> # lisans aktif et
|
|
1476
|
-
codedna plan demo # Enterprise demo lisans aktif et (hızlı test)
|
|
1477
|
-
codedna plan demo pro # Pro demo lisans aktif et
|
|
1716
|
+
codedna plan activate <KEY> # lisans aktif et
|
|
1478
1717
|
codedna plan <KEY> # kısa yol
|
|
1479
1718
|
"""
|
|
1480
1719
|
from codedna.plan import (
|
|
1481
1720
|
Plan as PlanEnum,
|
|
1482
1721
|
get_current_plan,
|
|
1483
1722
|
activate_license,
|
|
1484
|
-
activate_demo_license,
|
|
1485
1723
|
get_plan_limits,
|
|
1486
1724
|
)
|
|
1487
1725
|
|
|
1488
|
-
# "demo [plan]" syntax'ı
|
|
1489
|
-
if komut == "demo":
|
|
1490
|
-
demo_plan_str = (anahtar or "enterprise").lower()
|
|
1491
|
-
try:
|
|
1492
|
-
demo_plan = PlanEnum(demo_plan_str)
|
|
1493
|
-
except ValueError:
|
|
1494
|
-
console.print(f"[bold red]Hata:[/bold red] Geçersiz plan: {demo_plan_str!r}")
|
|
1495
|
-
console.print("Geçerli planlar: free, pro, team, enterprise")
|
|
1496
|
-
raise typer.Exit(1)
|
|
1497
|
-
aktif_plan = activate_demo_license(demo_plan)
|
|
1498
|
-
console.print(
|
|
1499
|
-
Panel(
|
|
1500
|
-
f"[bold green]✓ Demo lisans aktif edildi![/bold green]\n\n"
|
|
1501
|
-
f"[bold]Plan:[/bold] [cyan]{aktif_plan.value.upper()}[/cyan]\n"
|
|
1502
|
-
f"[bold yellow]⚠️ Bu bir DEMO lisanstır — production için gerçek key gerekir.[/bold yellow]\n\n"
|
|
1503
|
-
f"Gerçek lisans için:\n"
|
|
1504
|
-
f"[dim]codedna plan activate CDNA-{demo_plan_str.upper()[:3]}-XXXX-XXXX-XXXX[/dim]",
|
|
1505
|
-
title="[bold cyan]🧬 CodeDNA — Demo Plan[/bold cyan]",
|
|
1506
|
-
border_style="cyan",
|
|
1507
|
-
padding=(1, 2),
|
|
1508
|
-
)
|
|
1509
|
-
)
|
|
1510
|
-
return
|
|
1511
|
-
|
|
1512
1726
|
# "activate <KEY>" veya doğrudan "<KEY>" syntax'ı destekle
|
|
1513
1727
|
lisans_anahtari: Optional[str] = None
|
|
1514
1728
|
if komut == "activate" and anahtar:
|
|
@@ -1702,233 +1916,6 @@ def uninstall(
|
|
|
1702
1916
|
raise typer.Exit(1)
|
|
1703
1917
|
|
|
1704
1918
|
|
|
1705
|
-
# ---------------------------------------------------------------------------
|
|
1706
|
-
# codedna natureco (Pro+ özellik — NatureCo CLI entegrasyonu)
|
|
1707
|
-
# ---------------------------------------------------------------------------
|
|
1708
|
-
@app.command()
|
|
1709
|
-
def natureco(
|
|
1710
|
-
komut: Optional[str] = typer.Argument(
|
|
1711
|
-
None,
|
|
1712
|
-
help="'connect', 'sync', 'auto-scan', 'status' veya doğrudan dosya yolu",
|
|
1713
|
-
),
|
|
1714
|
-
dosya: Optional[str] = typer.Argument(
|
|
1715
|
-
None,
|
|
1716
|
-
help="Dosya yolu (sync/auto-scan için)",
|
|
1717
|
-
),
|
|
1718
|
-
) -> None:
|
|
1719
|
-
"""NatureCo CLI ile entegre ol (Pro+ plan gerekir).
|
|
1720
|
-
|
|
1721
|
-
NatureCo CLI kod ürettiğinde otomatik CodeDNA analizi yap.
|
|
1722
|
-
|
|
1723
|
-
Kullanım:
|
|
1724
|
-
codedna natureco connect # NatureCo CLI'ya bağlan
|
|
1725
|
-
codedna natureco status # Bağlantı durumu
|
|
1726
|
-
codedna natureco sync <file> # Dosyayı tarat
|
|
1727
|
-
codedna natureco auto-scan # Post-hook: commit'te otomatik scan
|
|
1728
|
-
codedna natureco disconnect # Bağlantıyı kes
|
|
1729
|
-
"""
|
|
1730
|
-
from codedna.plan import get_current_plan, Plan as PlanEnum, is_feature_available
|
|
1731
|
-
|
|
1732
|
-
# Pro+ plan gerekli
|
|
1733
|
-
if not is_feature_available("natureco_integration"):
|
|
1734
|
-
plan = get_current_plan()
|
|
1735
|
-
console.print(
|
|
1736
|
-
Panel(
|
|
1737
|
-
f"[bold yellow]🔒 NatureCo entegrasyonu Pro+ plan gerektirir.[/bold yellow]\n\n"
|
|
1738
|
-
f"Mevcut planınız: [cyan]{plan.value.upper()}[/cyan]\n\n"
|
|
1739
|
-
f"Demo için:\n"
|
|
1740
|
-
f"[dim]codedna plan demo pro[/dim]\n\n"
|
|
1741
|
-
f"Upgrade:\n"
|
|
1742
|
-
f"[dim]https://codedna.dev/pricing[/dim]",
|
|
1743
|
-
title="[bold cyan]🧬 CodeDNA — NatureCo Integration[/bold cyan]",
|
|
1744
|
-
border_style="yellow",
|
|
1745
|
-
padding=(1, 2),
|
|
1746
|
-
)
|
|
1747
|
-
)
|
|
1748
|
-
raise typer.Exit(1)
|
|
1749
|
-
|
|
1750
|
-
if not komut or komut == "status":
|
|
1751
|
-
return _natureco_status()
|
|
1752
|
-
elif komut == "connect":
|
|
1753
|
-
return _natureco_connect()
|
|
1754
|
-
elif komut == "disconnect":
|
|
1755
|
-
return _natureco_disconnect()
|
|
1756
|
-
elif komut == "sync":
|
|
1757
|
-
return _natureco_sync(dosya)
|
|
1758
|
-
elif komut == "auto-scan":
|
|
1759
|
-
return _natureco_auto_scan()
|
|
1760
|
-
else:
|
|
1761
|
-
console.print(f"[bold red]Bilinmeyen komut:[/bold red] {komut!r}")
|
|
1762
|
-
console.print("Geçerli: connect, status, sync, auto-scan, disconnect")
|
|
1763
|
-
raise typer.Exit(1)
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
def _natureco_config_path() -> Path:
|
|
1767
|
-
"""NatureCo entegrasyon ayar dosyası yolu."""
|
|
1768
|
-
return Path.home() / ".codedna" / "natureco.json"
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
def _natureco_status() -> None:
|
|
1772
|
-
"""Bağlantı durumunu göster."""
|
|
1773
|
-
config_path = _natureco_config_path()
|
|
1774
|
-
if config_path.exists():
|
|
1775
|
-
import json as _json
|
|
1776
|
-
data = _json.loads(config_path.read_text())
|
|
1777
|
-
console.print(
|
|
1778
|
-
Panel(
|
|
1779
|
-
f"[bold green]✓ NatureCo CLI bağlı[/bold green]\n\n"
|
|
1780
|
-
f"[bold]Hook:[/bold] {'[green]Aktif[/green]' if data.get('auto_scan') else '[yellow]Pasif[/yellow]'}\n"
|
|
1781
|
-
f"[bold]Son sync:[/bold] {data.get('last_sync', 'Hiç')}\n"
|
|
1782
|
-
f"[bold]Toplam dosya:[/bold] {data.get('total_files', 0)}\n"
|
|
1783
|
-
f"[bold]Bağlandı:[/bold] {data.get('connected_at', 'Bilinmiyor')}",
|
|
1784
|
-
title="[bold cyan]🧬 CodeDNA ↔ NatureCo[/bold cyan]",
|
|
1785
|
-
border_style="green",
|
|
1786
|
-
padding=(1, 2),
|
|
1787
|
-
)
|
|
1788
|
-
)
|
|
1789
|
-
else:
|
|
1790
|
-
console.print(
|
|
1791
|
-
Panel(
|
|
1792
|
-
"[bold yellow]NatureCo CLI henüz bağlı değil.[/bold yellow]\n\n"
|
|
1793
|
-
"Bağlamak için:\n"
|
|
1794
|
-
"[dim]codedna natureco connect[/dim]",
|
|
1795
|
-
title="[bold cyan]🧬 CodeDNA ↔ NatureCo[/bold cyan]",
|
|
1796
|
-
border_style="yellow",
|
|
1797
|
-
padding=(1, 2),
|
|
1798
|
-
)
|
|
1799
|
-
)
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
def _natureco_connect() -> None:
|
|
1803
|
-
"""NatureCo CLI ile bağlantı kur."""
|
|
1804
|
-
import json as _json
|
|
1805
|
-
import time as _time
|
|
1806
|
-
|
|
1807
|
-
config_path = _natureco_config_path()
|
|
1808
|
-
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1809
|
-
|
|
1810
|
-
# NatureCo CLI yüklü mü kontrol et
|
|
1811
|
-
nc_path = None
|
|
1812
|
-
for p in ["/usr/local/bin/natureco", "/opt/homebrew/bin/natureco",
|
|
1813
|
-
str(Path.home() / ".npm-global/bin/natureco")]:
|
|
1814
|
-
if Path(p).exists():
|
|
1815
|
-
nc_path = p
|
|
1816
|
-
break
|
|
1817
|
-
|
|
1818
|
-
# PATH'te ara
|
|
1819
|
-
if not nc_path:
|
|
1820
|
-
import shutil
|
|
1821
|
-
nc_path = shutil.which("natureco")
|
|
1822
|
-
|
|
1823
|
-
if nc_path:
|
|
1824
|
-
console.print(f"[green]✓[/green] NatureCo CLI bulundu: [dim]{nc_path}[/dim]")
|
|
1825
|
-
else:
|
|
1826
|
-
console.print("[yellow]⚠[/yellow] NatureCo CLI PATH'te bulunamadı")
|
|
1827
|
-
console.print("[dim] Yine de entegrasyon kuruldu — NatureCo CLI kurulunca çalışacak[/dim]")
|
|
1828
|
-
|
|
1829
|
-
config = {
|
|
1830
|
-
"connected": True,
|
|
1831
|
-
"natureco_cli_path": nc_path,
|
|
1832
|
-
"auto_scan": True, # Varsayılan açık
|
|
1833
|
-
"last_sync": None,
|
|
1834
|
-
"total_files": 0,
|
|
1835
|
-
"connected_at": _time.strftime("%Y-%m-%d %H:%M:%S"),
|
|
1836
|
-
}
|
|
1837
|
-
config_path.write_text(_json.dumps(config, indent=2))
|
|
1838
|
-
|
|
1839
|
-
console.print(
|
|
1840
|
-
Panel(
|
|
1841
|
-
"[bold green]✓ NatureCo CLI bağlantısı kuruldu![/bold green]\n\n"
|
|
1842
|
-
"Artık NatureCo CLI ile üretilen her kod otomatik taranacak.\n\n"
|
|
1843
|
-
"[bold]Sonraki adımlar:[/bold]\n"
|
|
1844
|
-
"1. NatureCo CLI'da kod üretin (`natureco code`)\n"
|
|
1845
|
-
"2. CodeDNA otomatik scan yapar\n"
|
|
1846
|
-
"3. `codedna natureco status` ile durumu kontrol edin",
|
|
1847
|
-
title="[bold green]🧬 CodeDNA ↔ NatureCo Bağlandı[/bold green]",
|
|
1848
|
-
border_style="green",
|
|
1849
|
-
padding=(1, 2),
|
|
1850
|
-
)
|
|
1851
|
-
)
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
def _natureco_disconnect() -> None:
|
|
1855
|
-
"""Bağlantıyı kes."""
|
|
1856
|
-
config_path = _natureco_config_path()
|
|
1857
|
-
if config_path.exists():
|
|
1858
|
-
config_path.unlink()
|
|
1859
|
-
console.print("[green]✓[/green] NatureCo bağlantısı kesildi.")
|
|
1860
|
-
else:
|
|
1861
|
-
console.print("[yellow]Zaten bağlı değil.[/yellow]")
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
def _natureco_sync(dosya: Optional[str]) -> None:
|
|
1865
|
-
"""Bir dosyayı CodeDNA ile tara."""
|
|
1866
|
-
import json as _json
|
|
1867
|
-
import time as _time
|
|
1868
|
-
|
|
1869
|
-
if not dosya:
|
|
1870
|
-
console.print("[bold red]Hata:[/bold red] Dosya yolu gerekli")
|
|
1871
|
-
console.print("Kullanım: [dim]codedna natureco sync <dosya>[/dim]")
|
|
1872
|
-
raise typer.Exit(1)
|
|
1873
|
-
|
|
1874
|
-
file_path = Path(dosya)
|
|
1875
|
-
if not file_path.exists():
|
|
1876
|
-
console.print(f"[bold red]Hata:[/bold red] Dosya bulunamadı: {dosya}")
|
|
1877
|
-
raise typer.Exit(1)
|
|
1878
|
-
|
|
1879
|
-
# Repo kökünü bul
|
|
1880
|
-
kok = find_git_root() or Path.cwd()
|
|
1881
|
-
db_yolu = _get_db(kok)
|
|
1882
|
-
|
|
1883
|
-
# Scoring yap (analyzer kullanarak)
|
|
1884
|
-
from codedna.analyzer import analyze_file
|
|
1885
|
-
|
|
1886
|
-
console.print(f"[dim]Taranıyor:[/dim] {file_path.name}")
|
|
1887
|
-
result = analyze_file(file_path, single_commit_ratio=0.0)
|
|
1888
|
-
|
|
1889
|
-
if result:
|
|
1890
|
-
console.print(
|
|
1891
|
-
Panel(
|
|
1892
|
-
f"[bold green]✓ Tarama tamamlandı[/bold green]\n\n"
|
|
1893
|
-
f"[bold]Dosya:[/bold] {file_path.name}\n"
|
|
1894
|
-
f"[bold]AI olasılığı:[/bold] [cyan]%{result.ai_probability * 100:.0f}[/cyan]\n"
|
|
1895
|
-
f"[bold]Karmaşıklık:[/bold] {result.complexity_score:.1f}\n"
|
|
1896
|
-
f"[bold]Yorum oranı:[/bold] {result.comment_ratio * 100:.0f}%\n",
|
|
1897
|
-
title="[bold cyan]🧬 Tarama Sonucu[/bold cyan]",
|
|
1898
|
-
border_style="cyan",
|
|
1899
|
-
padding=(1, 2),
|
|
1900
|
-
)
|
|
1901
|
-
)
|
|
1902
|
-
|
|
1903
|
-
# Config'i güncelle
|
|
1904
|
-
config_path = _natureco_config_path()
|
|
1905
|
-
if config_path.exists():
|
|
1906
|
-
cfg = _json.loads(config_path.read_text())
|
|
1907
|
-
cfg["last_sync"] = _time.strftime("%Y-%m-%d %H:%M:%S")
|
|
1908
|
-
cfg["total_files"] = cfg.get("total_files", 0) + 1
|
|
1909
|
-
config_path.write_text(_json.dumps(cfg, indent=2))
|
|
1910
|
-
else:
|
|
1911
|
-
console.print("[yellow]Dosya analiz edilemedi.[/yellow]")
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
def _natureco_auto_scan() -> None:
|
|
1915
|
-
"""Auto-scan hook'unu aktif/pasif yap."""
|
|
1916
|
-
import json as _json
|
|
1917
|
-
|
|
1918
|
-
config_path = _natureco_config_path()
|
|
1919
|
-
if not config_path.exists():
|
|
1920
|
-
console.print("[bold red]Hata:[/bold red] Önce connect gerekli")
|
|
1921
|
-
console.print("[dim]codedna natureco connect[/dim]")
|
|
1922
|
-
raise typer.Exit(1)
|
|
1923
|
-
|
|
1924
|
-
cfg = _json.loads(config_path.read_text())
|
|
1925
|
-
cfg["auto_scan"] = not cfg.get("auto_scan", False)
|
|
1926
|
-
config_path.write_text(_json.dumps(cfg, indent=2))
|
|
1927
|
-
|
|
1928
|
-
state = "[green]aktif[/green]" if cfg["auto_scan"] else "[yellow]pasif[/yellow]"
|
|
1929
|
-
console.print(f"[green]✓[/green] Auto-scan {state}")
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
1919
|
# ---------------------------------------------------------------------------
|
|
1933
1920
|
# Yardımcı fonksiyonlar
|
|
1934
1921
|
# ---------------------------------------------------------------------------
|
|
@@ -1958,6 +1945,15 @@ def _risk_etiketi(yuzde: float) -> tuple[str, str]:
|
|
|
1958
1945
|
|
|
1959
1946
|
def main() -> None:
|
|
1960
1947
|
"""CLI giriş noktası."""
|
|
1948
|
+
# --version veya -V kontrolu (Typer'in tanimadigi)
|
|
1949
|
+
if "--version" in sys.argv or "-V" in sys.argv:
|
|
1950
|
+
try:
|
|
1951
|
+
from importlib.metadata import version as _v
|
|
1952
|
+
ver = _v("codedna")
|
|
1953
|
+
except Exception:
|
|
1954
|
+
ver = "0.2.3"
|
|
1955
|
+
console.print(f"codedna {ver}")
|
|
1956
|
+
sys.exit(0)
|
|
1961
1957
|
app()
|
|
1962
1958
|
|
|
1963
1959
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codedna
|
|
3
|
-
Version: 0.2.
|
|
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=
|
|
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=
|
|
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.
|
|
24
|
-
codedna-0.2.
|
|
25
|
-
codedna-0.2.
|
|
26
|
-
codedna-0.2.
|
|
27
|
-
codedna-0.2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|