devloop 0.2.0__tar.gz → 0.2.2__tar.gz

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 (54) hide show
  1. {devloop-0.2.0 → devloop-0.2.2}/PKG-INFO +66 -11
  2. {devloop-0.2.0 → devloop-0.2.2}/README.md +65 -10
  3. {devloop-0.2.0 → devloop-0.2.2}/pyproject.toml +1 -1
  4. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/__init__.py +1 -1
  5. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/main.py +84 -9
  6. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/context_store.py +42 -0
  7. {devloop-0.2.0 → devloop-0.2.2}/LICENSE +0 -0
  8. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/__init__.py +0 -0
  9. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/agent_health_monitor.py +0 -0
  10. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/ci_monitor.py +0 -0
  11. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/code_rabbit.py +0 -0
  12. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/doc_lifecycle.py +0 -0
  13. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/echo.py +0 -0
  14. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/file_logger.py +0 -0
  15. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/formatter.py +0 -0
  16. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/git_commit_assistant.py +0 -0
  17. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/linter.py +0 -0
  18. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/performance_profiler.py +0 -0
  19. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/security_scanner.py +0 -0
  20. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/snyk.py +0 -0
  21. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/test_runner.py +0 -0
  22. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/type_checker.py +0 -0
  23. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/__init__.py +0 -0
  24. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/__init__.py +0 -0
  25. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/custom_agents.py +0 -0
  26. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/feedback.py +0 -0
  27. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/summary.py +0 -0
  28. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/main_v1.py +0 -0
  29. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/__init__.py +0 -0
  30. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/base.py +0 -0
  31. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/filesystem.py +0 -0
  32. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/git.py +0 -0
  33. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/manager.py +0 -0
  34. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/process.py +0 -0
  35. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/system.py +0 -0
  36. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/__init__.py +0 -0
  37. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/agent.py +0 -0
  38. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/agent_template.py +0 -0
  39. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/amp_integration.py +0 -0
  40. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/auto_fix.py +0 -0
  41. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/config.py +0 -0
  42. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/context.py +0 -0
  43. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/contextual_feedback.py +0 -0
  44. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/custom_agent.py +0 -0
  45. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/debug_trace.py +0 -0
  46. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/event.py +0 -0
  47. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/event_store.py +0 -0
  48. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/feedback.py +0 -0
  49. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/learning.py +0 -0
  50. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/manager.py +0 -0
  51. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/performance.py +0 -0
  52. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/proactive_feedback.py +0 -0
  53. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/summary_formatter.py +0 -0
  54. {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/summary_generator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devloop
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Intelligent background agents for development workflow automation
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -38,12 +38,32 @@ Description-Content-Type: text/markdown
38
38
 
39
39
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
40
40
  [![Tests Passing](https://img.shields.io/badge/tests-167%20passing-green.svg)](#testing)
41
- [![Production Ready](https://img.shields.io/badge/status-production%20ready-brightgreen.svg)](#status)
41
+ [![Alpha Release](https://img.shields.io/badge/status-alpha-orange.svg)](#status)
42
42
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
43
43
 
44
+ ## ⚠️ ALPHA SOFTWARE - NOT FOR PRODUCTION
45
+
46
+ **DevLoop is currently in active development and is not recommended for production use.**
47
+
48
+ This is **research-quality software**. Use at your own risk. See [Known Limitations & Risks](./history/RISK_ASSESSMENT.md) for details on:
49
+
50
+ - ✗ Subprocess execution not sandboxed (security risk)
51
+ - ✗ Auto-fix may corrupt code (enable only if willing to review changes)
52
+ - ✗ Race conditions possible in file operations (concurrent agent modifications)
53
+ - ✗ Limited error recovery (daemon may not restart automatically)
54
+ - ✗ Configuration migrations not yet supported
55
+ - ✗ No process supervision (manual daemon management)
56
+
57
+ **Suitable for:** Development on side projects, testing automation, research
58
+ **Not suitable for:** Critical code, production systems, untrusted projects
59
+
60
+ [View complete risk assessment →](./history/RISK_ASSESSMENT.md)
61
+
62
+ ---
63
+
44
64
  ## Status
45
65
 
46
- **PRODUCTION READY** — Full-featured development automation system. [View detailed implementation status →](./IMPLEMENTATION_STATUS.md)
66
+ 🔬 **ALPHA** — Full-featured development automation system in active development. [View detailed implementation status →](./IMPLEMENTATION_STATUS.md)
47
67
 
48
68
  ---
49
69
 
@@ -68,9 +88,26 @@ All agents run **non-intrusively in the background**, respecting your workflow.
68
88
 
69
89
  ## Quick Start
70
90
 
91
+ ### ⚠️ Before You Start
92
+
93
+ **ALPHA SOFTWARE DISCLAIMER:**
94
+ - This is research-quality code. Data loss is possible.
95
+ - Only use on projects you can afford to lose or easily recover.
96
+ - Make sure to commit your code to git before enabling DevLoop.
97
+ - Do not enable auto-fix on important code.
98
+ - Some agents may fail silently (see logs for details).
99
+
71
100
  ### Installation
72
101
 
73
- **Prerequisites:** Python 3.11+, Poetry
102
+ **Prerequisites:** Python 3.11+
103
+
104
+ #### Option 1: From PyPI (Recommended)
105
+
106
+ ```bash
107
+ pip install devloop
108
+ ```
109
+
110
+ #### Option 2: From Source
74
111
 
75
112
  ```bash
76
113
  # Clone the repository
@@ -316,7 +353,7 @@ Configure agent behavior in `.devloop/agents.json`:
316
353
  {
317
354
  "global": {
318
355
  "autonomousFixes": {
319
- "enabled": true,
356
+ "enabled": false,
320
357
  "safetyLevel": "safe_only"
321
358
  },
322
359
  "maxConcurrentAgents": 5,
@@ -338,11 +375,13 @@ Configure agent behavior in `.devloop/agents.json`:
338
375
  }
339
376
  ```
340
377
 
341
- **Safety levels:**
342
- - `safe_only` — Only fix whitespace/indentation
378
+ **Safety levels (Auto-fix):**
379
+ - `safe_only` — Only fix whitespace/indentation (default, recommended)
343
380
  - `medium_risk` — Include import/formatting fixes
344
381
  - `all` — Apply all fixes (use with caution)
345
382
 
383
+ ⚠️ **Auto-fix Warning:** Currently auto-fixes run without backups or review. **DO NOT enable auto-fix in production** or on critical code. Track [secure auto-fix with backups issue](https://github.com/wioota/devloop/issues/emc).
384
+
346
385
  [Full configuration reference →](./docs/configuration.md)
347
386
 
348
387
  ---
@@ -552,17 +591,26 @@ DevLoop follows these core principles:
552
591
 
553
592
  ## Troubleshooting
554
593
 
594
+ ### ⚠️ If Something Goes Wrong
595
+
596
+ **Recovery steps:**
597
+ 1. Stop the daemon: `devloop stop .`
598
+ 2. Check the logs: `tail -100 .devloop/devloop.log`
599
+ 3. Verify your code in git: `git status`
600
+ 4. Recover from git if files were modified: `git checkout <file>`
601
+ 5. Report the issue: [GitHub Issues](https://github.com/wioota/devloop/issues)
602
+
555
603
  ### Agents not running
556
604
 
557
605
  ```bash
558
606
  # Check status
559
607
  devloop status
560
608
 
561
- # View logs
562
- tail -f .devloop/agent.log
609
+ # View logs (useful for debugging)
610
+ tail -f .devloop/devloop.log
563
611
 
564
- # Enable verbose mode
565
- devloop watch . --verbose
612
+ # Enable verbose mode for more details
613
+ devloop watch . --foreground --verbose
566
614
  ```
567
615
 
568
616
  ### Performance issues
@@ -591,6 +639,13 @@ devloop custom-list
591
639
  ls -la .devloop/custom_agents/
592
640
  ```
593
641
 
642
+ ### Agent modified my files unexpectedly
643
+
644
+ 1. Check git diff: `git diff`
645
+ 2. Revert changes: `git checkout -- .`
646
+ 3. Disable the problematic agent in `.devloop/agents.json`
647
+ 4. Report issue with: `git show HEAD:.devloop/agents.json`
648
+
594
649
  [Full troubleshooting guide →](./docs/troubleshooting.md)
595
650
 
596
651
  ---
@@ -4,12 +4,32 @@
4
4
 
5
5
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
6
6
  [![Tests Passing](https://img.shields.io/badge/tests-167%20passing-green.svg)](#testing)
7
- [![Production Ready](https://img.shields.io/badge/status-production%20ready-brightgreen.svg)](#status)
7
+ [![Alpha Release](https://img.shields.io/badge/status-alpha-orange.svg)](#status)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
9
 
10
+ ## ⚠️ ALPHA SOFTWARE - NOT FOR PRODUCTION
11
+
12
+ **DevLoop is currently in active development and is not recommended for production use.**
13
+
14
+ This is **research-quality software**. Use at your own risk. See [Known Limitations & Risks](./history/RISK_ASSESSMENT.md) for details on:
15
+
16
+ - ✗ Subprocess execution not sandboxed (security risk)
17
+ - ✗ Auto-fix may corrupt code (enable only if willing to review changes)
18
+ - ✗ Race conditions possible in file operations (concurrent agent modifications)
19
+ - ✗ Limited error recovery (daemon may not restart automatically)
20
+ - ✗ Configuration migrations not yet supported
21
+ - ✗ No process supervision (manual daemon management)
22
+
23
+ **Suitable for:** Development on side projects, testing automation, research
24
+ **Not suitable for:** Critical code, production systems, untrusted projects
25
+
26
+ [View complete risk assessment →](./history/RISK_ASSESSMENT.md)
27
+
28
+ ---
29
+
10
30
  ## Status
11
31
 
12
- **PRODUCTION READY** — Full-featured development automation system. [View detailed implementation status →](./IMPLEMENTATION_STATUS.md)
32
+ 🔬 **ALPHA** — Full-featured development automation system in active development. [View detailed implementation status →](./IMPLEMENTATION_STATUS.md)
13
33
 
14
34
  ---
15
35
 
@@ -34,9 +54,26 @@ All agents run **non-intrusively in the background**, respecting your workflow.
34
54
 
35
55
  ## Quick Start
36
56
 
57
+ ### ⚠️ Before You Start
58
+
59
+ **ALPHA SOFTWARE DISCLAIMER:**
60
+ - This is research-quality code. Data loss is possible.
61
+ - Only use on projects you can afford to lose or easily recover.
62
+ - Make sure to commit your code to git before enabling DevLoop.
63
+ - Do not enable auto-fix on important code.
64
+ - Some agents may fail silently (see logs for details).
65
+
37
66
  ### Installation
38
67
 
39
- **Prerequisites:** Python 3.11+, Poetry
68
+ **Prerequisites:** Python 3.11+
69
+
70
+ #### Option 1: From PyPI (Recommended)
71
+
72
+ ```bash
73
+ pip install devloop
74
+ ```
75
+
76
+ #### Option 2: From Source
40
77
 
41
78
  ```bash
42
79
  # Clone the repository
@@ -282,7 +319,7 @@ Configure agent behavior in `.devloop/agents.json`:
282
319
  {
283
320
  "global": {
284
321
  "autonomousFixes": {
285
- "enabled": true,
322
+ "enabled": false,
286
323
  "safetyLevel": "safe_only"
287
324
  },
288
325
  "maxConcurrentAgents": 5,
@@ -304,11 +341,13 @@ Configure agent behavior in `.devloop/agents.json`:
304
341
  }
305
342
  ```
306
343
 
307
- **Safety levels:**
308
- - `safe_only` — Only fix whitespace/indentation
344
+ **Safety levels (Auto-fix):**
345
+ - `safe_only` — Only fix whitespace/indentation (default, recommended)
309
346
  - `medium_risk` — Include import/formatting fixes
310
347
  - `all` — Apply all fixes (use with caution)
311
348
 
349
+ ⚠️ **Auto-fix Warning:** Currently auto-fixes run without backups or review. **DO NOT enable auto-fix in production** or on critical code. Track [secure auto-fix with backups issue](https://github.com/wioota/devloop/issues/emc).
350
+
312
351
  [Full configuration reference →](./docs/configuration.md)
313
352
 
314
353
  ---
@@ -518,17 +557,26 @@ DevLoop follows these core principles:
518
557
 
519
558
  ## Troubleshooting
520
559
 
560
+ ### ⚠️ If Something Goes Wrong
561
+
562
+ **Recovery steps:**
563
+ 1. Stop the daemon: `devloop stop .`
564
+ 2. Check the logs: `tail -100 .devloop/devloop.log`
565
+ 3. Verify your code in git: `git status`
566
+ 4. Recover from git if files were modified: `git checkout <file>`
567
+ 5. Report the issue: [GitHub Issues](https://github.com/wioota/devloop/issues)
568
+
521
569
  ### Agents not running
522
570
 
523
571
  ```bash
524
572
  # Check status
525
573
  devloop status
526
574
 
527
- # View logs
528
- tail -f .devloop/agent.log
575
+ # View logs (useful for debugging)
576
+ tail -f .devloop/devloop.log
529
577
 
530
- # Enable verbose mode
531
- devloop watch . --verbose
578
+ # Enable verbose mode for more details
579
+ devloop watch . --foreground --verbose
532
580
  ```
533
581
 
534
582
  ### Performance issues
@@ -557,6 +605,13 @@ devloop custom-list
557
605
  ls -la .devloop/custom_agents/
558
606
  ```
559
607
 
608
+ ### Agent modified my files unexpectedly
609
+
610
+ 1. Check git diff: `git diff`
611
+ 2. Revert changes: `git checkout -- .`
612
+ 3. Disable the problematic agent in `.devloop/agents.json`
613
+ 4. Report issue with: `git show HEAD:.devloop/agents.json`
614
+
560
615
  [Full troubleshooting guide →](./docs/troubleshooting.md)
561
616
 
562
617
  ---
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "devloop"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "Intelligent background agents for development workflow automation"
5
5
  authors = ["DevLoop Contributors <devloop@example.com>"]
6
6
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """DevLoop - Background agents for development workflow automation."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
@@ -98,10 +98,44 @@ def setup_logging(verbose: bool = False):
98
98
  )
99
99
 
100
100
 
101
+ def setup_logging_with_rotation(verbose: bool = False, project_dir: Path | None = None):
102
+ """Setup logging configuration with file rotation for daemon mode."""
103
+ from logging.handlers import RotatingFileHandler
104
+
105
+ if project_dir is None:
106
+ project_dir = Path.cwd()
107
+
108
+ level = logging.DEBUG if verbose else logging.INFO
109
+ log_file = project_dir / ".devloop" / "devloop.log"
110
+ log_file.parent.mkdir(parents=True, exist_ok=True)
111
+
112
+ # Create rotating file handler
113
+ # maxBytes: 10MB per file
114
+ # backupCount: keep 3 rotated files (total ~40MB max)
115
+ rotating_handler = RotatingFileHandler(
116
+ filename=str(log_file),
117
+ maxBytes=10 * 1024 * 1024, # 10MB
118
+ backupCount=3,
119
+ )
120
+
121
+ # Format: timestamp | level | logger | message
122
+ formatter = logging.Formatter(
123
+ "%(asctime)s | %(levelname)-8s | %(name)-20s | %(message)s",
124
+ datefmt="%Y-%m-%d %H:%M:%S",
125
+ )
126
+ rotating_handler.setFormatter(formatter)
127
+
128
+ # Configure root logger
129
+ root_logger = logging.getLogger()
130
+ root_logger.setLevel(level)
131
+ root_logger.addHandler(rotating_handler)
132
+
133
+
101
134
  def run_daemon(path: Path, config_path: Path | None, verbose: bool):
102
135
  """Run devloop in daemon/background mode."""
103
136
  import os
104
137
  import sys
138
+ from logging.handlers import RotatingFileHandler
105
139
 
106
140
  # Fork to background
107
141
  try:
@@ -125,16 +159,11 @@ def run_daemon(path: Path, config_path: Path | None, verbose: bool):
125
159
  os.setsid()
126
160
  os.umask(0)
127
161
 
128
- # Redirect stdout/stderr to log file
129
- log_file = project_dir / ".devloop" / "devloop.log"
130
- log_file.parent.mkdir(parents=True, exist_ok=True)
162
+ # Setup logging with rotation BEFORE redirecting file descriptors
163
+ setup_logging_with_rotation(verbose, project_dir)
131
164
 
132
- with open(log_file, "a") as f:
133
- os.dup2(f.fileno(), sys.stdout.fileno())
134
- os.dup2(f.fileno(), sys.stderr.fileno())
135
-
136
- # Setup logging for daemon
137
- setup_logging(verbose)
165
+ # Don't redirect stdout/stderr - let logging handlers manage it
166
+ # This prevents unbounded log file growth
138
167
 
139
168
  # Write PID file
140
169
  pid_file = project_dir / ".devloop" / "devloop.pid"
@@ -200,6 +229,42 @@ def watch(
200
229
  console.print("\n[yellow]Shutting down...[/yellow]")
201
230
 
202
231
 
232
+ async def _cleanup_old_data(context_store, event_store, interval_hours: int = 1):
233
+ """Periodically clean up old findings and events to prevent disk fill-up.
234
+
235
+ Args:
236
+ context_store: Context store instance
237
+ event_store: Event store instance
238
+ interval_hours: How often to run cleanup (default: 1 hour)
239
+ """
240
+ cleanup_interval = interval_hours * 60 * 60 # Convert to seconds
241
+ context_retention_hours = 7 * 24 # Keep 7 days of findings
242
+ event_retention_days = 30 # Keep 30 days of events
243
+
244
+ while True:
245
+ try:
246
+ await asyncio.sleep(cleanup_interval)
247
+
248
+ # Clean up old context findings
249
+ findings_removed = await context_store.cleanup_old_findings(
250
+ hours_to_keep=context_retention_hours
251
+ )
252
+
253
+ # Clean up old events
254
+ events_removed = await event_store.cleanup_old_events(
255
+ days_to_keep=event_retention_days
256
+ )
257
+
258
+ console.print(
259
+ f"[dim]Cleanup: removed {findings_removed} old findings, {events_removed} old events[/dim]"
260
+ )
261
+
262
+ except asyncio.CancelledError:
263
+ break
264
+ except Exception as e:
265
+ console.print(f"[yellow]Cleanup error: {e}[/yellow]")
266
+
267
+
203
268
  async def watch_async(path: Path, config_path: Path | None):
204
269
  """Async watch implementation."""
205
270
  # Load configuration
@@ -232,6 +297,9 @@ async def watch_async(path: Path, config_path: Path | None):
232
297
  fs_config = {"watch_paths": [str(path)]}
233
298
  fs_collector = FileSystemCollector(event_bus=event_bus, config=fs_config)
234
299
 
300
+ # Start cleanup task (run every hour to remove old findings/events)
301
+ cleanup_task = asyncio.create_task(_cleanup_old_data(context_store, event_store))
302
+
235
303
  # Create and register agents based on configuration
236
304
  if config.is_agent_enabled("linter"):
237
305
  linter_config = config.get_agent_config("linter") or {}
@@ -324,8 +392,15 @@ async def watch_async(path: Path, config_path: Path | None):
324
392
  await shutdown_event.wait()
325
393
 
326
394
  # Stop everything
395
+ cleanup_task.cancel()
327
396
  await agent_manager.stop_all()
328
397
  await fs_collector.stop()
398
+
399
+ # Wait for cleanup task to finish
400
+ try:
401
+ await cleanup_task
402
+ except asyncio.CancelledError:
403
+ pass
329
404
 
330
405
 
331
406
  @app.command()
@@ -525,6 +525,48 @@ class ContextStore:
525
525
  logger.error(f"Failed to load {tier_file}: {e}")
526
526
  # Continue with other tiers
527
527
 
528
+ async def cleanup_old_findings(self, hours_to_keep: int = 168) -> int:
529
+ """
530
+ Clean up findings older than specified hours.
531
+
532
+ Args:
533
+ hours_to_keep: Number of hours to retain findings (default: 7 days = 168 hours)
534
+
535
+ Returns:
536
+ Number of findings removed
537
+ """
538
+ from datetime import datetime, UTC, timedelta
539
+
540
+ cutoff_time = datetime.now(UTC) - timedelta(hours=hours_to_keep)
541
+ cutoff_iso = cutoff_time.isoformat()
542
+
543
+ count = 0
544
+ async with self._lock:
545
+ for tier in Tier:
546
+ original_count = len(self._findings[tier])
547
+
548
+ # Filter out old findings
549
+ self._findings[tier] = [
550
+ f
551
+ for f in self._findings[tier]
552
+ if f.timestamp > cutoff_iso # ISO strings compare correctly
553
+ ]
554
+
555
+ count += original_count - len(self._findings[tier])
556
+
557
+ # Write cleaned tier to disk
558
+ await self._write_tier(tier)
559
+
560
+ # Update index after cleanup
561
+ await self._update_index()
562
+
563
+ if count > 0:
564
+ logger.info(
565
+ f"Cleaned up {count} findings older than {hours_to_keep} hours"
566
+ )
567
+
568
+ return count
569
+
528
570
 
529
571
  # Global instance
530
572
  context_store = ContextStore()
File without changes