devloop 0.2.1__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.1 → devloop-0.2.2}/PKG-INFO +57 -10
  2. {devloop-0.2.1 → devloop-0.2.2}/README.md +56 -9
  3. {devloop-0.2.1 → devloop-0.2.2}/pyproject.toml +1 -1
  4. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/__init__.py +1 -1
  5. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/cli/main.py +84 -9
  6. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/context_store.py +42 -0
  7. {devloop-0.2.1 → devloop-0.2.2}/LICENSE +0 -0
  8. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/__init__.py +0 -0
  9. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/agent_health_monitor.py +0 -0
  10. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/ci_monitor.py +0 -0
  11. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/code_rabbit.py +0 -0
  12. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/doc_lifecycle.py +0 -0
  13. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/echo.py +0 -0
  14. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/file_logger.py +0 -0
  15. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/formatter.py +0 -0
  16. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/git_commit_assistant.py +0 -0
  17. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/linter.py +0 -0
  18. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/performance_profiler.py +0 -0
  19. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/security_scanner.py +0 -0
  20. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/snyk.py +0 -0
  21. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/test_runner.py +0 -0
  22. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/agents/type_checker.py +0 -0
  23. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/cli/__init__.py +0 -0
  24. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/cli/commands/__init__.py +0 -0
  25. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/cli/commands/custom_agents.py +0 -0
  26. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/cli/commands/feedback.py +0 -0
  27. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/cli/commands/summary.py +0 -0
  28. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/cli/main_v1.py +0 -0
  29. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/collectors/__init__.py +0 -0
  30. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/collectors/base.py +0 -0
  31. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/collectors/filesystem.py +0 -0
  32. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/collectors/git.py +0 -0
  33. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/collectors/manager.py +0 -0
  34. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/collectors/process.py +0 -0
  35. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/collectors/system.py +0 -0
  36. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/__init__.py +0 -0
  37. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/agent.py +0 -0
  38. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/agent_template.py +0 -0
  39. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/amp_integration.py +0 -0
  40. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/auto_fix.py +0 -0
  41. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/config.py +0 -0
  42. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/context.py +0 -0
  43. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/contextual_feedback.py +0 -0
  44. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/custom_agent.py +0 -0
  45. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/debug_trace.py +0 -0
  46. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/event.py +0 -0
  47. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/event_store.py +0 -0
  48. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/feedback.py +0 -0
  49. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/learning.py +0 -0
  50. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/manager.py +0 -0
  51. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/performance.py +0 -0
  52. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/proactive_feedback.py +0 -0
  53. {devloop-0.2.1 → devloop-0.2.2}/src/devloop/core/summary_formatter.py +0 -0
  54. {devloop-0.2.1 → 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.1
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,6 +88,15 @@ 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
102
  **Prerequisites:** Python 3.11+
@@ -324,7 +353,7 @@ Configure agent behavior in `.devloop/agents.json`:
324
353
  {
325
354
  "global": {
326
355
  "autonomousFixes": {
327
- "enabled": true,
356
+ "enabled": false,
328
357
  "safetyLevel": "safe_only"
329
358
  },
330
359
  "maxConcurrentAgents": 5,
@@ -346,11 +375,13 @@ Configure agent behavior in `.devloop/agents.json`:
346
375
  }
347
376
  ```
348
377
 
349
- **Safety levels:**
350
- - `safe_only` — Only fix whitespace/indentation
378
+ **Safety levels (Auto-fix):**
379
+ - `safe_only` — Only fix whitespace/indentation (default, recommended)
351
380
  - `medium_risk` — Include import/formatting fixes
352
381
  - `all` — Apply all fixes (use with caution)
353
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
+
354
385
  [Full configuration reference →](./docs/configuration.md)
355
386
 
356
387
  ---
@@ -560,17 +591,26 @@ DevLoop follows these core principles:
560
591
 
561
592
  ## Troubleshooting
562
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
+
563
603
  ### Agents not running
564
604
 
565
605
  ```bash
566
606
  # Check status
567
607
  devloop status
568
608
 
569
- # View logs
570
- tail -f .devloop/agent.log
609
+ # View logs (useful for debugging)
610
+ tail -f .devloop/devloop.log
571
611
 
572
- # Enable verbose mode
573
- devloop watch . --verbose
612
+ # Enable verbose mode for more details
613
+ devloop watch . --foreground --verbose
574
614
  ```
575
615
 
576
616
  ### Performance issues
@@ -599,6 +639,13 @@ devloop custom-list
599
639
  ls -la .devloop/custom_agents/
600
640
  ```
601
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
+
602
649
  [Full troubleshooting guide →](./docs/troubleshooting.md)
603
650
 
604
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,6 +54,15 @@ 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
68
  **Prerequisites:** Python 3.11+
@@ -290,7 +319,7 @@ Configure agent behavior in `.devloop/agents.json`:
290
319
  {
291
320
  "global": {
292
321
  "autonomousFixes": {
293
- "enabled": true,
322
+ "enabled": false,
294
323
  "safetyLevel": "safe_only"
295
324
  },
296
325
  "maxConcurrentAgents": 5,
@@ -312,11 +341,13 @@ Configure agent behavior in `.devloop/agents.json`:
312
341
  }
313
342
  ```
314
343
 
315
- **Safety levels:**
316
- - `safe_only` — Only fix whitespace/indentation
344
+ **Safety levels (Auto-fix):**
345
+ - `safe_only` — Only fix whitespace/indentation (default, recommended)
317
346
  - `medium_risk` — Include import/formatting fixes
318
347
  - `all` — Apply all fixes (use with caution)
319
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
+
320
351
  [Full configuration reference →](./docs/configuration.md)
321
352
 
322
353
  ---
@@ -526,17 +557,26 @@ DevLoop follows these core principles:
526
557
 
527
558
  ## Troubleshooting
528
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
+
529
569
  ### Agents not running
530
570
 
531
571
  ```bash
532
572
  # Check status
533
573
  devloop status
534
574
 
535
- # View logs
536
- tail -f .devloop/agent.log
575
+ # View logs (useful for debugging)
576
+ tail -f .devloop/devloop.log
537
577
 
538
- # Enable verbose mode
539
- devloop watch . --verbose
578
+ # Enable verbose mode for more details
579
+ devloop watch . --foreground --verbose
540
580
  ```
541
581
 
542
582
  ### Performance issues
@@ -565,6 +605,13 @@ devloop custom-list
565
605
  ls -la .devloop/custom_agents/
566
606
  ```
567
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
+
568
615
  [Full troubleshooting guide →](./docs/troubleshooting.md)
569
616
 
570
617
  ---
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "devloop"
3
- version = "0.2.1"
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