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,275 @@
1
+ """Summary generator for dev-agent findings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from dataclasses import dataclass
7
+ from datetime import datetime, timedelta, UTC
8
+ from typing import Any, Dict, List, Tuple
9
+
10
+ from .context_store import Finding, Severity, Tier, context_store
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @dataclass
16
+ class AgentSummary:
17
+ """Summary for a specific agent."""
18
+
19
+ agent_name: str
20
+ finding_count: int
21
+ severity_breakdown: Dict[str, int]
22
+ top_issues: List[Finding]
23
+ improvement_trend: str # "improving", "worsening", "stable"
24
+
25
+
26
+ @dataclass
27
+ class SummaryReport:
28
+ """Comprehensive summary report."""
29
+
30
+ scope: str
31
+ time_range: Tuple[datetime, datetime]
32
+ total_findings: int
33
+
34
+ # Breakdowns
35
+ by_agent: Dict[str, AgentSummary]
36
+ by_severity: Dict[str, int]
37
+ by_category: Dict[str, int]
38
+
39
+ # Trends
40
+ trends: Dict[str, Any]
41
+
42
+ # Priority items
43
+ critical_issues: List[Finding]
44
+ auto_fixable: List[Finding]
45
+
46
+ # Insights
47
+ insights: List[str]
48
+
49
+
50
+ class SummaryGenerator:
51
+ """Generate intelligent summaries of dev-agent findings."""
52
+
53
+ def __init__(self, context_store_instance=None):
54
+ """Initialize summary generator.
55
+
56
+ Args:
57
+ context_store_instance: Context store to use (defaults to global)
58
+ """
59
+ self.context_store = context_store_instance or context_store
60
+
61
+ async def generate_summary(
62
+ self, scope: str = "recent", filters: Dict[str, Any] = None
63
+ ) -> SummaryReport:
64
+ """Generate intelligent summary of findings.
65
+
66
+ Args:
67
+ scope: Time scope ("recent", "today", "session", "all")
68
+ filters: Optional filters (agent, severity, category)
69
+
70
+ Returns:
71
+ SummaryReport with findings analysis
72
+ """
73
+ filters = filters or {}
74
+
75
+ # Get time range for scope
76
+ time_range = self._get_time_range(scope)
77
+
78
+ # Get all findings within time range
79
+ findings = await self._get_findings_in_range(time_range)
80
+
81
+ # Apply filters
82
+ findings = self._filter_findings(findings, filters)
83
+
84
+ # Generate report
85
+ report = SummaryReport(
86
+ scope=scope,
87
+ time_range=time_range,
88
+ total_findings=len(findings),
89
+ by_agent=self._group_by_agent(findings),
90
+ by_severity=self._count_by_severity(findings),
91
+ by_category=self._count_by_category(findings),
92
+ trends=self._calculate_trends(findings, scope),
93
+ critical_issues=self._get_critical_issues(findings),
94
+ auto_fixable=self._get_auto_fixable(findings),
95
+ insights=self._generate_insights(findings, scope),
96
+ )
97
+
98
+ return report
99
+
100
+ def _get_time_range(self, scope: str) -> Tuple[datetime, datetime]:
101
+ """Get datetime range for scope."""
102
+ now = datetime.now(UTC)
103
+
104
+ if scope == "recent":
105
+ # Last 24 hours
106
+ start = now - timedelta(hours=24)
107
+ elif scope == "today":
108
+ # Start of today
109
+ start = now.replace(hour=0, minute=0, second=0, microsecond=0)
110
+ elif scope == "session":
111
+ # Last 4 hours (typical coding session)
112
+ start = now - timedelta(hours=4)
113
+ elif scope == "all":
114
+ # All time - use a very old date
115
+ start = datetime(2020, 1, 1, tzinfo=UTC)
116
+ else:
117
+ raise ValueError(f"Unknown scope: {scope}")
118
+
119
+ return (start, now)
120
+
121
+ async def _get_findings_in_range(
122
+ self, time_range: Tuple[datetime, datetime]
123
+ ) -> List[Finding]:
124
+ """Get all findings within time range."""
125
+ start_time, end_time = time_range
126
+
127
+ # Get findings from all tiers
128
+ all_findings = []
129
+ for tier in [Tier.IMMEDIATE, Tier.RELEVANT, Tier.BACKGROUND]:
130
+ tier_findings = await self.context_store.get_findings(tier=tier)
131
+ all_findings.extend(tier_findings)
132
+
133
+ # Filter by timestamp
134
+ filtered_findings = []
135
+ for finding in all_findings:
136
+ try:
137
+ finding_time = datetime.fromisoformat(
138
+ finding.timestamp.replace("Z", "+00:00")
139
+ )
140
+ if start_time <= finding_time <= end_time:
141
+ filtered_findings.append(finding)
142
+ except (ValueError, AttributeError):
143
+ # If timestamp parsing fails, include the finding
144
+ filtered_findings.append(finding)
145
+
146
+ return filtered_findings
147
+
148
+ def _filter_findings(
149
+ self, findings: List[Finding], filters: Dict[str, Any]
150
+ ) -> List[Finding]:
151
+ """Apply user-specified filters."""
152
+ if not filters:
153
+ return findings
154
+
155
+ filtered = findings
156
+
157
+ # Filter by agent
158
+ if "agent" in filters:
159
+ agent_filter = filters["agent"]
160
+ filtered = [f for f in filtered if f.agent == agent_filter]
161
+
162
+ # Filter by severity
163
+ if "severity" in filters:
164
+ severity_filter = filters["severity"]
165
+ try:
166
+ severity_enum = Severity(severity_filter)
167
+ filtered = [f for f in filtered if f.severity == severity_enum]
168
+ except ValueError:
169
+ logger.warning(f"Invalid severity filter: {severity_filter}")
170
+
171
+ # Filter by category
172
+ if "category" in filters:
173
+ category_filter = filters["category"]
174
+ filtered = [f for f in filtered if f.category == category_filter]
175
+
176
+ return filtered
177
+
178
+ def _group_by_agent(self, findings: List[Finding]) -> Dict[str, AgentSummary]:
179
+ """Group findings by agent type."""
180
+ agent_groups: Dict[str, List[Finding]] = {}
181
+ for finding in findings:
182
+ agent_groups.setdefault(finding.agent, []).append(finding)
183
+
184
+ summaries = {}
185
+ for agent_name, agent_findings in agent_groups.items():
186
+ # Count by severity
187
+ severity_counts: Dict[str, int] = {}
188
+ for finding in agent_findings:
189
+ severity = finding.severity.value
190
+ severity_counts[severity] = severity_counts.get(severity, 0) + 1
191
+
192
+ # Get top 3 issues by severity (errors first, then warnings, etc.)
193
+ sorted_findings = sorted(
194
+ agent_findings,
195
+ key=lambda f: (
196
+ f.severity.value == "error",
197
+ f.severity.value == "warning",
198
+ ),
199
+ reverse=True,
200
+ )
201
+ top_issues = sorted_findings[:3]
202
+
203
+ summaries[agent_name] = AgentSummary(
204
+ agent_name=agent_name,
205
+ finding_count=len(agent_findings),
206
+ severity_breakdown=severity_counts,
207
+ top_issues=top_issues,
208
+ improvement_trend="stable", # TODO: Implement trend calculation
209
+ )
210
+
211
+ return summaries
212
+
213
+ def _count_by_severity(self, findings: List[Finding]) -> Dict[str, int]:
214
+ """Count findings by severity."""
215
+ counts: Dict[str, int] = {}
216
+ for finding in findings:
217
+ severity = finding.severity.value
218
+ counts[severity] = counts.get(severity, 0) + 1
219
+ return counts
220
+
221
+ def _count_by_category(self, findings: List[Finding]) -> Dict[str, int]:
222
+ """Count findings by category."""
223
+ counts: Dict[str, int] = {}
224
+ for finding in findings:
225
+ counts[finding.category] = counts.get(finding.category, 0) + 1
226
+ return counts
227
+
228
+ def _calculate_trends(self, findings: List[Finding], scope: str) -> Dict[str, Any]:
229
+ """Calculate improvement/worsening trends."""
230
+ # TODO: Implement actual trend calculation comparing to previous periods
231
+ # For now, return basic stats
232
+ return {
233
+ "comparison_period": "previous_24h",
234
+ "change_percent": 0.0, # No change
235
+ "direction": "stable",
236
+ }
237
+
238
+ def _get_critical_issues(self, findings: List[Finding]) -> List[Finding]:
239
+ """Get critical/blocking issues."""
240
+ return [f for f in findings if f.blocking or f.severity == Severity.ERROR]
241
+
242
+ def _get_auto_fixable(self, findings: List[Finding]) -> List[Finding]:
243
+ """Get auto-fixable findings."""
244
+ return [f for f in findings if f.auto_fixable]
245
+
246
+ def _generate_insights(self, findings: List[Finding], scope: str) -> List[str]:
247
+ """Generate actionable insights."""
248
+ insights = []
249
+
250
+ if not findings:
251
+ return ["No findings in the selected scope"]
252
+
253
+ # Count by agent
254
+ agent_counts: Dict[str, int] = {}
255
+ for finding in findings:
256
+ agent_counts[finding.agent] = agent_counts.get(finding.agent, 0) + 1
257
+
258
+ # Most active agent
259
+ if agent_counts:
260
+ top_agent = max(agent_counts.items(), key=lambda x: x[1])
261
+ insights.append(
262
+ f"Most active agent: {top_agent[0]} ({top_agent[1]} findings)"
263
+ )
264
+
265
+ # Severity breakdown insight
266
+ error_count = sum(1 for f in findings if f.severity == Severity.ERROR)
267
+ if error_count > 0:
268
+ insights.append(f"{error_count} error-level findings requiring attention")
269
+
270
+ # Auto-fixable insight
271
+ auto_fixable_count = sum(1 for f in findings if f.auto_fixable)
272
+ if auto_fixable_count > 0:
273
+ insights.append(f"{auto_fixable_count} findings can be auto-fixed")
274
+
275
+ return insights