codeframe-ai 0.9.0__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.
- codeframe/__init__.py +11 -0
- codeframe/__main__.py +20 -0
- codeframe/adapters/__init__.py +5 -0
- codeframe/adapters/e2b/__init__.py +13 -0
- codeframe/adapters/e2b/adapter.py +342 -0
- codeframe/adapters/e2b/budget.py +71 -0
- codeframe/adapters/e2b/credential_scanner.py +134 -0
- codeframe/adapters/llm/__init__.py +92 -0
- codeframe/adapters/llm/anthropic.py +414 -0
- codeframe/adapters/llm/base.py +444 -0
- codeframe/adapters/llm/mock.py +281 -0
- codeframe/adapters/llm/openai.py +483 -0
- codeframe/agents/__init__.py +8 -0
- codeframe/agents/dependency_resolver.py +714 -0
- codeframe/auth/__init__.py +16 -0
- codeframe/auth/api_key_router.py +238 -0
- codeframe/auth/api_keys.py +156 -0
- codeframe/auth/dependencies.py +358 -0
- codeframe/auth/manager.py +178 -0
- codeframe/auth/models.py +30 -0
- codeframe/auth/router.py +93 -0
- codeframe/auth/schemas.py +15 -0
- codeframe/auth/scopes.py +53 -0
- codeframe/cli/__init__.py +12 -0
- codeframe/cli/__main__.py +20 -0
- codeframe/cli/api_client.py +275 -0
- codeframe/cli/app.py +5688 -0
- codeframe/cli/auth.py +122 -0
- codeframe/cli/auth_commands.py +958 -0
- codeframe/cli/commands/__init__.py +5 -0
- codeframe/cli/config_commands.py +79 -0
- codeframe/cli/dashboard_commands.py +67 -0
- codeframe/cli/engines_commands.py +205 -0
- codeframe/cli/env_commands.py +409 -0
- codeframe/cli/helpers.py +56 -0
- codeframe/cli/hooks_commands.py +208 -0
- codeframe/cli/import_commands.py +129 -0
- codeframe/cli/pr_commands.py +549 -0
- codeframe/cli/proof_commands.py +415 -0
- codeframe/cli/stats_commands.py +311 -0
- codeframe/cli/telemetry_runtime.py +153 -0
- codeframe/cli/validators.py +123 -0
- codeframe/config/rate_limits.py +165 -0
- codeframe/core/__init__.py +15 -0
- codeframe/core/adapters/__init__.py +43 -0
- codeframe/core/adapters/agent_adapter.py +114 -0
- codeframe/core/adapters/builtin.py +326 -0
- codeframe/core/adapters/claude_code.py +62 -0
- codeframe/core/adapters/codex.py +393 -0
- codeframe/core/adapters/git_utils.py +40 -0
- codeframe/core/adapters/kilocode.py +126 -0
- codeframe/core/adapters/opencode.py +48 -0
- codeframe/core/adapters/streaming_chat.py +483 -0
- codeframe/core/adapters/subprocess_adapter.py +213 -0
- codeframe/core/adapters/verification_wrapper.py +269 -0
- codeframe/core/agent.py +2183 -0
- codeframe/core/agents_config.py +569 -0
- codeframe/core/api_key_service.py +211 -0
- codeframe/core/artifacts.py +428 -0
- codeframe/core/blocker_detection.py +218 -0
- codeframe/core/blockers.py +433 -0
- codeframe/core/checkpoints.py +481 -0
- codeframe/core/conductor.py +2255 -0
- codeframe/core/config.py +827 -0
- codeframe/core/config_watcher.py +268 -0
- codeframe/core/context.py +542 -0
- codeframe/core/context_packager.py +234 -0
- codeframe/core/credentials.py +735 -0
- codeframe/core/dependency_analyzer.py +229 -0
- codeframe/core/dependency_graph.py +290 -0
- codeframe/core/diagnostic_agent.py +712 -0
- codeframe/core/diagnostics.py +616 -0
- codeframe/core/editor.py +556 -0
- codeframe/core/engine_registry.py +256 -0
- codeframe/core/engine_stats.py +231 -0
- codeframe/core/environment.py +697 -0
- codeframe/core/events.py +375 -0
- codeframe/core/executor.py +1005 -0
- codeframe/core/fix_tracker.py +480 -0
- codeframe/core/gates.py +1322 -0
- codeframe/core/git.py +477 -0
- codeframe/core/github_connect_service.py +178 -0
- codeframe/core/github_integration_config.py +118 -0
- codeframe/core/github_issues_service.py +449 -0
- codeframe/core/hooks.py +184 -0
- codeframe/core/importers/__init__.py +1 -0
- codeframe/core/importers/ralph.py +540 -0
- codeframe/core/installer.py +650 -0
- codeframe/core/models.py +1026 -0
- codeframe/core/notifications_config.py +183 -0
- codeframe/core/planner.py +437 -0
- codeframe/core/prd.py +670 -0
- codeframe/core/prd_discovery.py +1118 -0
- codeframe/core/prd_stress_test.py +499 -0
- codeframe/core/progress.py +126 -0
- codeframe/core/proof/__init__.py +34 -0
- codeframe/core/proof/capture.py +79 -0
- codeframe/core/proof/evidence.py +56 -0
- codeframe/core/proof/ledger.py +574 -0
- codeframe/core/proof/models.py +162 -0
- codeframe/core/proof/obligations.py +103 -0
- codeframe/core/proof/runner.py +233 -0
- codeframe/core/proof/scope.py +81 -0
- codeframe/core/proof/stubs.py +156 -0
- codeframe/core/quick_fixes.py +558 -0
- codeframe/core/react_agent.py +1650 -0
- codeframe/core/reconciliation.py +183 -0
- codeframe/core/replay.py +788 -0
- codeframe/core/review.py +285 -0
- codeframe/core/runtime.py +1134 -0
- codeframe/core/sandbox/__init__.py +27 -0
- codeframe/core/sandbox/context.py +98 -0
- codeframe/core/sandbox/worktree.py +20 -0
- codeframe/core/schedule.py +396 -0
- codeframe/core/stall_detector.py +71 -0
- codeframe/core/stall_monitor.py +134 -0
- codeframe/core/state_machine.py +121 -0
- codeframe/core/streaming.py +502 -0
- codeframe/core/task_tree.py +400 -0
- codeframe/core/tasks.py +1022 -0
- codeframe/core/telemetry.py +232 -0
- codeframe/core/templates.py +221 -0
- codeframe/core/tools.py +942 -0
- codeframe/core/workspace.py +887 -0
- codeframe/core/worktrees.py +276 -0
- codeframe/git/__init__.py +5 -0
- codeframe/git/github_integration.py +505 -0
- codeframe/lib/__init__.py +0 -0
- codeframe/lib/audit_logger.py +248 -0
- codeframe/lib/metrics_tracker.py +800 -0
- codeframe/lib/quality/__init__.py +7 -0
- codeframe/lib/quality/complexity_analyzer.py +316 -0
- codeframe/lib/quality/owasp_patterns.py +284 -0
- codeframe/lib/quality/security_scanner.py +250 -0
- codeframe/lib/rate_limiter.py +312 -0
- codeframe/notifications/__init__.py +0 -0
- codeframe/notifications/webhook.py +380 -0
- codeframe/planning/__init__.py +30 -0
- codeframe/planning/issue_generator.py +219 -0
- codeframe/planning/prd_template_functions.py +137 -0
- codeframe/planning/prd_templates.py +975 -0
- codeframe/planning/task_scheduler.py +511 -0
- codeframe/planning/task_templates.py +533 -0
- codeframe/platform_store/__init__.py +5 -0
- codeframe/platform_store/database.py +277 -0
- codeframe/platform_store/repositories/__init__.py +24 -0
- codeframe/platform_store/repositories/api_key_repository.py +245 -0
- codeframe/platform_store/repositories/audit_repository.py +67 -0
- codeframe/platform_store/repositories/base.py +295 -0
- codeframe/platform_store/repositories/interactive_sessions.py +165 -0
- codeframe/platform_store/repositories/token_repository.py +598 -0
- codeframe/platform_store/repositories/workspace_registry_repository.py +175 -0
- codeframe/platform_store/schema_manager.py +321 -0
- codeframe/templates/AGENTS.md.default +94 -0
- codeframe/tui/__init__.py +5 -0
- codeframe/tui/app.py +256 -0
- codeframe/tui/data_service.py +103 -0
- codeframe/ui/__init__.py +0 -0
- codeframe/ui/dependencies.py +103 -0
- codeframe/ui/models.py +999 -0
- codeframe/ui/response_models.py +201 -0
- codeframe/ui/routers/__init__.py +5 -0
- codeframe/ui/routers/_helpers.py +29 -0
- codeframe/ui/routers/batches_v2.py +315 -0
- codeframe/ui/routers/blockers_v2.py +320 -0
- codeframe/ui/routers/checkpoints_v2.py +310 -0
- codeframe/ui/routers/costs_v2.py +322 -0
- codeframe/ui/routers/diagnose_v2.py +225 -0
- codeframe/ui/routers/discovery_v2.py +417 -0
- codeframe/ui/routers/environment_v2.py +284 -0
- codeframe/ui/routers/events_v2.py +75 -0
- codeframe/ui/routers/gates_v2.py +166 -0
- codeframe/ui/routers/git_v2.py +284 -0
- codeframe/ui/routers/github_integrations_v2.py +532 -0
- codeframe/ui/routers/interactive_sessions_v2.py +238 -0
- codeframe/ui/routers/pr_v2.py +709 -0
- codeframe/ui/routers/prd_v2.py +695 -0
- codeframe/ui/routers/proof_v2.py +755 -0
- codeframe/ui/routers/review_v2.py +360 -0
- codeframe/ui/routers/schedule_v2.py +214 -0
- codeframe/ui/routers/session_chat_ws.py +354 -0
- codeframe/ui/routers/settings_v2.py +562 -0
- codeframe/ui/routers/streaming_v2.py +155 -0
- codeframe/ui/routers/tasks_v2.py +1098 -0
- codeframe/ui/routers/templates_v2.py +232 -0
- codeframe/ui/routers/terminal_ws.py +267 -0
- codeframe/ui/routers/workspace_v2.py +527 -0
- codeframe/ui/server.py +568 -0
- codeframe/ui/shared.py +241 -0
- codeframe/workspace/__init__.py +5 -0
- codeframe/workspace/manager.py +249 -0
- codeframe_ai-0.9.0.dist-info/METADATA +517 -0
- codeframe_ai-0.9.0.dist-info/RECORD +197 -0
- codeframe_ai-0.9.0.dist-info/WHEEL +5 -0
- codeframe_ai-0.9.0.dist-info/entry_points.txt +3 -0
- codeframe_ai-0.9.0.dist-info/licenses/LICENSE +661 -0
- codeframe_ai-0.9.0.dist-info/top_level.txt +1 -0
codeframe/core/review.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Code review operations for CodeFRAME v2.
|
|
2
|
+
|
|
3
|
+
This module provides v2-compatible code review operations that work with
|
|
4
|
+
the Workspace model. It provides a simplified interface to the quality
|
|
5
|
+
analyzers without requiring v1 database persistence.
|
|
6
|
+
|
|
7
|
+
This module is headless - no FastAPI or HTTP dependencies.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Literal, Optional
|
|
13
|
+
|
|
14
|
+
from codeframe.core.workspace import Workspace
|
|
15
|
+
from codeframe.lib.quality.complexity_analyzer import ComplexityAnalyzer
|
|
16
|
+
from codeframe.lib.quality.security_scanner import SecurityScanner
|
|
17
|
+
from codeframe.lib.quality.owasp_patterns import OWASPPatterns
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ============================================================================
|
|
23
|
+
# Data Classes
|
|
24
|
+
# ============================================================================
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ReviewFinding:
|
|
29
|
+
"""Individual review finding."""
|
|
30
|
+
|
|
31
|
+
category: str # complexity, security, style
|
|
32
|
+
severity: Literal["critical", "high", "medium", "low", "info"]
|
|
33
|
+
message: str
|
|
34
|
+
file_path: str
|
|
35
|
+
line_number: Optional[int] = None
|
|
36
|
+
suggestion: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class ReviewResult:
|
|
41
|
+
"""Result of a code review."""
|
|
42
|
+
|
|
43
|
+
status: Literal["approved", "changes_requested", "rejected"]
|
|
44
|
+
overall_score: float
|
|
45
|
+
findings: list[ReviewFinding]
|
|
46
|
+
summary: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class ReviewStatus:
|
|
51
|
+
"""Review status for a task."""
|
|
52
|
+
|
|
53
|
+
has_review: bool
|
|
54
|
+
status: Optional[str]
|
|
55
|
+
overall_score: Optional[float]
|
|
56
|
+
findings_count: int
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ============================================================================
|
|
60
|
+
# Score Thresholds
|
|
61
|
+
# ============================================================================
|
|
62
|
+
|
|
63
|
+
EXCELLENT_THRESHOLD = 90
|
|
64
|
+
GOOD_THRESHOLD = 70
|
|
65
|
+
ACCEPTABLE_THRESHOLD = 50
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ============================================================================
|
|
69
|
+
# Review Functions
|
|
70
|
+
# ============================================================================
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _determine_status(score: float) -> Literal["approved", "changes_requested", "rejected"]:
|
|
74
|
+
"""Determine review status based on score.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
score: Overall review score (0-100)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Review status
|
|
81
|
+
"""
|
|
82
|
+
if score >= GOOD_THRESHOLD:
|
|
83
|
+
return "approved"
|
|
84
|
+
elif score >= ACCEPTABLE_THRESHOLD:
|
|
85
|
+
return "changes_requested"
|
|
86
|
+
else:
|
|
87
|
+
return "rejected"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _severity_from_score(score: float) -> Literal["critical", "high", "medium", "low", "info"]:
|
|
91
|
+
"""Determine severity based on score.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
score: Individual finding score
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Severity level
|
|
98
|
+
"""
|
|
99
|
+
if score < 30:
|
|
100
|
+
return "critical"
|
|
101
|
+
elif score < 50:
|
|
102
|
+
return "high"
|
|
103
|
+
elif score < 70:
|
|
104
|
+
return "medium"
|
|
105
|
+
elif score < 90:
|
|
106
|
+
return "low"
|
|
107
|
+
else:
|
|
108
|
+
return "info"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def review_files(
|
|
112
|
+
workspace: Workspace,
|
|
113
|
+
files: list[str],
|
|
114
|
+
) -> ReviewResult:
|
|
115
|
+
"""Run code review on specified files.
|
|
116
|
+
|
|
117
|
+
Performs complexity analysis, security scanning, and OWASP pattern
|
|
118
|
+
detection on the given files.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
workspace: Target workspace
|
|
122
|
+
files: List of file paths to review (relative to repo root)
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
ReviewResult with findings and overall score
|
|
126
|
+
"""
|
|
127
|
+
project_path = workspace.repo_path
|
|
128
|
+
findings: list[ReviewFinding] = []
|
|
129
|
+
|
|
130
|
+
# Initialize analyzers
|
|
131
|
+
complexity_analyzer = ComplexityAnalyzer(project_path)
|
|
132
|
+
security_scanner = SecurityScanner(project_path)
|
|
133
|
+
owasp_checker = OWASPPatterns(project_path)
|
|
134
|
+
|
|
135
|
+
# Track scores for averaging
|
|
136
|
+
scores: list[float] = []
|
|
137
|
+
|
|
138
|
+
for file_path in files:
|
|
139
|
+
full_path = project_path / file_path
|
|
140
|
+
|
|
141
|
+
if not full_path.exists():
|
|
142
|
+
logger.warning(f"File not found: {file_path}")
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
if not full_path.suffix == ".py":
|
|
146
|
+
# Only analyze Python files for now
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
# Complexity analysis
|
|
150
|
+
try:
|
|
151
|
+
complexity_findings = complexity_analyzer.analyze_file(full_path)
|
|
152
|
+
for finding in complexity_findings:
|
|
153
|
+
findings.append(
|
|
154
|
+
ReviewFinding(
|
|
155
|
+
category=finding.category,
|
|
156
|
+
severity=finding.severity,
|
|
157
|
+
message=finding.message,
|
|
158
|
+
file_path=file_path,
|
|
159
|
+
line_number=finding.line_number,
|
|
160
|
+
suggestion=finding.suggestion,
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
# Map severity to score for averaging
|
|
164
|
+
severity_scores = {"critical": 20, "high": 40, "medium": 60, "low": 80, "info": 95}
|
|
165
|
+
scores.append(severity_scores.get(finding.severity, 60))
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.warning(f"Complexity analysis failed for {file_path}: {e}")
|
|
168
|
+
|
|
169
|
+
# Security scan
|
|
170
|
+
try:
|
|
171
|
+
security_findings = security_scanner.analyze_file(full_path)
|
|
172
|
+
for finding in security_findings:
|
|
173
|
+
findings.append(
|
|
174
|
+
ReviewFinding(
|
|
175
|
+
category=finding.category,
|
|
176
|
+
severity=finding.severity,
|
|
177
|
+
message=finding.message,
|
|
178
|
+
file_path=file_path,
|
|
179
|
+
line_number=finding.line_number,
|
|
180
|
+
suggestion=finding.suggestion,
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
# Security issues have heavier weight on score
|
|
184
|
+
severity_scores = {"critical": 20, "high": 40, "medium": 60, "low": 80, "info": 95}
|
|
185
|
+
scores.append(severity_scores.get(finding.severity, 60))
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.warning(f"Security scan failed for {file_path}: {e}")
|
|
188
|
+
|
|
189
|
+
# OWASP pattern check
|
|
190
|
+
try:
|
|
191
|
+
owasp_findings = owasp_checker.check_file(full_path)
|
|
192
|
+
for finding in owasp_findings:
|
|
193
|
+
findings.append(
|
|
194
|
+
ReviewFinding(
|
|
195
|
+
category=finding.category,
|
|
196
|
+
severity=finding.severity,
|
|
197
|
+
message=finding.message,
|
|
198
|
+
file_path=file_path,
|
|
199
|
+
line_number=finding.line_number,
|
|
200
|
+
suggestion=finding.suggestion,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
# OWASP findings are typically high severity
|
|
204
|
+
severity_scores = {"critical": 20, "high": 40, "medium": 60, "low": 80, "info": 95}
|
|
205
|
+
scores.append(severity_scores.get(finding.severity, 40))
|
|
206
|
+
except Exception as e:
|
|
207
|
+
logger.warning(f"OWASP check failed for {file_path}: {e}")
|
|
208
|
+
|
|
209
|
+
# Calculate overall score
|
|
210
|
+
if scores:
|
|
211
|
+
overall_score = sum(scores) / len(scores)
|
|
212
|
+
else:
|
|
213
|
+
# No issues found = perfect score
|
|
214
|
+
overall_score = 100.0
|
|
215
|
+
|
|
216
|
+
status = _determine_status(overall_score)
|
|
217
|
+
|
|
218
|
+
# Generate summary
|
|
219
|
+
if not findings:
|
|
220
|
+
summary = "No issues found. Code looks good!"
|
|
221
|
+
else:
|
|
222
|
+
critical_count = sum(1 for f in findings if f.severity == "critical")
|
|
223
|
+
high_count = sum(1 for f in findings if f.severity == "high")
|
|
224
|
+
summary = f"Found {len(findings)} issues: {critical_count} critical, {high_count} high severity"
|
|
225
|
+
|
|
226
|
+
logger.info(f"Review completed: {status} (score: {overall_score:.1f})")
|
|
227
|
+
|
|
228
|
+
return ReviewResult(
|
|
229
|
+
status=status,
|
|
230
|
+
overall_score=round(overall_score, 1),
|
|
231
|
+
findings=findings,
|
|
232
|
+
summary=summary,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def review_task(
|
|
237
|
+
workspace: Workspace,
|
|
238
|
+
task_id: str,
|
|
239
|
+
files_modified: list[str],
|
|
240
|
+
) -> ReviewResult:
|
|
241
|
+
"""Run code review for a task's modified files.
|
|
242
|
+
|
|
243
|
+
Convenience function that wraps review_files with task context.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
workspace: Target workspace
|
|
247
|
+
task_id: Task ID (for logging)
|
|
248
|
+
files_modified: List of modified file paths
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
ReviewResult with findings and overall score
|
|
252
|
+
"""
|
|
253
|
+
logger.info(f"Starting review for task {task_id} ({len(files_modified)} files)")
|
|
254
|
+
return review_files(workspace, files_modified)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def get_review_summary(result: ReviewResult) -> dict:
|
|
258
|
+
"""Get a summary dict from review result.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
result: ReviewResult from review_files/review_task
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Summary dict suitable for API responses
|
|
265
|
+
"""
|
|
266
|
+
severity_counts = {
|
|
267
|
+
"critical": 0,
|
|
268
|
+
"high": 0,
|
|
269
|
+
"medium": 0,
|
|
270
|
+
"low": 0,
|
|
271
|
+
"info": 0,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
for finding in result.findings:
|
|
275
|
+
if finding.severity in severity_counts:
|
|
276
|
+
severity_counts[finding.severity] += 1
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
"status": result.status,
|
|
280
|
+
"overall_score": result.overall_score,
|
|
281
|
+
"total_findings": len(result.findings),
|
|
282
|
+
"severity_counts": severity_counts,
|
|
283
|
+
"summary": result.summary,
|
|
284
|
+
"has_blocking_issues": severity_counts["critical"] + severity_counts["high"] > 0,
|
|
285
|
+
}
|