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,281 @@
1
+ """Mock LLM provider for testing.
2
+
3
+ Provides predictable responses without making API calls.
4
+ Supports configurable responses and call tracking.
5
+ """
6
+
7
+ import asyncio
8
+ from typing import AsyncIterator, Callable, Iterator, Optional
9
+
10
+ from codeframe.adapters.llm.base import (
11
+ LLMProvider,
12
+ LLMResponse,
13
+ ModelSelector,
14
+ Purpose,
15
+ StreamChunk,
16
+ Tool,
17
+ ToolCall,
18
+ )
19
+
20
+
21
+ class MockProvider(LLMProvider):
22
+ """Mock LLM provider for testing.
23
+
24
+ Tracks all calls and returns configurable responses.
25
+ Useful for unit tests and development without API costs.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ default_response: str = "Mock response",
31
+ model_selector: Optional[ModelSelector] = None,
32
+ ):
33
+ """Initialize the mock provider.
34
+
35
+ Args:
36
+ default_response: Default response text
37
+ model_selector: Custom model selector
38
+ """
39
+ super().__init__(model_selector)
40
+ self.default_response = default_response
41
+ self.calls: list[dict] = []
42
+ self.responses: list[LLMResponse] = []
43
+ self.response_index = 0
44
+ self.response_handler: Optional[Callable[[list[dict]], LLMResponse]] = None
45
+ self.stream_chunks: list[list[StreamChunk]] = []
46
+ self.stream_index = 0
47
+
48
+ def add_response(self, response: LLMResponse) -> None:
49
+ """Add a canned response to the queue.
50
+
51
+ Responses are returned in order for subsequent calls.
52
+
53
+ Args:
54
+ response: Response to add to queue
55
+ """
56
+ self.responses.append(response)
57
+
58
+ def add_text_response(self, content: str) -> None:
59
+ """Add a simple text response to the queue.
60
+
61
+ Args:
62
+ content: Text content for the response
63
+ """
64
+ self.responses.append(LLMResponse(content=content))
65
+
66
+ def add_tool_response(self, tool_calls: list[ToolCall], content: str = "") -> None:
67
+ """Add a response with tool calls.
68
+
69
+ Args:
70
+ tool_calls: Tool calls to include
71
+ content: Optional text content
72
+ """
73
+ self.responses.append(
74
+ LLMResponse(
75
+ content=content,
76
+ tool_calls=tool_calls,
77
+ stop_reason="tool_use",
78
+ )
79
+ )
80
+
81
+ def set_response_handler(
82
+ self, handler: Callable[[list[dict]], LLMResponse]
83
+ ) -> None:
84
+ """Set a custom handler for generating responses.
85
+
86
+ The handler receives the messages and returns a response.
87
+ Takes precedence over queued responses.
88
+
89
+ Args:
90
+ handler: Function that takes messages and returns LLMResponse
91
+ """
92
+ self.response_handler = handler
93
+
94
+ def complete(
95
+ self,
96
+ messages: list[dict],
97
+ purpose: Purpose = Purpose.EXECUTION,
98
+ tools: Optional[list[Tool]] = None,
99
+ max_tokens: int = 4096,
100
+ temperature: float = 0.0,
101
+ system: Optional[str] = None,
102
+ ) -> LLMResponse:
103
+ """Return a mock completion.
104
+
105
+ Tracks the call and returns from queue or default.
106
+
107
+ Args:
108
+ messages: Conversation messages
109
+ purpose: Purpose of call (for model selection)
110
+ tools: Available tools (tracked but not used)
111
+ max_tokens: Maximum tokens (tracked but not used)
112
+ temperature: Temperature (tracked but not used)
113
+ system: System prompt (tracked but not used)
114
+
115
+ Returns:
116
+ Queued response, handler result, or default response
117
+ """
118
+ model = self.get_model(purpose)
119
+
120
+ # Track the call
121
+ self.calls.append(
122
+ {
123
+ "messages": messages,
124
+ "purpose": purpose,
125
+ "tools": tools,
126
+ "max_tokens": max_tokens,
127
+ "temperature": temperature,
128
+ "system": system,
129
+ "model": model,
130
+ }
131
+ )
132
+
133
+ # Custom handler takes precedence
134
+ if self.response_handler:
135
+ return self.response_handler(messages)
136
+
137
+ # Return queued response if available
138
+ if self.response_index < len(self.responses):
139
+ response = self.responses[self.response_index]
140
+ self.response_index += 1
141
+ return response
142
+
143
+ # Default response
144
+ return LLMResponse(
145
+ content=self.default_response,
146
+ model=model,
147
+ input_tokens=len(str(messages)),
148
+ output_tokens=len(self.default_response),
149
+ )
150
+
151
+ def stream(
152
+ self,
153
+ messages: list[dict],
154
+ purpose: Purpose = Purpose.EXECUTION,
155
+ max_tokens: int = 4096,
156
+ temperature: float = 0.0,
157
+ system: Optional[str] = None,
158
+ ) -> Iterator[str]:
159
+ """Stream mock response word by word.
160
+
161
+ Args:
162
+ messages: Conversation messages
163
+ purpose: Purpose of call
164
+ max_tokens: Maximum tokens
165
+ temperature: Temperature
166
+ system: System prompt
167
+
168
+ Yields:
169
+ Words from the response
170
+ """
171
+ response = self.complete(
172
+ messages=messages,
173
+ purpose=purpose,
174
+ max_tokens=max_tokens,
175
+ temperature=temperature,
176
+ system=system,
177
+ )
178
+ # Simulate streaming by yielding words
179
+ for word in response.content.split():
180
+ yield word + " "
181
+
182
+ def add_stream_chunks(self, chunks: list[StreamChunk]) -> None:
183
+ """Add a sequence of StreamChunks for the next async_stream() call.
184
+
185
+ Args:
186
+ chunks: Ordered list of StreamChunk objects to yield.
187
+ """
188
+ self.stream_chunks.append(chunks)
189
+
190
+ async def async_stream(
191
+ self,
192
+ messages: list[dict],
193
+ system: str,
194
+ tools: list[dict],
195
+ model: str,
196
+ max_tokens: int,
197
+ interrupt_event: Optional[asyncio.Event] = None,
198
+ extended_thinking: bool = False,
199
+ ) -> AsyncIterator[StreamChunk]:
200
+ """Yield pre-configured StreamChunk sequences for testing.
201
+
202
+ Tracks each call in :attr:`calls` (same metadata as :meth:`complete`).
203
+ When pre-configured ``stream_chunks`` are available, yields them in
204
+ order. Otherwise falls back to a minimal ``text_delta`` +
205
+ ``message_stop`` pair derived from the normal response queue
206
+ (``responses`` / ``response_handler`` / ``default_response``).
207
+ """
208
+ # Track the call so tests can assert on it
209
+ self.calls.append(
210
+ {
211
+ "messages": messages,
212
+ "system": system,
213
+ "tools": tools,
214
+ "model": model,
215
+ "max_tokens": max_tokens,
216
+ "extended_thinking": extended_thinking,
217
+ }
218
+ )
219
+
220
+ if self.stream_index < len(self.stream_chunks):
221
+ chunks = self.stream_chunks[self.stream_index]
222
+ self.stream_index += 1
223
+ else:
224
+ # Derive response text from the normal queue / handler
225
+ if self.response_handler:
226
+ resp = self.response_handler(messages)
227
+ text = resp.content
228
+ elif self.response_index < len(self.responses):
229
+ resp = self.responses[self.response_index]
230
+ self.response_index += 1
231
+ text = resp.content
232
+ else:
233
+ text = self.default_response
234
+
235
+ chunks = [
236
+ StreamChunk(type="text_delta", text=text),
237
+ StreamChunk(
238
+ type="message_stop",
239
+ stop_reason="end_turn",
240
+ input_tokens=len(str(messages)),
241
+ output_tokens=len(text),
242
+ tool_inputs_by_id={},
243
+ ),
244
+ ]
245
+
246
+ for chunk in chunks:
247
+ if interrupt_event and interrupt_event.is_set():
248
+ return
249
+ yield chunk
250
+
251
+ def reset(self) -> None:
252
+ """Reset call tracking and response queue."""
253
+ self.calls.clear()
254
+ self.responses.clear()
255
+ self.response_index = 0
256
+ self.response_handler = None
257
+ self.stream_chunks.clear()
258
+ self.stream_index = 0
259
+
260
+ @property
261
+ def call_count(self) -> int:
262
+ """Number of calls made to this provider."""
263
+ return len(self.calls)
264
+
265
+ @property
266
+ def last_call(self) -> Optional[dict]:
267
+ """The most recent call, or None if no calls made."""
268
+ return self.calls[-1] if self.calls else None
269
+
270
+ def get_call(self, index: int) -> Optional[dict]:
271
+ """Get a specific call by index.
272
+
273
+ Args:
274
+ index: Call index (0-based)
275
+
276
+ Returns:
277
+ Call dict or None if index out of range
278
+ """
279
+ if 0 <= index < len(self.calls):
280
+ return self.calls[index]
281
+ return None