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
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Continuous reconciliation engine for batch execution.
|
|
2
|
+
|
|
3
|
+
Periodically checks if tasks have been externally modified (GitHub issue
|
|
4
|
+
closed, task manually completed, blocker resolved) and adjusts the running
|
|
5
|
+
batch accordingly.
|
|
6
|
+
|
|
7
|
+
This module is headless (no FastAPI, no HTTP). It exposes a standalone
|
|
8
|
+
ReconciliationEngine that can be driven by a background thread in the
|
|
9
|
+
conductor.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from typing import TYPE_CHECKING, Callable, Optional
|
|
17
|
+
|
|
18
|
+
from codeframe.core import blockers, tasks
|
|
19
|
+
from codeframe.core.state_machine import TaskStatus
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from codeframe.core.workspace import Workspace
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ExternalStateChange:
|
|
29
|
+
"""A detected external change to a task's state."""
|
|
30
|
+
|
|
31
|
+
task_id: str
|
|
32
|
+
change_type: str # "completed", "closed", "blocker_resolved"
|
|
33
|
+
source: str # "manual", "github"
|
|
34
|
+
details: dict = field(default_factory=dict)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class ReconciliationResult:
|
|
39
|
+
"""Accumulated result from a reconciliation check."""
|
|
40
|
+
|
|
41
|
+
changes_detected: list[ExternalStateChange] = field(default_factory=list)
|
|
42
|
+
tasks_skipped: list[str] = field(default_factory=list)
|
|
43
|
+
tasks_requeued: list[str] = field(default_factory=list)
|
|
44
|
+
errors: list[str] = field(default_factory=list)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ReconciliationEngine:
|
|
48
|
+
"""Checks tasks for external state changes and applies adjustments.
|
|
49
|
+
|
|
50
|
+
The engine is stateless per invocation — call check_all_active() to scan,
|
|
51
|
+
then apply_changes() to act on the results.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
workspace: The workspace to check tasks in.
|
|
55
|
+
github_checker: Optional callable(task_id, task) -> list[ExternalStateChange]
|
|
56
|
+
for GitHub issue state sync. If None, GitHub checks are skipped.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
workspace: Workspace,
|
|
62
|
+
*,
|
|
63
|
+
github_checker: Optional[Callable] = None,
|
|
64
|
+
) -> None:
|
|
65
|
+
self._workspace = workspace
|
|
66
|
+
self._github_checker = github_checker
|
|
67
|
+
|
|
68
|
+
def check_task(self, task_id: str) -> list[ExternalStateChange]:
|
|
69
|
+
"""Check a single task for external state changes.
|
|
70
|
+
|
|
71
|
+
Returns a list of detected changes (may be empty).
|
|
72
|
+
"""
|
|
73
|
+
task = tasks.get(self._workspace, task_id)
|
|
74
|
+
if task is None:
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
changes: list[ExternalStateChange] = []
|
|
78
|
+
|
|
79
|
+
# Task was completed externally (e.g., manually marked DONE)
|
|
80
|
+
if task.status == TaskStatus.DONE:
|
|
81
|
+
changes.append(ExternalStateChange(
|
|
82
|
+
task_id=task_id,
|
|
83
|
+
change_type="completed",
|
|
84
|
+
source="manual",
|
|
85
|
+
details={"status": task.status.value},
|
|
86
|
+
))
|
|
87
|
+
|
|
88
|
+
# Task is blocked but all blockers have been answered
|
|
89
|
+
elif task.status == TaskStatus.BLOCKED:
|
|
90
|
+
task_blockers = blockers.list_for_task(self._workspace, task_id)
|
|
91
|
+
if task_blockers and all(
|
|
92
|
+
b.status.value in ("ANSWERED", "RESOLVED") for b in task_blockers
|
|
93
|
+
):
|
|
94
|
+
changes.append(ExternalStateChange(
|
|
95
|
+
task_id=task_id,
|
|
96
|
+
change_type="blocker_resolved",
|
|
97
|
+
source="manual",
|
|
98
|
+
details={"blockers_resolved": len(task_blockers)},
|
|
99
|
+
))
|
|
100
|
+
|
|
101
|
+
# Check GitHub issue state if checker is available
|
|
102
|
+
if self._github_checker:
|
|
103
|
+
try:
|
|
104
|
+
gh_changes = self._github_checker(task_id, task)
|
|
105
|
+
changes.extend(gh_changes)
|
|
106
|
+
except Exception as exc:
|
|
107
|
+
logger.warning("GitHub check failed for task %s: %s", task_id, exc)
|
|
108
|
+
|
|
109
|
+
return changes
|
|
110
|
+
|
|
111
|
+
def check_all_active(
|
|
112
|
+
self, active_task_ids: list[str]
|
|
113
|
+
) -> ReconciliationResult:
|
|
114
|
+
"""Check all active tasks for external state changes.
|
|
115
|
+
|
|
116
|
+
Individual task check failures are caught and logged — a single
|
|
117
|
+
failure never crashes the entire reconciliation pass.
|
|
118
|
+
"""
|
|
119
|
+
result = ReconciliationResult()
|
|
120
|
+
|
|
121
|
+
for task_id in active_task_ids:
|
|
122
|
+
try:
|
|
123
|
+
changes = self.check_task(task_id)
|
|
124
|
+
result.changes_detected.extend(changes)
|
|
125
|
+
except Exception as exc:
|
|
126
|
+
error_msg = f"Reconciliation check failed for {task_id}: {exc}"
|
|
127
|
+
result.errors.append(error_msg)
|
|
128
|
+
logger.warning(error_msg)
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
def apply_changes(
|
|
133
|
+
self,
|
|
134
|
+
result: ReconciliationResult,
|
|
135
|
+
batch: object,
|
|
136
|
+
active_processes: dict,
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Apply detected changes to the batch and running processes.
|
|
139
|
+
|
|
140
|
+
For completed/closed tasks: terminate the subprocess and skip.
|
|
141
|
+
For blocker_resolved tasks: mark for re-queue.
|
|
142
|
+
|
|
143
|
+
All exceptions are caught and appended to result.errors.
|
|
144
|
+
"""
|
|
145
|
+
for change in result.changes_detected:
|
|
146
|
+
try:
|
|
147
|
+
if change.change_type in ("completed", "closed"):
|
|
148
|
+
# Terminate subprocess if running
|
|
149
|
+
proc = active_processes.get(change.task_id)
|
|
150
|
+
if proc is not None:
|
|
151
|
+
try:
|
|
152
|
+
proc.terminate()
|
|
153
|
+
except OSError:
|
|
154
|
+
pass # Process already dead
|
|
155
|
+
|
|
156
|
+
result.tasks_skipped.append(change.task_id)
|
|
157
|
+
|
|
158
|
+
# Update batch results
|
|
159
|
+
if hasattr(batch, "results"):
|
|
160
|
+
status = "COMPLETED" if change.change_type == "completed" else "FAILED"
|
|
161
|
+
batch.results[change.task_id] = status
|
|
162
|
+
|
|
163
|
+
logger.info(
|
|
164
|
+
"Task %s %s externally (%s) — skipped in batch",
|
|
165
|
+
change.task_id, change.change_type, change.source,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
elif change.change_type == "blocker_resolved":
|
|
169
|
+
result.tasks_requeued.append(change.task_id)
|
|
170
|
+
|
|
171
|
+
# Update batch results to signal re-queue
|
|
172
|
+
if hasattr(batch, "results"):
|
|
173
|
+
batch.results[change.task_id] = "READY"
|
|
174
|
+
|
|
175
|
+
logger.info(
|
|
176
|
+
"Task %s blocker resolved — re-queued",
|
|
177
|
+
change.task_id,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
except Exception as exc:
|
|
181
|
+
error_msg = f"Failed to apply change for {change.task_id}: {exc}"
|
|
182
|
+
result.errors.append(error_msg)
|
|
183
|
+
logger.warning(error_msg)
|