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.
Files changed (197) hide show
  1. codeframe/__init__.py +11 -0
  2. codeframe/__main__.py +20 -0
  3. codeframe/adapters/__init__.py +5 -0
  4. codeframe/adapters/e2b/__init__.py +13 -0
  5. codeframe/adapters/e2b/adapter.py +342 -0
  6. codeframe/adapters/e2b/budget.py +71 -0
  7. codeframe/adapters/e2b/credential_scanner.py +134 -0
  8. codeframe/adapters/llm/__init__.py +92 -0
  9. codeframe/adapters/llm/anthropic.py +414 -0
  10. codeframe/adapters/llm/base.py +444 -0
  11. codeframe/adapters/llm/mock.py +281 -0
  12. codeframe/adapters/llm/openai.py +483 -0
  13. codeframe/agents/__init__.py +8 -0
  14. codeframe/agents/dependency_resolver.py +714 -0
  15. codeframe/auth/__init__.py +16 -0
  16. codeframe/auth/api_key_router.py +238 -0
  17. codeframe/auth/api_keys.py +156 -0
  18. codeframe/auth/dependencies.py +358 -0
  19. codeframe/auth/manager.py +178 -0
  20. codeframe/auth/models.py +30 -0
  21. codeframe/auth/router.py +93 -0
  22. codeframe/auth/schemas.py +15 -0
  23. codeframe/auth/scopes.py +53 -0
  24. codeframe/cli/__init__.py +12 -0
  25. codeframe/cli/__main__.py +20 -0
  26. codeframe/cli/api_client.py +275 -0
  27. codeframe/cli/app.py +5688 -0
  28. codeframe/cli/auth.py +122 -0
  29. codeframe/cli/auth_commands.py +958 -0
  30. codeframe/cli/commands/__init__.py +5 -0
  31. codeframe/cli/config_commands.py +79 -0
  32. codeframe/cli/dashboard_commands.py +67 -0
  33. codeframe/cli/engines_commands.py +205 -0
  34. codeframe/cli/env_commands.py +409 -0
  35. codeframe/cli/helpers.py +56 -0
  36. codeframe/cli/hooks_commands.py +208 -0
  37. codeframe/cli/import_commands.py +129 -0
  38. codeframe/cli/pr_commands.py +549 -0
  39. codeframe/cli/proof_commands.py +415 -0
  40. codeframe/cli/stats_commands.py +311 -0
  41. codeframe/cli/telemetry_runtime.py +153 -0
  42. codeframe/cli/validators.py +123 -0
  43. codeframe/config/rate_limits.py +165 -0
  44. codeframe/core/__init__.py +15 -0
  45. codeframe/core/adapters/__init__.py +43 -0
  46. codeframe/core/adapters/agent_adapter.py +114 -0
  47. codeframe/core/adapters/builtin.py +326 -0
  48. codeframe/core/adapters/claude_code.py +62 -0
  49. codeframe/core/adapters/codex.py +393 -0
  50. codeframe/core/adapters/git_utils.py +40 -0
  51. codeframe/core/adapters/kilocode.py +126 -0
  52. codeframe/core/adapters/opencode.py +48 -0
  53. codeframe/core/adapters/streaming_chat.py +483 -0
  54. codeframe/core/adapters/subprocess_adapter.py +213 -0
  55. codeframe/core/adapters/verification_wrapper.py +269 -0
  56. codeframe/core/agent.py +2183 -0
  57. codeframe/core/agents_config.py +569 -0
  58. codeframe/core/api_key_service.py +211 -0
  59. codeframe/core/artifacts.py +428 -0
  60. codeframe/core/blocker_detection.py +218 -0
  61. codeframe/core/blockers.py +433 -0
  62. codeframe/core/checkpoints.py +481 -0
  63. codeframe/core/conductor.py +2255 -0
  64. codeframe/core/config.py +827 -0
  65. codeframe/core/config_watcher.py +268 -0
  66. codeframe/core/context.py +542 -0
  67. codeframe/core/context_packager.py +234 -0
  68. codeframe/core/credentials.py +735 -0
  69. codeframe/core/dependency_analyzer.py +229 -0
  70. codeframe/core/dependency_graph.py +290 -0
  71. codeframe/core/diagnostic_agent.py +712 -0
  72. codeframe/core/diagnostics.py +616 -0
  73. codeframe/core/editor.py +556 -0
  74. codeframe/core/engine_registry.py +256 -0
  75. codeframe/core/engine_stats.py +231 -0
  76. codeframe/core/environment.py +697 -0
  77. codeframe/core/events.py +375 -0
  78. codeframe/core/executor.py +1005 -0
  79. codeframe/core/fix_tracker.py +480 -0
  80. codeframe/core/gates.py +1322 -0
  81. codeframe/core/git.py +477 -0
  82. codeframe/core/github_connect_service.py +178 -0
  83. codeframe/core/github_integration_config.py +118 -0
  84. codeframe/core/github_issues_service.py +449 -0
  85. codeframe/core/hooks.py +184 -0
  86. codeframe/core/importers/__init__.py +1 -0
  87. codeframe/core/importers/ralph.py +540 -0
  88. codeframe/core/installer.py +650 -0
  89. codeframe/core/models.py +1026 -0
  90. codeframe/core/notifications_config.py +183 -0
  91. codeframe/core/planner.py +437 -0
  92. codeframe/core/prd.py +670 -0
  93. codeframe/core/prd_discovery.py +1118 -0
  94. codeframe/core/prd_stress_test.py +499 -0
  95. codeframe/core/progress.py +126 -0
  96. codeframe/core/proof/__init__.py +34 -0
  97. codeframe/core/proof/capture.py +79 -0
  98. codeframe/core/proof/evidence.py +56 -0
  99. codeframe/core/proof/ledger.py +574 -0
  100. codeframe/core/proof/models.py +162 -0
  101. codeframe/core/proof/obligations.py +103 -0
  102. codeframe/core/proof/runner.py +233 -0
  103. codeframe/core/proof/scope.py +81 -0
  104. codeframe/core/proof/stubs.py +156 -0
  105. codeframe/core/quick_fixes.py +558 -0
  106. codeframe/core/react_agent.py +1650 -0
  107. codeframe/core/reconciliation.py +183 -0
  108. codeframe/core/replay.py +788 -0
  109. codeframe/core/review.py +285 -0
  110. codeframe/core/runtime.py +1134 -0
  111. codeframe/core/sandbox/__init__.py +27 -0
  112. codeframe/core/sandbox/context.py +98 -0
  113. codeframe/core/sandbox/worktree.py +20 -0
  114. codeframe/core/schedule.py +396 -0
  115. codeframe/core/stall_detector.py +71 -0
  116. codeframe/core/stall_monitor.py +134 -0
  117. codeframe/core/state_machine.py +121 -0
  118. codeframe/core/streaming.py +502 -0
  119. codeframe/core/task_tree.py +400 -0
  120. codeframe/core/tasks.py +1022 -0
  121. codeframe/core/telemetry.py +232 -0
  122. codeframe/core/templates.py +221 -0
  123. codeframe/core/tools.py +942 -0
  124. codeframe/core/workspace.py +887 -0
  125. codeframe/core/worktrees.py +276 -0
  126. codeframe/git/__init__.py +5 -0
  127. codeframe/git/github_integration.py +505 -0
  128. codeframe/lib/__init__.py +0 -0
  129. codeframe/lib/audit_logger.py +248 -0
  130. codeframe/lib/metrics_tracker.py +800 -0
  131. codeframe/lib/quality/__init__.py +7 -0
  132. codeframe/lib/quality/complexity_analyzer.py +316 -0
  133. codeframe/lib/quality/owasp_patterns.py +284 -0
  134. codeframe/lib/quality/security_scanner.py +250 -0
  135. codeframe/lib/rate_limiter.py +312 -0
  136. codeframe/notifications/__init__.py +0 -0
  137. codeframe/notifications/webhook.py +380 -0
  138. codeframe/planning/__init__.py +30 -0
  139. codeframe/planning/issue_generator.py +219 -0
  140. codeframe/planning/prd_template_functions.py +137 -0
  141. codeframe/planning/prd_templates.py +975 -0
  142. codeframe/planning/task_scheduler.py +511 -0
  143. codeframe/planning/task_templates.py +533 -0
  144. codeframe/platform_store/__init__.py +5 -0
  145. codeframe/platform_store/database.py +277 -0
  146. codeframe/platform_store/repositories/__init__.py +24 -0
  147. codeframe/platform_store/repositories/api_key_repository.py +245 -0
  148. codeframe/platform_store/repositories/audit_repository.py +67 -0
  149. codeframe/platform_store/repositories/base.py +295 -0
  150. codeframe/platform_store/repositories/interactive_sessions.py +165 -0
  151. codeframe/platform_store/repositories/token_repository.py +598 -0
  152. codeframe/platform_store/repositories/workspace_registry_repository.py +175 -0
  153. codeframe/platform_store/schema_manager.py +321 -0
  154. codeframe/templates/AGENTS.md.default +94 -0
  155. codeframe/tui/__init__.py +5 -0
  156. codeframe/tui/app.py +256 -0
  157. codeframe/tui/data_service.py +103 -0
  158. codeframe/ui/__init__.py +0 -0
  159. codeframe/ui/dependencies.py +103 -0
  160. codeframe/ui/models.py +999 -0
  161. codeframe/ui/response_models.py +201 -0
  162. codeframe/ui/routers/__init__.py +5 -0
  163. codeframe/ui/routers/_helpers.py +29 -0
  164. codeframe/ui/routers/batches_v2.py +315 -0
  165. codeframe/ui/routers/blockers_v2.py +320 -0
  166. codeframe/ui/routers/checkpoints_v2.py +310 -0
  167. codeframe/ui/routers/costs_v2.py +322 -0
  168. codeframe/ui/routers/diagnose_v2.py +225 -0
  169. codeframe/ui/routers/discovery_v2.py +417 -0
  170. codeframe/ui/routers/environment_v2.py +284 -0
  171. codeframe/ui/routers/events_v2.py +75 -0
  172. codeframe/ui/routers/gates_v2.py +166 -0
  173. codeframe/ui/routers/git_v2.py +284 -0
  174. codeframe/ui/routers/github_integrations_v2.py +532 -0
  175. codeframe/ui/routers/interactive_sessions_v2.py +238 -0
  176. codeframe/ui/routers/pr_v2.py +709 -0
  177. codeframe/ui/routers/prd_v2.py +695 -0
  178. codeframe/ui/routers/proof_v2.py +755 -0
  179. codeframe/ui/routers/review_v2.py +360 -0
  180. codeframe/ui/routers/schedule_v2.py +214 -0
  181. codeframe/ui/routers/session_chat_ws.py +354 -0
  182. codeframe/ui/routers/settings_v2.py +562 -0
  183. codeframe/ui/routers/streaming_v2.py +155 -0
  184. codeframe/ui/routers/tasks_v2.py +1098 -0
  185. codeframe/ui/routers/templates_v2.py +232 -0
  186. codeframe/ui/routers/terminal_ws.py +267 -0
  187. codeframe/ui/routers/workspace_v2.py +527 -0
  188. codeframe/ui/server.py +568 -0
  189. codeframe/ui/shared.py +241 -0
  190. codeframe/workspace/__init__.py +5 -0
  191. codeframe/workspace/manager.py +249 -0
  192. codeframe_ai-0.9.0.dist-info/METADATA +517 -0
  193. codeframe_ai-0.9.0.dist-info/RECORD +197 -0
  194. codeframe_ai-0.9.0.dist-info/WHEEL +5 -0
  195. codeframe_ai-0.9.0.dist-info/entry_points.txt +3 -0
  196. codeframe_ai-0.9.0.dist-info/licenses/LICENSE +661 -0
  197. codeframe_ai-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,134 @@
1
+ """Stall detection monitor for agent execution.
2
+
3
+ Detects when an agent stops making progress (no tool calls for a
4
+ configurable duration) and triggers a callback. Uses a daemon thread
5
+ that polls at regular intervals.
6
+
7
+ This module is headless - no FastAPI or HTTP dependencies.
8
+ """
9
+
10
+ import logging
11
+ import threading
12
+ from dataclasses import dataclass
13
+ from datetime import datetime, timezone
14
+ from typing import Callable, Optional
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ @dataclass
20
+ class StallEvent:
21
+ """Information about a detected stall.
22
+
23
+ Attributes:
24
+ task_id: The task that stalled.
25
+ stall_timeout_s: Configured timeout threshold.
26
+ elapsed_s: Seconds since last tool execution.
27
+ last_tool_call_at: Timestamp of the last tool execution (None if no tools ran).
28
+ iterations_completed: Number of loop iterations completed before stall.
29
+ """
30
+
31
+ task_id: str
32
+ stall_timeout_s: float
33
+ elapsed_s: float
34
+ last_tool_call_at: Optional[datetime]
35
+ iterations_completed: int
36
+
37
+
38
+ class StallMonitor:
39
+ """Thread-based watchdog that fires when agent activity stops.
40
+
41
+ Create one instance per agent invocation. The monitor is stateless
42
+ between runs.
43
+
44
+ Usage::
45
+
46
+ monitor = StallMonitor(stall_timeout_s=300, on_stall=my_callback)
47
+ monitor.start("task-123")
48
+ try:
49
+ for iteration in agent_loop:
50
+ # ... do work ...
51
+ monitor.notify_tool_executed("task-123", iteration)
52
+ finally:
53
+ monitor.stop()
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ stall_timeout_s: float,
59
+ on_stall: Callable[[StallEvent], None],
60
+ poll_interval_s: float = 5.0,
61
+ ) -> None:
62
+ self._stall_timeout_s = stall_timeout_s
63
+ self._on_stall = on_stall
64
+ self._poll_interval_s = poll_interval_s
65
+
66
+ self._task_id: Optional[str] = None
67
+ self._last_activity: Optional[datetime] = None
68
+ self._last_tool_call_at: Optional[datetime] = None
69
+ self._iterations: int = 0
70
+ self._lock = threading.Lock()
71
+ self._stop_event = threading.Event()
72
+ self._thread: Optional[threading.Thread] = None
73
+
74
+ def start(self, task_id: str) -> None:
75
+ """Begin monitoring for the given task."""
76
+ if self._stall_timeout_s <= 0:
77
+ return # Disabled
78
+
79
+ self.stop() # Clean up any previous run
80
+ self._task_id = task_id
81
+ self._last_activity = datetime.now(timezone.utc)
82
+ self._iterations = 0
83
+ self._stop_event.clear()
84
+
85
+ self._thread = threading.Thread(
86
+ target=self._watch_loop,
87
+ name=f"stall-monitor-{task_id}",
88
+ daemon=True,
89
+ )
90
+ self._thread.start()
91
+
92
+ def stop(self) -> None:
93
+ """Stop the watcher thread."""
94
+ self._stop_event.set()
95
+ if self._thread is not None:
96
+ self._thread.join(timeout=2.0)
97
+ self._thread = None
98
+
99
+ def notify_tool_executed(self, task_id: str, iteration: int) -> None:
100
+ """Record that a tool was successfully executed.
101
+
102
+ Call this after each successful tool execution to reset the
103
+ inactivity timer.
104
+ """
105
+ with self._lock:
106
+ now = datetime.now(timezone.utc)
107
+ self._last_activity = now
108
+ self._last_tool_call_at = now
109
+ self._iterations = iteration
110
+
111
+ def _watch_loop(self) -> None:
112
+ """Daemon thread loop: check for stall at regular intervals."""
113
+ while not self._stop_event.wait(timeout=self._poll_interval_s):
114
+ with self._lock:
115
+ if self._last_activity is None:
116
+ continue
117
+ elapsed = (datetime.now(timezone.utc) - self._last_activity).total_seconds()
118
+ iterations = self._iterations
119
+ last_tool_call = self._last_tool_call_at
120
+
121
+ if elapsed >= self._stall_timeout_s:
122
+ event = StallEvent(
123
+ task_id=self._task_id or "",
124
+ stall_timeout_s=self._stall_timeout_s,
125
+ elapsed_s=elapsed,
126
+ last_tool_call_at=last_tool_call,
127
+ iterations_completed=iterations,
128
+ )
129
+ try:
130
+ self._on_stall(event)
131
+ except Exception:
132
+ logger.exception("Stall callback failed")
133
+ self._stop_event.set()
134
+ return
@@ -0,0 +1,121 @@
1
+ """Task state machine for CodeFRAME v2.
2
+
3
+ Defines the authoritative task statuses and allowed transitions.
4
+ Per GOLDEN_PATH.md, the CLI is the authority for transitions.
5
+
6
+ Statuses:
7
+ - BACKLOG: Task identified but not ready to work on
8
+ - READY: Task is ready to be started
9
+ - IN_PROGRESS: Task is actively being worked on
10
+ - BLOCKED: Task is blocked by a blocker (human-in-the-loop)
11
+ - DONE: Task completed successfully
12
+ - MERGED: Task changes merged (optional, for later)
13
+
14
+ This module is headless - no FastAPI or HTTP dependencies.
15
+ """
16
+
17
+ from enum import Enum
18
+ from typing import Set
19
+
20
+
21
+ class TaskStatus(str, Enum):
22
+ """Task execution status.
23
+
24
+ Uses str mixin for easy JSON serialization and SQLite storage.
25
+ """
26
+
27
+ BACKLOG = "BACKLOG"
28
+ READY = "READY"
29
+ IN_PROGRESS = "IN_PROGRESS"
30
+ BLOCKED = "BLOCKED"
31
+ FAILED = "FAILED" # Task execution failed (technical error, needs retry)
32
+ DONE = "DONE"
33
+ MERGED = "MERGED"
34
+
35
+
36
+ # Allowed status transitions (from -> set of allowed targets)
37
+ ALLOWED_TRANSITIONS: dict[TaskStatus, Set[TaskStatus]] = {
38
+ TaskStatus.BACKLOG: {TaskStatus.READY},
39
+ TaskStatus.READY: {TaskStatus.IN_PROGRESS, TaskStatus.BACKLOG},
40
+ TaskStatus.IN_PROGRESS: {TaskStatus.BLOCKED, TaskStatus.DONE, TaskStatus.READY, TaskStatus.FAILED},
41
+ TaskStatus.BLOCKED: {TaskStatus.IN_PROGRESS, TaskStatus.READY},
42
+ TaskStatus.FAILED: {TaskStatus.READY, TaskStatus.IN_PROGRESS}, # Can retry from FAILED
43
+ TaskStatus.DONE: {TaskStatus.READY, TaskStatus.MERGED},
44
+ TaskStatus.MERGED: set(), # Terminal state
45
+ }
46
+
47
+
48
+ class InvalidTransitionError(Exception):
49
+ """Raised when an invalid status transition is attempted."""
50
+
51
+ def __init__(self, current: TaskStatus, target: TaskStatus):
52
+ self.current = current
53
+ self.target = target
54
+ allowed = ALLOWED_TRANSITIONS.get(current, set())
55
+ allowed_str = ", ".join(s.value for s in allowed) if allowed else "none"
56
+ super().__init__(
57
+ f"Invalid transition: {current.value} -> {target.value}. "
58
+ f"Allowed transitions from {current.value}: {allowed_str}"
59
+ )
60
+
61
+
62
+ def can_transition(current: TaskStatus, target: TaskStatus) -> bool:
63
+ """Check if a status transition is allowed.
64
+
65
+ Args:
66
+ current: Current task status
67
+ target: Desired target status
68
+
69
+ Returns:
70
+ True if transition is allowed, False otherwise
71
+ """
72
+ allowed = ALLOWED_TRANSITIONS.get(current, set())
73
+ return target in allowed
74
+
75
+
76
+ def validate_transition(current: TaskStatus, target: TaskStatus) -> None:
77
+ """Validate a status transition, raising if invalid.
78
+
79
+ Args:
80
+ current: Current task status
81
+ target: Desired target status
82
+
83
+ Raises:
84
+ InvalidTransitionError: If the transition is not allowed
85
+ """
86
+ if not can_transition(current, target):
87
+ raise InvalidTransitionError(current, target)
88
+
89
+
90
+ def get_allowed_transitions(current: TaskStatus) -> Set[TaskStatus]:
91
+ """Get the set of statuses that can be transitioned to from current.
92
+
93
+ Args:
94
+ current: Current task status
95
+
96
+ Returns:
97
+ Set of allowed target statuses
98
+ """
99
+ return ALLOWED_TRANSITIONS.get(current, set()).copy()
100
+
101
+
102
+ def parse_status(value: str) -> TaskStatus:
103
+ """Parse a string into a TaskStatus.
104
+
105
+ Accepts both uppercase and lowercase input.
106
+
107
+ Args:
108
+ value: Status string (e.g., "READY", "ready", "in_progress")
109
+
110
+ Returns:
111
+ TaskStatus enum value
112
+
113
+ Raises:
114
+ ValueError: If the string doesn't match any status
115
+ """
116
+ normalized = value.upper().replace("-", "_")
117
+ try:
118
+ return TaskStatus(normalized)
119
+ except ValueError:
120
+ valid = ", ".join(s.value for s in TaskStatus)
121
+ raise ValueError(f"Invalid status '{value}'. Valid statuses: {valid}")