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.
- {devloop-0.2.0 → devloop-0.2.2}/PKG-INFO +66 -11
- {devloop-0.2.0 → devloop-0.2.2}/README.md +65 -10
- {devloop-0.2.0 → devloop-0.2.2}/pyproject.toml +1 -1
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/__init__.py +1 -1
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/main.py +84 -9
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/context_store.py +42 -0
- {devloop-0.2.0 → devloop-0.2.2}/LICENSE +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/__init__.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/agent_health_monitor.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/ci_monitor.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/code_rabbit.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/doc_lifecycle.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/echo.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/file_logger.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/formatter.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/git_commit_assistant.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/linter.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/performance_profiler.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/security_scanner.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/snyk.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/test_runner.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/agents/type_checker.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/__init__.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/__init__.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/custom_agents.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/feedback.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/commands/summary.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/cli/main_v1.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/__init__.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/base.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/filesystem.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/git.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/manager.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/process.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/collectors/system.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/__init__.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/agent.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/agent_template.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/amp_integration.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/auto_fix.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/config.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/context.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/contextual_feedback.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/custom_agent.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/debug_trace.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/event.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/event_store.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/feedback.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/learning.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/manager.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/performance.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/proactive_feedback.py +0 -0
- {devloop-0.2.0 → devloop-0.2.2}/src/devloop/core/summary_formatter.py +0 -0
- {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.
|
|
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
|
[](https://www.python.org/downloads/)
|
|
40
40
|
[](#testing)
|
|
41
|
-
[](#status)
|
|
42
42
|
[](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
|
-
|
|
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
|
|
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":
|
|
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/
|
|
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
|
[](https://www.python.org/downloads/)
|
|
6
6
|
[](#testing)
|
|
7
|
-
[](#status)
|
|
8
8
|
[](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
|
-
|
|
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
|
|
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":
|
|
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/
|
|
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
|
---
|
|
@@ -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
|
-
#
|
|
129
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|