devloop 0.2.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 (55) hide show
  1. devloop/__init__.py +3 -0
  2. devloop/agents/__init__.py +33 -0
  3. devloop/agents/agent_health_monitor.py +105 -0
  4. devloop/agents/ci_monitor.py +237 -0
  5. devloop/agents/code_rabbit.py +248 -0
  6. devloop/agents/doc_lifecycle.py +374 -0
  7. devloop/agents/echo.py +24 -0
  8. devloop/agents/file_logger.py +46 -0
  9. devloop/agents/formatter.py +511 -0
  10. devloop/agents/git_commit_assistant.py +421 -0
  11. devloop/agents/linter.py +399 -0
  12. devloop/agents/performance_profiler.py +284 -0
  13. devloop/agents/security_scanner.py +322 -0
  14. devloop/agents/snyk.py +292 -0
  15. devloop/agents/test_runner.py +484 -0
  16. devloop/agents/type_checker.py +242 -0
  17. devloop/cli/__init__.py +1 -0
  18. devloop/cli/commands/__init__.py +1 -0
  19. devloop/cli/commands/custom_agents.py +144 -0
  20. devloop/cli/commands/feedback.py +161 -0
  21. devloop/cli/commands/summary.py +50 -0
  22. devloop/cli/main.py +430 -0
  23. devloop/cli/main_v1.py +144 -0
  24. devloop/collectors/__init__.py +17 -0
  25. devloop/collectors/base.py +55 -0
  26. devloop/collectors/filesystem.py +126 -0
  27. devloop/collectors/git.py +171 -0
  28. devloop/collectors/manager.py +159 -0
  29. devloop/collectors/process.py +221 -0
  30. devloop/collectors/system.py +195 -0
  31. devloop/core/__init__.py +21 -0
  32. devloop/core/agent.py +206 -0
  33. devloop/core/agent_template.py +498 -0
  34. devloop/core/amp_integration.py +166 -0
  35. devloop/core/auto_fix.py +224 -0
  36. devloop/core/config.py +272 -0
  37. devloop/core/context.py +0 -0
  38. devloop/core/context_store.py +530 -0
  39. devloop/core/contextual_feedback.py +311 -0
  40. devloop/core/custom_agent.py +439 -0
  41. devloop/core/debug_trace.py +289 -0
  42. devloop/core/event.py +105 -0
  43. devloop/core/event_store.py +316 -0
  44. devloop/core/feedback.py +311 -0
  45. devloop/core/learning.py +351 -0
  46. devloop/core/manager.py +219 -0
  47. devloop/core/performance.py +433 -0
  48. devloop/core/proactive_feedback.py +302 -0
  49. devloop/core/summary_formatter.py +159 -0
  50. devloop/core/summary_generator.py +275 -0
  51. devloop-0.2.0.dist-info/METADATA +705 -0
  52. devloop-0.2.0.dist-info/RECORD +55 -0
  53. devloop-0.2.0.dist-info/WHEEL +4 -0
  54. devloop-0.2.0.dist-info/entry_points.txt +3 -0
  55. devloop-0.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,311 @@
1
+ """Contextual feedback inference from developer behavior patterns."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import time
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from .event import Event, EventBus
12
+ from .feedback import FeedbackAPI, FeedbackType
13
+
14
+
15
+ @dataclass
16
+ class DeveloperAction:
17
+ """Represents a developer action that can provide implicit feedback."""
18
+
19
+ action_type: (
20
+ str # 'file_save', 'file_edit', 'cursor_move', 'suggestion_accept', etc.
21
+ )
22
+ file_path: Optional[str]
23
+ timestamp: float
24
+ context: Optional[Dict[str, Any]] = None
25
+
26
+ def __post_init__(self):
27
+ if self.context is None:
28
+ self.context = {}
29
+
30
+
31
+ class ContextualFeedbackEngine:
32
+ """Engine that infers feedback from developer behavior patterns."""
33
+
34
+ def __init__(
35
+ self, event_bus: EventBus, feedback_api: FeedbackAPI, project_dir: Path
36
+ ):
37
+ self.event_bus = event_bus
38
+ self.feedback_api = feedback_api
39
+ self.project_dir = project_dir
40
+
41
+ # Track recent developer actions
42
+ self.recent_actions: List[DeveloperAction] = []
43
+ self.action_window = 300 # 5 minutes window for action correlation
44
+
45
+ # Track agent actions to correlate with developer responses
46
+ self.recent_agent_actions: List[Dict[str, Any]] = []
47
+ self.agent_action_window = 600 # 10 minutes
48
+
49
+ # File interaction patterns
50
+ self.file_interaction_times: Dict[str, List[float]] = {}
51
+ self.file_last_modified: Dict[str, float] = {}
52
+
53
+ # Initialize event subscriptions
54
+ self._setup_event_listeners()
55
+
56
+ def _setup_event_listeners(self):
57
+ """Set up listeners for various developer actions."""
58
+ # Listen for agent completion events to correlate with developer actions
59
+ asyncio.create_task(
60
+ self.event_bus.subscribe("agent:*:completed", self._on_agent_completed)
61
+ )
62
+
63
+ # Listen for file system events that indicate developer activity
64
+ asyncio.create_task(self.event_bus.subscribe("file:*", self._on_file_event))
65
+
66
+ # Could be extended to listen for IDE events like cursor movements, selections, etc.
67
+
68
+ async def _on_agent_completed(self, event: Event) -> None:
69
+ """Handle agent completion and look for correlated developer feedback."""
70
+ agent_name = event.payload.get("agent_name")
71
+ success = event.payload.get("success", False)
72
+ duration = event.payload.get("duration", 0)
73
+
74
+ # Store agent action for correlation
75
+ agent_action = {
76
+ "agent_name": agent_name,
77
+ "success": success,
78
+ "duration": duration,
79
+ "timestamp": time.time(),
80
+ "event": event.payload,
81
+ }
82
+
83
+ self.recent_agent_actions.append(agent_action)
84
+
85
+ # Clean old agent actions
86
+ cutoff = time.time() - self.agent_action_window
87
+ self.recent_agent_actions = [
88
+ action
89
+ for action in self.recent_agent_actions
90
+ if action["timestamp"] > cutoff
91
+ ]
92
+
93
+ # Check for immediate feedback patterns
94
+ await self._analyze_immediate_feedback(agent_action)
95
+
96
+ async def _on_file_event(self, event: Event) -> None:
97
+ """Handle file system events as developer actions."""
98
+ file_path = event.payload.get("path")
99
+ if not file_path:
100
+ return
101
+
102
+ action = DeveloperAction(
103
+ action_type=f"file_{event.type.split(':')[1]}", # file_modified, file_created, etc.
104
+ file_path=file_path,
105
+ timestamp=time.time(),
106
+ context={"event_payload": event.payload},
107
+ )
108
+
109
+ self.recent_actions.append(action)
110
+
111
+ # Track file interaction patterns
112
+ if file_path not in self.file_interaction_times:
113
+ self.file_interaction_times[file_path] = []
114
+
115
+ self.file_interaction_times[file_path].append(action.timestamp)
116
+
117
+ # Keep only recent interactions (last 24 hours)
118
+ cutoff = time.time() - 86400
119
+ self.file_interaction_times[file_path] = [
120
+ t for t in self.file_interaction_times[file_path] if t > cutoff
121
+ ]
122
+
123
+ # Update last modified time
124
+ if event.type == "file:modified":
125
+ self.file_last_modified[file_path] = action.timestamp
126
+
127
+ # Analyze patterns
128
+ await self._analyze_file_patterns(file_path, action)
129
+
130
+ async def _analyze_immediate_feedback(self, agent_action: Dict[str, Any]) -> None:
131
+ """Analyze immediate feedback patterns after agent actions."""
132
+ agent_time = agent_action["timestamp"]
133
+
134
+ # Look for developer actions within 30 seconds after agent action
135
+ immediate_window = 30
136
+ recent_actions = [
137
+ action
138
+ for action in self.recent_actions
139
+ if agent_time <= action.timestamp <= agent_time + immediate_window
140
+ ]
141
+
142
+ if not recent_actions:
143
+ return
144
+
145
+ # Analyze the pattern of immediate actions
146
+ file_actions = [a for a in recent_actions if a.action_type.startswith("file_")]
147
+
148
+ if file_actions:
149
+ # Developer made file changes shortly after agent action
150
+ # This could indicate engagement with agent results
151
+ await self._infer_feedback_from_file_changes(agent_action, file_actions)
152
+
153
+ async def _analyze_file_patterns(
154
+ self, file_path: str, latest_action: DeveloperAction
155
+ ) -> None:
156
+ """Analyze file interaction patterns to infer feedback."""
157
+ if file_path not in self.file_interaction_times:
158
+ return
159
+
160
+ interactions = self.file_interaction_times[file_path]
161
+
162
+ # Look for patterns that might indicate satisfaction/dissatisfaction
163
+ if len(interactions) >= 3:
164
+ # Calculate interaction frequency
165
+ time_span = interactions[-1] - interactions[0]
166
+ if time_span > 0:
167
+ frequency = len(interactions) / time_span # interactions per second
168
+
169
+ # High frequency of interactions might indicate dissatisfaction
170
+ # (developer repeatedly editing the same file)
171
+ if frequency > 0.01: # More than 1 interaction per 100 seconds
172
+ await self._infer_feedback_from_interaction_pattern(
173
+ file_path, frequency, "high_frequency_editing"
174
+ )
175
+
176
+ # Check if file was modified shortly after being touched by agents
177
+ last_modified = self.file_last_modified.get(file_path, 0)
178
+ if latest_action.timestamp - last_modified < 60: # Modified within 1 minute
179
+ await self._infer_feedback_from_quick_modification(file_path, latest_action)
180
+
181
+ async def _infer_feedback_from_file_changes(
182
+ self, agent_action: Dict[str, Any], file_actions: List[DeveloperAction]
183
+ ) -> None:
184
+ """Infer feedback from file changes after agent action."""
185
+
186
+ # If developer modifies files that agent just processed, it might indicate
187
+ # they're refining the agent's work (mixed feedback)
188
+ modified_files = set(
189
+ a.file_path for a in file_actions if a.action_type == "file_modified"
190
+ )
191
+
192
+ if modified_files:
193
+ # Submit neutral/mixed feedback
194
+ await self.feedback_api.submit_feedback(
195
+ agent_name=agent_action["agent_name"],
196
+ event_type="file_interaction",
197
+ feedback_type=FeedbackType.RATING,
198
+ value=3, # Neutral rating
199
+ comment="Developer refined agent output",
200
+ context={
201
+ "agent_action": agent_action,
202
+ "modified_files": list(modified_files),
203
+ "inference_type": "file_changes_after_agent",
204
+ },
205
+ )
206
+
207
+ async def _infer_feedback_from_interaction_pattern(
208
+ self, file_path: str, frequency: float, pattern_type: str
209
+ ) -> None:
210
+ """Infer feedback from file interaction patterns."""
211
+ # Find agents that recently worked on this file
212
+ recent_agents = []
213
+ for agent_action in self.recent_agent_actions[-10:]: # Last 10 agent actions
214
+ # This is a simplified check - in reality we'd need to correlate
215
+ # agent actions with specific files
216
+ recent_agents.append(agent_action["agent_name"])
217
+
218
+ if not recent_agents:
219
+ return
220
+
221
+ # High frequency editing might indicate dissatisfaction
222
+ if frequency > 0.02: # Very high frequency
223
+ feedback_value = 2 # Low rating
224
+ comment = f"High frequency file editing detected ({frequency:.3f} interactions/sec)"
225
+ elif frequency > 0.01: # Moderately high
226
+ feedback_value = 3 # Neutral rating
227
+ comment = f"Moderate file interaction frequency ({frequency:.3f} interactions/sec)"
228
+ else:
229
+ return # Normal frequency, no inference needed
230
+
231
+ # Submit feedback for the most recent agent
232
+ most_recent_agent = recent_agents[-1]
233
+ await self.feedback_api.submit_feedback(
234
+ agent_name=most_recent_agent,
235
+ event_type="file_interaction_pattern",
236
+ feedback_type=FeedbackType.RATING,
237
+ value=feedback_value,
238
+ comment=comment,
239
+ context={
240
+ "file_path": file_path,
241
+ "interaction_frequency": frequency,
242
+ "pattern_type": pattern_type,
243
+ "inference_type": "interaction_pattern",
244
+ },
245
+ )
246
+
247
+ async def _infer_feedback_from_quick_modification(
248
+ self, file_path: str, action: DeveloperAction
249
+ ) -> None:
250
+ """Infer feedback from quick file modifications."""
251
+ # Find the most recent agent that might have worked on this file
252
+ # This is simplified - in a real implementation we'd track agent-file associations
253
+ recent_agent = None
254
+ for agent_action in reversed(self.recent_agent_actions[-5:]):
255
+ # Simplified check - look for agents that completed recently
256
+ if time.time() - agent_action["timestamp"] < 300: # Within 5 minutes
257
+ recent_agent = agent_action["agent_name"]
258
+ break
259
+
260
+ if not recent_agent:
261
+ return
262
+
263
+ # Quick modification might indicate the developer is actively working
264
+ # with the agent's output, which is generally positive
265
+ await self.feedback_api.submit_feedback(
266
+ agent_name=recent_agent,
267
+ event_type="quick_file_modification",
268
+ feedback_type=FeedbackType.THUMBS_UP,
269
+ value=True,
270
+ comment="Developer quickly engaged with agent output",
271
+ context={
272
+ "file_path": file_path,
273
+ "time_since_agent": time.time() - (recent_agent and 0 or time.time()),
274
+ "inference_type": "quick_modification",
275
+ },
276
+ )
277
+
278
+ async def get_contextual_insights(self, agent_name: str) -> Dict[str, Any]:
279
+ """Get contextual insights about developer behavior patterns."""
280
+ # Analyze recent actions for patterns
281
+ cutoff_time = time.time() - 3600 # Last hour
282
+
283
+ agent_related_actions = []
284
+ for action in self.recent_agent_actions:
285
+ if action["agent_name"] == agent_name and action["timestamp"] > cutoff_time:
286
+ agent_related_actions.append(action)
287
+
288
+ # Count correlated developer actions
289
+ correlated_file_actions = 0
290
+ for agent_action in agent_related_actions:
291
+ agent_time = agent_action["timestamp"]
292
+ # Count developer actions within 5 minutes after agent action
293
+ correlated = sum(
294
+ 1
295
+ for action in self.recent_actions
296
+ if agent_time <= action.timestamp <= agent_time + 300
297
+ )
298
+ correlated_file_actions += correlated
299
+
300
+ return {
301
+ "agent_name": agent_name,
302
+ "time_window_hours": 1,
303
+ "agent_actions_count": len(agent_related_actions),
304
+ "correlated_developer_actions": correlated_file_actions,
305
+ "inference_types": [
306
+ "file_changes",
307
+ "interaction_patterns",
308
+ "quick_modifications",
309
+ ],
310
+ "confidence_level": "medium", # Could be improved with ML
311
+ }