devloop 0.4.1__tar.gz → 0.5.0__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.4.1 → devloop-0.5.0}/PKG-INFO +61 -5
- {devloop-0.4.1 → devloop-0.5.0}/README.md +58 -4
- {devloop-0.4.1 → devloop-0.5.0}/pyproject.toml +10 -2
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/ci_monitor.py +71 -97
- devloop-0.5.0/src/devloop/cli/commands/release.py +213 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/main.py +214 -30
- devloop-0.5.0/src/devloop/cli/pre_push_check.py +66 -0
- devloop-0.5.0/src/devloop/cli/templates/devloop_agents_template.md +317 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/git_hooks/pre-push +34 -11
- devloop-0.5.0/src/devloop/cli/templates/supervisor/devloop.conf +25 -0
- devloop-0.5.0/src/devloop/cli/templates/systemd/devloop.service +35 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/collectors/filesystem.py +44 -1
- devloop-0.5.0/src/devloop/core/__init__.py +90 -0
- devloop-0.5.0/src/devloop/core/action_logger.py +265 -0
- devloop-0.5.0/src/devloop/core/amp_thread_mapper.py +589 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/config.py +82 -7
- devloop-0.5.0/src/devloop/core/config_schema.py +400 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/context_store.py +41 -3
- devloop-0.5.0/src/devloop/core/daemon_health.py +159 -0
- devloop-0.5.0/src/devloop/core/pattern_analyzer.py +411 -0
- devloop-0.5.0/src/devloop/core/pattern_detector.py +285 -0
- devloop-0.5.0/src/devloop/core/transactional_io.py +486 -0
- devloop-0.5.0/src/devloop/integrations/__init__.py +16 -0
- devloop-0.5.0/src/devloop/integrations/beads_integration.py +324 -0
- devloop-0.5.0/src/devloop/lsp/__init__.py +5 -0
- devloop-0.5.0/src/devloop/lsp/__main__.py +6 -0
- devloop-0.5.0/src/devloop/lsp/mapper.py +130 -0
- devloop-0.5.0/src/devloop/lsp/server.py +366 -0
- devloop-0.5.0/src/devloop/providers/__init__.py +37 -0
- devloop-0.5.0/src/devloop/providers/ci_provider.py +158 -0
- devloop-0.5.0/src/devloop/providers/circleci_provider.py +330 -0
- devloop-0.5.0/src/devloop/providers/github_actions_provider.py +232 -0
- devloop-0.5.0/src/devloop/providers/gitlab_ci_provider.py +248 -0
- devloop-0.5.0/src/devloop/providers/jenkins_provider.py +288 -0
- devloop-0.5.0/src/devloop/providers/provider_manager.py +190 -0
- devloop-0.5.0/src/devloop/providers/pypi_registry.py +192 -0
- devloop-0.5.0/src/devloop/providers/registry_provider.py +117 -0
- devloop-0.5.0/src/devloop/release/__init__.py +5 -0
- devloop-0.5.0/src/devloop/release/release_manager.py +454 -0
- devloop-0.5.0/src/devloop/security/__init__.py +49 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/bubblewrap_sandbox.py +24 -2
- devloop-0.5.0/src/devloop/security/path_validator.py +352 -0
- devloop-0.5.0/src/devloop/security/token_manager.py +419 -0
- devloop-0.4.1/src/devloop/core/__init__.py +0 -21
- devloop-0.4.1/src/devloop/security/__init__.py +0 -15
- {devloop-0.4.1 → devloop-0.5.0}/LICENSE +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/__init__.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/__init__.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/agent_health_monitor.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/code_rabbit.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/doc_lifecycle.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/echo.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/file_logger.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/formatter.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/git_commit_assistant.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/linter.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/performance_profiler.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/sandbox_helper.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/security_scanner.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/snyk.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/test_runner.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/agents/type_checker.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/__init__.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/commands/__init__.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/commands/audit.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/commands/custom_agents.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/commands/feedback.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/commands/summary.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/commands/telemetry.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/main_v1.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/prerequisites.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/pyodide_installer.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/claude_commands/README.md +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/claude_commands/agent-summary.md +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/claude_commands/devloop-findings.md +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/claude_commands/devloop-status.md +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/claude_commands/extract-findings.md +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/claude_commands/verify-work.md +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/git_hooks/pre-commit +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/cli/templates/git_hooks/pre-commit-checks +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/collectors/__init__.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/collectors/base.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/collectors/git.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/collectors/manager.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/collectors/process.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/collectors/system.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/agent.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/agent_audit_logger.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/agent_template.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/amp_integration.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/auto_fix.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/backup_manager.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/claude_adapter.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/context.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/contextual_feedback.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/custom_agent.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/debug_trace.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/event.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/event_store.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/feedback.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/file_lock_manager.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/learning.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/manager.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/operational_health.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/performance.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/proactive_feedback.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/summary_formatter.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/summary_generator.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/core/telemetry.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/audit_logger.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/cgroups_helper.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/factory.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/no_sandbox.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/package.json +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/pyodide_runner.js +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/pyodide_sandbox.py +0 -0
- {devloop-0.4.1 → devloop-0.5.0}/src/devloop/security/sandbox.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devloop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Intelligent background agents for development workflow automation
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -26,8 +26,10 @@ Provides-Extra: ci-monitor
|
|
|
26
26
|
Provides-Extra: code-rabbit
|
|
27
27
|
Provides-Extra: snyk
|
|
28
28
|
Requires-Dist: aiofiles (>=23.2,<24.0)
|
|
29
|
+
Requires-Dist: lsprotocol (>=2023.0.0,<2024.0.0)
|
|
29
30
|
Requires-Dist: psutil (>=5.9,<6.0)
|
|
30
31
|
Requires-Dist: pydantic (>=2.5,<3.0)
|
|
32
|
+
Requires-Dist: pygls (>=1.3.0,<2.0.0)
|
|
31
33
|
Requires-Dist: rich (>=13.7,<14.0)
|
|
32
34
|
Requires-Dist: typer (>=0.15,<1.0)
|
|
33
35
|
Requires-Dist: watchdog (>=3.0,<4.0)
|
|
@@ -214,7 +216,9 @@ All agents run **non-intrusively in the background**, respecting your workflow.
|
|
|
214
216
|
|
|
215
217
|
### Installation
|
|
216
218
|
|
|
217
|
-
**Prerequisites:**
|
|
219
|
+
**Prerequisites:**
|
|
220
|
+
- Python 3.11 or later
|
|
221
|
+
- For release workflow: Poetry 1.7+ and GitHub CLI 2.78+
|
|
218
222
|
|
|
219
223
|
#### Option 1: From PyPI (Recommended)
|
|
220
224
|
|
|
@@ -250,13 +254,27 @@ DevLoop automatically detects and uses several system tools. Install them for fu
|
|
|
250
254
|
|
|
251
255
|
**For Pre-Push CI Verification (Optional but Recommended):**
|
|
252
256
|
```bash
|
|
253
|
-
# GitHub CLI (for checking CI status before push)
|
|
257
|
+
# GitHub CLI 2.78+ (for checking CI status before push)
|
|
254
258
|
# Ubuntu/Debian:
|
|
255
259
|
sudo apt-get install -y gh
|
|
256
260
|
|
|
257
261
|
# macOS:
|
|
258
262
|
brew install gh
|
|
259
263
|
|
|
264
|
+
# Verify installation
|
|
265
|
+
gh --version
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**For Release Management (Optional but Recommended for Publishing):**
|
|
269
|
+
```bash
|
|
270
|
+
# Poetry 1.7+ (for package management and publishing)
|
|
271
|
+
curl -sSL https://install.python-poetry.org | python3 -
|
|
272
|
+
|
|
273
|
+
# Verify installation
|
|
274
|
+
poetry --version
|
|
275
|
+
|
|
276
|
+
# Configure PyPI credentials (get token from https://pypi.org/account/)
|
|
277
|
+
poetry config pypi-token.pypi "pypi-AgEIcHlwaS5vcmc..."
|
|
260
278
|
```
|
|
261
279
|
|
|
262
280
|
**For Task Management Integration (Optional):**
|
|
@@ -266,8 +284,9 @@ pip install beads-mcp
|
|
|
266
284
|
```
|
|
267
285
|
|
|
268
286
|
**What happens if missing:**
|
|
269
|
-
- `gh
|
|
270
|
-
- `
|
|
287
|
+
- `gh` (2.78+): Pre-push CI verification is skipped (but DevLoop still works)
|
|
288
|
+
- `poetry` (1.7+): Release workflow unavailable (but development still works)
|
|
289
|
+
- `bd`: Task creation on push won't work (but DevLoop still works)
|
|
271
290
|
|
|
272
291
|
DevLoop will warn you during `devloop init` if any tools are missing and provide installation instructions. You can install them later and they'll be detected automatically.
|
|
273
292
|
|
|
@@ -343,6 +362,43 @@ devloop custom-create my_agent pattern_matcher
|
|
|
343
362
|
|
|
344
363
|
[View all CLI commands →](./docs/cli-commands.md)
|
|
345
364
|
|
|
365
|
+
### Verify Installation & Version Compatibility
|
|
366
|
+
|
|
367
|
+
After installation, verify everything is working:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# Check DevLoop version
|
|
371
|
+
devloop --version
|
|
372
|
+
|
|
373
|
+
# Verify system dependencies are detected
|
|
374
|
+
devloop init --check-requirements
|
|
375
|
+
|
|
376
|
+
# Check daemon status (if running)
|
|
377
|
+
devloop status
|
|
378
|
+
|
|
379
|
+
# Verify git hooks are installed (in your project)
|
|
380
|
+
cat .git/hooks/pre-commit # Should exist
|
|
381
|
+
cat .git/hooks/pre-push # Should exist
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Version compatibility:**
|
|
385
|
+
- DevLoop 0.4.1+ requires Python 3.11+
|
|
386
|
+
- Release workflow requires Poetry 1.7+ and GitHub CLI 2.78+
|
|
387
|
+
- AGENTS.md template was updated in DevLoop 0.4.0+
|
|
388
|
+
|
|
389
|
+
**If you're upgrading DevLoop:**
|
|
390
|
+
```bash
|
|
391
|
+
# Upgrade to latest
|
|
392
|
+
pip install --upgrade devloop
|
|
393
|
+
|
|
394
|
+
# Update your project's AGENTS.md (templates may have changed)
|
|
395
|
+
devloop init --merge-templates /path/to/your/project
|
|
396
|
+
|
|
397
|
+
# Restart the daemon
|
|
398
|
+
devloop stop
|
|
399
|
+
devloop watch .
|
|
400
|
+
```
|
|
401
|
+
|
|
346
402
|
---
|
|
347
403
|
|
|
348
404
|
## Architecture
|
|
@@ -176,7 +176,9 @@ All agents run **non-intrusively in the background**, respecting your workflow.
|
|
|
176
176
|
|
|
177
177
|
### Installation
|
|
178
178
|
|
|
179
|
-
**Prerequisites:**
|
|
179
|
+
**Prerequisites:**
|
|
180
|
+
- Python 3.11 or later
|
|
181
|
+
- For release workflow: Poetry 1.7+ and GitHub CLI 2.78+
|
|
180
182
|
|
|
181
183
|
#### Option 1: From PyPI (Recommended)
|
|
182
184
|
|
|
@@ -212,13 +214,27 @@ DevLoop automatically detects and uses several system tools. Install them for fu
|
|
|
212
214
|
|
|
213
215
|
**For Pre-Push CI Verification (Optional but Recommended):**
|
|
214
216
|
```bash
|
|
215
|
-
# GitHub CLI (for checking CI status before push)
|
|
217
|
+
# GitHub CLI 2.78+ (for checking CI status before push)
|
|
216
218
|
# Ubuntu/Debian:
|
|
217
219
|
sudo apt-get install -y gh
|
|
218
220
|
|
|
219
221
|
# macOS:
|
|
220
222
|
brew install gh
|
|
221
223
|
|
|
224
|
+
# Verify installation
|
|
225
|
+
gh --version
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**For Release Management (Optional but Recommended for Publishing):**
|
|
229
|
+
```bash
|
|
230
|
+
# Poetry 1.7+ (for package management and publishing)
|
|
231
|
+
curl -sSL https://install.python-poetry.org | python3 -
|
|
232
|
+
|
|
233
|
+
# Verify installation
|
|
234
|
+
poetry --version
|
|
235
|
+
|
|
236
|
+
# Configure PyPI credentials (get token from https://pypi.org/account/)
|
|
237
|
+
poetry config pypi-token.pypi "pypi-AgEIcHlwaS5vcmc..."
|
|
222
238
|
```
|
|
223
239
|
|
|
224
240
|
**For Task Management Integration (Optional):**
|
|
@@ -228,8 +244,9 @@ pip install beads-mcp
|
|
|
228
244
|
```
|
|
229
245
|
|
|
230
246
|
**What happens if missing:**
|
|
231
|
-
- `gh
|
|
232
|
-
- `
|
|
247
|
+
- `gh` (2.78+): Pre-push CI verification is skipped (but DevLoop still works)
|
|
248
|
+
- `poetry` (1.7+): Release workflow unavailable (but development still works)
|
|
249
|
+
- `bd`: Task creation on push won't work (but DevLoop still works)
|
|
233
250
|
|
|
234
251
|
DevLoop will warn you during `devloop init` if any tools are missing and provide installation instructions. You can install them later and they'll be detected automatically.
|
|
235
252
|
|
|
@@ -305,6 +322,43 @@ devloop custom-create my_agent pattern_matcher
|
|
|
305
322
|
|
|
306
323
|
[View all CLI commands →](./docs/cli-commands.md)
|
|
307
324
|
|
|
325
|
+
### Verify Installation & Version Compatibility
|
|
326
|
+
|
|
327
|
+
After installation, verify everything is working:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
# Check DevLoop version
|
|
331
|
+
devloop --version
|
|
332
|
+
|
|
333
|
+
# Verify system dependencies are detected
|
|
334
|
+
devloop init --check-requirements
|
|
335
|
+
|
|
336
|
+
# Check daemon status (if running)
|
|
337
|
+
devloop status
|
|
338
|
+
|
|
339
|
+
# Verify git hooks are installed (in your project)
|
|
340
|
+
cat .git/hooks/pre-commit # Should exist
|
|
341
|
+
cat .git/hooks/pre-push # Should exist
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Version compatibility:**
|
|
345
|
+
- DevLoop 0.4.1+ requires Python 3.11+
|
|
346
|
+
- Release workflow requires Poetry 1.7+ and GitHub CLI 2.78+
|
|
347
|
+
- AGENTS.md template was updated in DevLoop 0.4.0+
|
|
348
|
+
|
|
349
|
+
**If you're upgrading DevLoop:**
|
|
350
|
+
```bash
|
|
351
|
+
# Upgrade to latest
|
|
352
|
+
pip install --upgrade devloop
|
|
353
|
+
|
|
354
|
+
# Update your project's AGENTS.md (templates may have changed)
|
|
355
|
+
devloop init --merge-templates /path/to/your/project
|
|
356
|
+
|
|
357
|
+
# Restart the daemon
|
|
358
|
+
devloop stop
|
|
359
|
+
devloop watch .
|
|
360
|
+
```
|
|
361
|
+
|
|
308
362
|
---
|
|
309
363
|
|
|
310
364
|
## Architecture
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "devloop"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
description = "Intelligent background agents for development workflow automation"
|
|
5
5
|
authors = ["DevLoop Contributors <devloop@example.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -47,6 +47,8 @@ typer = ">=0.15,<1.0"
|
|
|
47
47
|
rich = "^13.7"
|
|
48
48
|
aiofiles = "^23.2"
|
|
49
49
|
psutil = "^5.9"
|
|
50
|
+
pygls = "^1.3.0"
|
|
51
|
+
lsprotocol = "^2023.0.0"
|
|
50
52
|
|
|
51
53
|
[tool.poetry.group.dev.dependencies]
|
|
52
54
|
pytest = "^7.4"
|
|
@@ -77,12 +79,18 @@ target-version = ['py311']
|
|
|
77
79
|
|
|
78
80
|
[tool.ruff]
|
|
79
81
|
line-length = 88
|
|
80
|
-
target-version = "
|
|
82
|
+
target-version = "py311"
|
|
81
83
|
|
|
82
84
|
[tool.pytest.ini_options]
|
|
83
85
|
asyncio_mode = "auto"
|
|
84
86
|
testpaths = ["tests"]
|
|
85
87
|
pythonpath = ["src"]
|
|
88
|
+
markers = [
|
|
89
|
+
"integration: marks tests as integration tests",
|
|
90
|
+
"performance: marks tests as performance benchmarks",
|
|
91
|
+
"security: marks tests as security tests",
|
|
92
|
+
"benchmark: marks tests as benchmarks",
|
|
93
|
+
]
|
|
86
94
|
|
|
87
95
|
[tool.mypy]
|
|
88
96
|
python_version = "0.4.1"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
"""CI Monitor Agent - Monitors
|
|
1
|
+
"""CI Monitor Agent - Monitors CI system status (platform-agnostic)."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import subprocess
|
|
5
4
|
from datetime import datetime, timedelta
|
|
6
5
|
from typing import Any, Dict, List, Optional
|
|
@@ -8,10 +7,12 @@ from typing import Any, Dict, List, Optional
|
|
|
8
7
|
from devloop.core.agent import Agent, AgentResult
|
|
9
8
|
from devloop.core.context_store import Finding, Severity
|
|
10
9
|
from devloop.core.event import Event
|
|
10
|
+
from devloop.providers.ci_provider import CIProvider, RunConclusion, RunStatus
|
|
11
|
+
from devloop.providers.provider_manager import get_provider_manager
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class CIMonitorAgent(Agent):
|
|
14
|
-
"""Monitors
|
|
15
|
+
"""Monitors CI status and reports failures (provider-agnostic)."""
|
|
15
16
|
|
|
16
17
|
def __init__(
|
|
17
18
|
self,
|
|
@@ -19,7 +20,9 @@ class CIMonitorAgent(Agent):
|
|
|
19
20
|
triggers: List[str],
|
|
20
21
|
event_bus: Any,
|
|
21
22
|
check_interval: int = 300,
|
|
22
|
-
|
|
23
|
+
ci_provider: Optional[CIProvider] = None,
|
|
24
|
+
provider_name: str = "github",
|
|
25
|
+
):
|
|
23
26
|
"""
|
|
24
27
|
Initialize CI monitor agent.
|
|
25
28
|
|
|
@@ -28,14 +31,36 @@ class CIMonitorAgent(Agent):
|
|
|
28
31
|
triggers: Event triggers for this agent
|
|
29
32
|
event_bus: Event bus for publishing events
|
|
30
33
|
check_interval: How often to check CI status (in seconds)
|
|
34
|
+
ci_provider: Optional pre-configured CIProvider. If not provided, auto-detects.
|
|
35
|
+
provider_name: CI provider name (e.g., "github", "gitlab") if ci_provider not provided
|
|
31
36
|
"""
|
|
32
37
|
super().__init__(name, triggers, event_bus)
|
|
33
38
|
self.check_interval = check_interval
|
|
34
39
|
self.last_check: Optional[datetime] = None
|
|
35
40
|
self.last_status: Optional[dict] = None
|
|
36
41
|
|
|
42
|
+
# Use provided provider or auto-detect
|
|
43
|
+
if ci_provider:
|
|
44
|
+
self.provider: Optional[CIProvider] = ci_provider
|
|
45
|
+
else:
|
|
46
|
+
manager = get_provider_manager()
|
|
47
|
+
provider = manager.get_ci_provider(provider_name)
|
|
48
|
+
if provider:
|
|
49
|
+
self.provider = provider
|
|
50
|
+
else:
|
|
51
|
+
# Fall back to auto-detection
|
|
52
|
+
self.provider = manager.auto_detect_ci_provider()
|
|
53
|
+
|
|
37
54
|
async def handle(self, event: Event) -> AgentResult:
|
|
38
55
|
"""Handle events and check CI status."""
|
|
56
|
+
if not self.provider:
|
|
57
|
+
return AgentResult(
|
|
58
|
+
agent_name=self.name,
|
|
59
|
+
success=False,
|
|
60
|
+
duration=0,
|
|
61
|
+
error="No CI provider available",
|
|
62
|
+
)
|
|
63
|
+
|
|
39
64
|
# Check if it's time for periodic check
|
|
40
65
|
should_check = False
|
|
41
66
|
|
|
@@ -55,11 +80,29 @@ class CIMonitorAgent(Agent):
|
|
|
55
80
|
|
|
56
81
|
# Check CI status
|
|
57
82
|
try:
|
|
58
|
-
|
|
83
|
+
branch = self._get_current_branch()
|
|
84
|
+
if not branch:
|
|
85
|
+
return AgentResult(
|
|
86
|
+
agent_name=self.name,
|
|
87
|
+
success=False,
|
|
88
|
+
duration=0,
|
|
89
|
+
error="Could not determine current branch",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Check if provider is available
|
|
93
|
+
if not self.provider.is_available():
|
|
94
|
+
return AgentResult(
|
|
95
|
+
agent_name=self.name,
|
|
96
|
+
success=True,
|
|
97
|
+
duration=0,
|
|
98
|
+
message=f"CI provider '{self.provider.get_provider_name()}' not available",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Get latest runs
|
|
102
|
+
runs = self.provider.list_runs(branch, limit=5)
|
|
59
103
|
self.last_check = datetime.now()
|
|
60
|
-
self.last_status = status
|
|
61
104
|
|
|
62
|
-
if not
|
|
105
|
+
if not runs:
|
|
63
106
|
return AgentResult(
|
|
64
107
|
agent_name=self.name,
|
|
65
108
|
success=True,
|
|
@@ -68,7 +111,7 @@ class CIMonitorAgent(Agent):
|
|
|
68
111
|
)
|
|
69
112
|
|
|
70
113
|
# Analyze status and create findings
|
|
71
|
-
findings = self.
|
|
114
|
+
findings = self._analyze_runs(runs)
|
|
72
115
|
|
|
73
116
|
if findings:
|
|
74
117
|
return AgentResult(
|
|
@@ -102,40 +145,8 @@ class CIMonitorAgent(Agent):
|
|
|
102
145
|
time_since_check = datetime.now() - self.last_check
|
|
103
146
|
return time_since_check > timedelta(seconds=self.check_interval)
|
|
104
147
|
|
|
105
|
-
|
|
106
|
-
"""
|
|
107
|
-
Check CI status using gh CLI.
|
|
108
|
-
|
|
109
|
-
Returns:
|
|
110
|
-
CI status dict or None if unavailable
|
|
111
|
-
"""
|
|
112
|
-
# Check if gh CLI is available
|
|
113
|
-
try:
|
|
114
|
-
result = subprocess.run(
|
|
115
|
-
["gh", "--version"],
|
|
116
|
-
capture_output=True,
|
|
117
|
-
text=True,
|
|
118
|
-
timeout=5,
|
|
119
|
-
)
|
|
120
|
-
if result.returncode != 0:
|
|
121
|
-
return None
|
|
122
|
-
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
123
|
-
return None
|
|
124
|
-
|
|
125
|
-
# Check if authenticated
|
|
126
|
-
try:
|
|
127
|
-
result = subprocess.run(
|
|
128
|
-
["gh", "auth", "status"],
|
|
129
|
-
capture_output=True,
|
|
130
|
-
text=True,
|
|
131
|
-
timeout=5,
|
|
132
|
-
)
|
|
133
|
-
if result.returncode != 0:
|
|
134
|
-
return None
|
|
135
|
-
except subprocess.TimeoutExpired:
|
|
136
|
-
return None
|
|
137
|
-
|
|
138
|
-
# Get current branch
|
|
148
|
+
def _get_current_branch(self) -> Optional[str]:
|
|
149
|
+
"""Get the current git branch."""
|
|
139
150
|
try:
|
|
140
151
|
result = subprocess.run(
|
|
141
152
|
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
@@ -144,92 +155,55 @@ class CIMonitorAgent(Agent):
|
|
|
144
155
|
check=True,
|
|
145
156
|
timeout=5,
|
|
146
157
|
)
|
|
147
|
-
|
|
158
|
+
return result.stdout.strip()
|
|
148
159
|
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
|
149
160
|
return None
|
|
150
161
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
[
|
|
155
|
-
"gh",
|
|
156
|
-
"run",
|
|
157
|
-
"list",
|
|
158
|
-
"--branch",
|
|
159
|
-
branch,
|
|
160
|
-
"--limit",
|
|
161
|
-
"5",
|
|
162
|
-
"--json",
|
|
163
|
-
"status,conclusion,name,databaseId,createdAt,workflowName",
|
|
164
|
-
],
|
|
165
|
-
capture_output=True,
|
|
166
|
-
text=True,
|
|
167
|
-
check=True,
|
|
168
|
-
timeout=10,
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
if result.stdout.strip():
|
|
172
|
-
runs = json.loads(result.stdout)
|
|
173
|
-
return {
|
|
174
|
-
"branch": branch,
|
|
175
|
-
"runs": runs,
|
|
176
|
-
"checked_at": datetime.now().isoformat(),
|
|
177
|
-
}
|
|
178
|
-
except (
|
|
179
|
-
subprocess.CalledProcessError,
|
|
180
|
-
subprocess.TimeoutExpired,
|
|
181
|
-
json.JSONDecodeError,
|
|
182
|
-
):
|
|
183
|
-
return None
|
|
184
|
-
|
|
185
|
-
return None
|
|
162
|
+
def _analyze_runs(self, runs: List) -> list[Finding]:
|
|
163
|
+
"""Analyze CI runs and create findings for issues."""
|
|
164
|
+
findings: list[Finding] = []
|
|
186
165
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
runs = status.get("runs", [])
|
|
191
|
-
branch = status.get("branch", "unknown")
|
|
166
|
+
# Ensure provider is available for messages
|
|
167
|
+
if not self.provider:
|
|
168
|
+
return findings
|
|
192
169
|
|
|
193
|
-
# Group runs by workflow
|
|
170
|
+
# Group runs by workflow name
|
|
194
171
|
workflows: Dict[str, list] = {}
|
|
195
172
|
for run in runs:
|
|
196
|
-
workflow_name = run.
|
|
173
|
+
workflow_name = run.name
|
|
197
174
|
if workflow_name not in workflows:
|
|
198
175
|
workflows[workflow_name] = []
|
|
199
176
|
workflows[workflow_name].append(run)
|
|
200
177
|
|
|
201
178
|
# Check each workflow's latest run
|
|
202
179
|
for workflow_name, workflow_runs in workflows.items():
|
|
203
|
-
latest_run = workflow_runs[0]
|
|
204
|
-
conclusion = latest_run.get("conclusion")
|
|
205
|
-
status_val = latest_run.get("status")
|
|
206
|
-
run_id = latest_run.get("databaseId", "unknown")
|
|
180
|
+
latest_run = workflow_runs[0]
|
|
207
181
|
|
|
208
|
-
if conclusion ==
|
|
182
|
+
if latest_run.conclusion == RunConclusion.FAILURE:
|
|
209
183
|
findings.append(
|
|
210
184
|
Finding(
|
|
211
|
-
id=f"ci-{
|
|
185
|
+
id=f"ci-{latest_run.id}",
|
|
212
186
|
agent="ci-monitor",
|
|
213
187
|
timestamp=datetime.now().isoformat(),
|
|
214
188
|
file=".github/workflows/ci.yml",
|
|
215
189
|
category="ci",
|
|
216
190
|
severity=Severity.ERROR,
|
|
217
|
-
message=f"CI workflow '{workflow_name}' failed on branch '{branch}' (Run #{
|
|
218
|
-
suggestion=f"View details:
|
|
191
|
+
message=f"CI workflow '{workflow_name}' failed on branch '{latest_run.branch}' (Run #{latest_run.id})",
|
|
192
|
+
suggestion=f"View details: {latest_run.url}\nProvider: {self.provider.get_provider_name()}",
|
|
219
193
|
auto_fixable=False,
|
|
220
194
|
)
|
|
221
195
|
)
|
|
222
|
-
elif
|
|
196
|
+
elif latest_run.status == RunStatus.IN_PROGRESS:
|
|
223
197
|
findings.append(
|
|
224
198
|
Finding(
|
|
225
|
-
id=f"ci-{
|
|
199
|
+
id=f"ci-{latest_run.id}",
|
|
226
200
|
agent="ci-monitor",
|
|
227
201
|
timestamp=datetime.now().isoformat(),
|
|
228
202
|
file=".github/workflows/ci.yml",
|
|
229
203
|
category="ci",
|
|
230
204
|
severity=Severity.INFO,
|
|
231
|
-
message=f"CI workflow '{workflow_name}' is running on branch '{branch}' (Run #{
|
|
232
|
-
suggestion=f"Watch progress:
|
|
205
|
+
message=f"CI workflow '{workflow_name}' is running on branch '{latest_run.branch}' (Run #{latest_run.id})",
|
|
206
|
+
suggestion=f"Watch progress at: {latest_run.url}",
|
|
233
207
|
auto_fixable=False,
|
|
234
208
|
)
|
|
235
209
|
)
|