devloop 0.3.0__tar.gz → 0.3.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.3.0 → devloop-0.3.2}/PKG-INFO +1 -1
- {devloop-0.3.0 → devloop-0.3.2}/pyproject.toml +2 -1
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/__init__.py +1 -1
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/type_checker.py +0 -1
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/main.py +106 -3
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/pyodide_installer.py +1 -1
- devloop-0.3.2/src/devloop/cli/templates/claude_commands/README.md +73 -0
- devloop-0.3.2/src/devloop/cli/templates/claude_commands/agent-summary.md +17 -0
- devloop-0.3.2/src/devloop/cli/templates/claude_commands/devloop-findings.md +20 -0
- devloop-0.3.2/src/devloop/cli/templates/claude_commands/devloop-status.md +12 -0
- devloop-0.3.2/src/devloop/cli/templates/git_hooks/pre-commit +68 -0
- devloop-0.3.2/src/devloop/cli/templates/git_hooks/pre-commit-checks +72 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/agent.py +29 -15
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/auto_fix.py +151 -10
- devloop-0.3.2/src/devloop/core/backup_manager.py +409 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/config.py +93 -2
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/manager.py +121 -3
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/performance.py +114 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/cgroups_helper.py +3 -3
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/no_sandbox.py +0 -1
- {devloop-0.3.0 → devloop-0.3.2}/LICENSE +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/README.md +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/__init__.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/agent_health_monitor.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/ci_monitor.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/code_rabbit.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/doc_lifecycle.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/echo.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/file_logger.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/formatter.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/git_commit_assistant.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/linter.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/performance_profiler.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/sandbox_helper.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/security_scanner.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/snyk.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/test_runner.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/__init__.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/__init__.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/custom_agents.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/feedback.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/summary.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/main_v1.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/__init__.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/base.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/filesystem.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/git.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/manager.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/process.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/system.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/__init__.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/agent_template.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/amp_integration.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/context.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/context_store.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/contextual_feedback.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/custom_agent.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/debug_trace.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/event.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/event_store.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/feedback.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/learning.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/operational_health.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/proactive_feedback.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/summary_formatter.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/summary_generator.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/__init__.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/audit_logger.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/bubblewrap_sandbox.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/factory.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/package.json +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/pyodide_runner.js +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/pyodide_sandbox.py +0 -0
- {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/sandbox.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "devloop"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Intelligent background agents for development workflow automation"
|
|
5
5
|
authors = ["DevLoop Contributors <devloop@example.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -36,6 +36,7 @@ packages = [{include = "devloop", from = "src"}]
|
|
|
36
36
|
include = [
|
|
37
37
|
"src/devloop/security/package.json",
|
|
38
38
|
"src/devloop/security/pyodide_runner.js",
|
|
39
|
+
"src/devloop/cli/templates/claude_commands/*.md",
|
|
39
40
|
]
|
|
40
41
|
|
|
41
42
|
[tool.poetry.dependencies]
|
|
@@ -69,7 +69,7 @@ def amp_context():
|
|
|
69
69
|
from pathlib import Path
|
|
70
70
|
|
|
71
71
|
# Try to read the context index
|
|
72
|
-
context_dir = Path(".devloop/context"
|
|
72
|
+
context_dir = Path.cwd() / ".devloop/context"
|
|
73
73
|
index_file = context_dir / "index.json"
|
|
74
74
|
|
|
75
75
|
if index_file.exists():
|
|
@@ -289,8 +289,13 @@ async def watch_async(path: Path, config_path: Path | None):
|
|
|
289
289
|
console.print(f"[dim]Context store: {context_store.context_dir}[/dim]")
|
|
290
290
|
console.print(f"[dim]Event store: {event_store.db_path}[/dim]")
|
|
291
291
|
|
|
292
|
-
#
|
|
293
|
-
|
|
292
|
+
# Get global config for resource limits
|
|
293
|
+
global_config = config.get_global_config()
|
|
294
|
+
|
|
295
|
+
# Create agent manager with project directory and resource limits
|
|
296
|
+
agent_manager = AgentManager(
|
|
297
|
+
event_bus, project_dir=path, resource_limits=global_config.resource_limits
|
|
298
|
+
)
|
|
294
299
|
|
|
295
300
|
# Create filesystem collector
|
|
296
301
|
fs_config = {"watch_paths": [str(path)]}
|
|
@@ -540,6 +545,55 @@ This project uses background agents and Beads for task management.
|
|
|
540
545
|
f"[yellow]Warning:[/yellow] Could not create CLAUDE.md symlink: {e}"
|
|
541
546
|
)
|
|
542
547
|
|
|
548
|
+
# Set up Claude Code slash commands
|
|
549
|
+
claude_commands_dir = path / ".claude" / "commands"
|
|
550
|
+
template_commands_dir = Path(__file__).parent / "templates" / "claude_commands"
|
|
551
|
+
|
|
552
|
+
if template_commands_dir.exists():
|
|
553
|
+
claude_commands_dir.mkdir(parents=True, exist_ok=True)
|
|
554
|
+
|
|
555
|
+
# Copy command templates
|
|
556
|
+
import shutil
|
|
557
|
+
|
|
558
|
+
commands_copied = []
|
|
559
|
+
for template_file in template_commands_dir.glob("*.md"):
|
|
560
|
+
dest_file = claude_commands_dir / template_file.name
|
|
561
|
+
if not dest_file.exists():
|
|
562
|
+
shutil.copy2(template_file, dest_file)
|
|
563
|
+
commands_copied.append(template_file.stem)
|
|
564
|
+
|
|
565
|
+
if commands_copied:
|
|
566
|
+
console.print("\n[green]✓[/green] Created Claude Code slash commands:")
|
|
567
|
+
for cmd in commands_copied:
|
|
568
|
+
console.print(f" • /{cmd}")
|
|
569
|
+
|
|
570
|
+
# Install git hooks if this is a git repository
|
|
571
|
+
git_dir = path / ".git"
|
|
572
|
+
if git_dir.exists() and git_dir.is_dir():
|
|
573
|
+
hooks_template_dir = Path(__file__).parent / "templates" / "git_hooks"
|
|
574
|
+
hooks_dest_dir = git_dir / "hooks"
|
|
575
|
+
|
|
576
|
+
if hooks_template_dir.exists():
|
|
577
|
+
hooks_installed = []
|
|
578
|
+
for template_file in hooks_template_dir.iterdir():
|
|
579
|
+
if template_file.is_file():
|
|
580
|
+
dest_file = hooks_dest_dir / template_file.name
|
|
581
|
+
|
|
582
|
+
# Backup existing hook if present
|
|
583
|
+
if dest_file.exists():
|
|
584
|
+
backup_file = hooks_dest_dir / f"{template_file.name}.backup"
|
|
585
|
+
shutil.copy2(dest_file, backup_file)
|
|
586
|
+
|
|
587
|
+
# Install new hook
|
|
588
|
+
shutil.copy2(template_file, dest_file)
|
|
589
|
+
dest_file.chmod(0o755) # Make executable
|
|
590
|
+
hooks_installed.append(template_file.name)
|
|
591
|
+
|
|
592
|
+
if hooks_installed:
|
|
593
|
+
console.print("\n[green]✓[/green] Installed git hooks:")
|
|
594
|
+
for hook in hooks_installed:
|
|
595
|
+
console.print(f" • {hook}")
|
|
596
|
+
|
|
543
597
|
console.print("\n[green]✓[/green] Initialized!")
|
|
544
598
|
console.print("\nNext steps:")
|
|
545
599
|
console.print(f" 1. Review/edit: [cyan]{claude_dir / 'agents.json'}[/cyan]")
|
|
@@ -611,5 +665,54 @@ def version():
|
|
|
611
665
|
console.print(f"DevLoop v{__version__}")
|
|
612
666
|
|
|
613
667
|
|
|
668
|
+
@app.command()
|
|
669
|
+
def update_hooks(path: Path = typer.Argument(Path.cwd(), help="Project directory")):
|
|
670
|
+
"""Update git hooks from latest templates."""
|
|
671
|
+
import shutil
|
|
672
|
+
|
|
673
|
+
git_dir = path / ".git"
|
|
674
|
+
|
|
675
|
+
if not git_dir.exists() or not git_dir.is_dir():
|
|
676
|
+
console.print(
|
|
677
|
+
f"[red]✗[/red] Not a git repository: {path}\n"
|
|
678
|
+
"[yellow]Git hooks can only be installed in git repositories.[/yellow]"
|
|
679
|
+
)
|
|
680
|
+
return
|
|
681
|
+
|
|
682
|
+
hooks_template_dir = Path(__file__).parent / "templates" / "git_hooks"
|
|
683
|
+
hooks_dest_dir = git_dir / "hooks"
|
|
684
|
+
|
|
685
|
+
if not hooks_template_dir.exists():
|
|
686
|
+
console.print(f"[red]✗[/red] Hook templates not found at: {hooks_template_dir}")
|
|
687
|
+
return
|
|
688
|
+
|
|
689
|
+
hooks_dest_dir.mkdir(parents=True, exist_ok=True)
|
|
690
|
+
hooks_updated = []
|
|
691
|
+
|
|
692
|
+
for template_file in hooks_template_dir.iterdir():
|
|
693
|
+
if template_file.is_file():
|
|
694
|
+
dest_file = hooks_dest_dir / template_file.name
|
|
695
|
+
|
|
696
|
+
# Backup existing hook if present
|
|
697
|
+
if dest_file.exists():
|
|
698
|
+
backup_file = hooks_dest_dir / f"{template_file.name}.backup"
|
|
699
|
+
shutil.copy2(dest_file, backup_file)
|
|
700
|
+
console.print(
|
|
701
|
+
f"[dim] Backed up existing hook: {template_file.name} -> {template_file.name}.backup[/dim]"
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
# Install new hook
|
|
705
|
+
shutil.copy2(template_file, dest_file)
|
|
706
|
+
dest_file.chmod(0o755) # Make executable
|
|
707
|
+
hooks_updated.append(template_file.name)
|
|
708
|
+
|
|
709
|
+
if hooks_updated:
|
|
710
|
+
console.print("\n[green]✓[/green] Updated git hooks:")
|
|
711
|
+
for hook in hooks_updated:
|
|
712
|
+
console.print(f" • {hook}")
|
|
713
|
+
else:
|
|
714
|
+
console.print("[yellow]No hooks found to update[/yellow]")
|
|
715
|
+
|
|
716
|
+
|
|
614
717
|
if __name__ == "__main__":
|
|
615
718
|
app()
|
|
@@ -105,7 +105,7 @@ def prompt_pyodide_installation(non_interactive: bool = False) -> bool:
|
|
|
105
105
|
return True # Not an error, just not available
|
|
106
106
|
|
|
107
107
|
# Node.js is available
|
|
108
|
-
console.print(
|
|
108
|
+
console.print("\n[cyan]Pyodide WASM Sandbox Setup[/cyan]")
|
|
109
109
|
console.print(f" Node.js {node_info} detected")
|
|
110
110
|
console.print(" Pyodide enables cross-platform Python code sandboxing in WASM")
|
|
111
111
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Claude Code Slash Commands
|
|
2
|
+
|
|
3
|
+
This directory contains custom slash commands for Claude Code that integrate with the devloop system.
|
|
4
|
+
|
|
5
|
+
## Available Commands
|
|
6
|
+
|
|
7
|
+
### `/devloop-status`
|
|
8
|
+
|
|
9
|
+
Check if devloop watch is running and show current status.
|
|
10
|
+
|
|
11
|
+
**Usage:**
|
|
12
|
+
```
|
|
13
|
+
/devloop-status
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Output:**
|
|
17
|
+
- Running status (✅ or ❌)
|
|
18
|
+
- Process ID and memory usage
|
|
19
|
+
- Instructions to start if not running
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### `/agent-summary`
|
|
24
|
+
|
|
25
|
+
Generate an intelligent summary of recent dev-agent findings with operational health metrics.
|
|
26
|
+
|
|
27
|
+
**Usage:**
|
|
28
|
+
```
|
|
29
|
+
/agent-summary
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Output:**
|
|
33
|
+
- System health status
|
|
34
|
+
- Recent agent activity
|
|
35
|
+
- Agent performance metrics
|
|
36
|
+
- Findings breakdown by urgency
|
|
37
|
+
- Actionable insights and recommendations
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### `/devloop-findings`
|
|
42
|
+
|
|
43
|
+
Extract DevLoop findings and automatically create Beads issues for tracking.
|
|
44
|
+
|
|
45
|
+
**Usage:**
|
|
46
|
+
```
|
|
47
|
+
/devloop-findings
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**What it does:**
|
|
51
|
+
1. Scans recent findings (last 24 hours) from `.devloop/context/`
|
|
52
|
+
2. Categorizes by type (formatter, linter, performance, security)
|
|
53
|
+
3. Creates Beads issues with appropriate priorities:
|
|
54
|
+
- Formatter violations (P1) - can break CI
|
|
55
|
+
- Linter errors (P1) - need fixing
|
|
56
|
+
- Performance issues (P2) - nice to have
|
|
57
|
+
4. Links issues to current work automatically
|
|
58
|
+
|
|
59
|
+
**After running:**
|
|
60
|
+
Use `bd ready` to see newly created actionable issues.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## How Slash Commands Work
|
|
65
|
+
|
|
66
|
+
Claude Code slash commands are markdown files that define prompts. When you type `/command-name`, Claude Code expands the prompt from the corresponding markdown file and executes it.
|
|
67
|
+
|
|
68
|
+
## Requirements
|
|
69
|
+
|
|
70
|
+
- Devloop must be installed and watch running
|
|
71
|
+
- Python virtual environment activated
|
|
72
|
+
- Context data available in `.devloop/context/`
|
|
73
|
+
- Beads (`bd`) installed for `/devloop-findings` command
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show intelligent summary of recent dev-agent findings
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Generate a summary of recent devloop agent findings by running:
|
|
6
|
+
```bash
|
|
7
|
+
poetry run devloop summary agent-summary recent
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The summary should include:
|
|
11
|
+
- System health status
|
|
12
|
+
- Recent agent activity
|
|
13
|
+
- Agent performance metrics
|
|
14
|
+
- Findings breakdown by urgency
|
|
15
|
+
- Actionable insights
|
|
16
|
+
|
|
17
|
+
Present the output in a clear, organized format highlighting any issues that need attention.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Extract devloop findings and create Beads issues for tracking
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run the findings extraction hook to automatically create Beads issues from recent DevLoop findings:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
./.agents/hooks/extract-findings-to-beads
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This will:
|
|
12
|
+
1. Scan `.devloop/context/` for recent findings (last 24 hours)
|
|
13
|
+
2. Categorize findings by type (formatter, linter, performance, security)
|
|
14
|
+
3. Create Beads issues for high-priority items:
|
|
15
|
+
- Formatter violations (priority 1) - can break CI
|
|
16
|
+
- Linter errors (priority 1) - need fixing
|
|
17
|
+
- Performance issues (priority 2) - nice to have
|
|
18
|
+
4. Link created issues to the current work
|
|
19
|
+
|
|
20
|
+
After running, show the created issues and suggest running `bd ready` to see what's actionable.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Check if devloop watch is running and show status
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Check the status of devloop watch by running the `./.agents/check-devloop-status` script.
|
|
6
|
+
|
|
7
|
+
Show:
|
|
8
|
+
- Whether devloop watch is running
|
|
9
|
+
- Process ID and memory usage if running
|
|
10
|
+
- Instructions to start it if not running
|
|
11
|
+
|
|
12
|
+
Keep the output concise and actionable.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
#
|
|
3
|
+
# Combined pre-commit hook
|
|
4
|
+
#
|
|
5
|
+
# Checks:
|
|
6
|
+
# 1. Poetry lock file sync with pyproject.toml
|
|
7
|
+
# 2. Code quality checks (Black, Ruff, mypy, pytest)
|
|
8
|
+
# 3. bd (beads) pre-commit flush
|
|
9
|
+
# 4. Any existing pre-commit.old hook
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
# Check if pyproject.toml changed but poetry.lock didn't
|
|
13
|
+
if git diff --cached --name-only | grep -q "pyproject.toml"; then
|
|
14
|
+
if ! git diff --cached --name-only | grep -q "poetry.lock"; then
|
|
15
|
+
echo "ERROR: pyproject.toml changed but poetry.lock not updated"
|
|
16
|
+
echo "This will cause CI failure: 'poetry.lock was last generated' error"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "Fix: Run 'poetry lock' and stage both files"
|
|
19
|
+
echo " poetry lock"
|
|
20
|
+
echo " git add poetry.lock"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Run type checks and tests (only if there are Python changes)
|
|
26
|
+
if git diff --cached --name-only | grep -qE '\.py$'; then
|
|
27
|
+
if [ -x ".git/hooks/pre-commit-checks" ]; then
|
|
28
|
+
".git/hooks/pre-commit-checks"
|
|
29
|
+
EXIT_CODE=$?
|
|
30
|
+
if [ $EXIT_CODE -ne 0 ]; then
|
|
31
|
+
exit $EXIT_CODE
|
|
32
|
+
fi
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Run existing hook if present
|
|
37
|
+
if [ -x ".git/hooks/pre-commit.old" ]; then
|
|
38
|
+
".git/hooks/pre-commit.old" "$@"
|
|
39
|
+
EXIT_CODE=$?
|
|
40
|
+
if [ $EXIT_CODE -ne 0 ]; then
|
|
41
|
+
exit $EXIT_CODE
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Check if bd is available
|
|
46
|
+
if ! command -v bd >/dev/null 2>&1; then
|
|
47
|
+
echo "Warning: bd command not found, skipping pre-commit flush" >&2
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Check if we're in a bd workspace
|
|
52
|
+
if [ ! -d .beads ]; then
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Flush pending changes to JSONL
|
|
57
|
+
if ! bd sync --flush-only >/dev/null 2>&1; then
|
|
58
|
+
echo "Error: Failed to flush bd changes to JSONL" >&2
|
|
59
|
+
echo "Run 'bd sync --flush-only' manually to diagnose" >&2
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# If the JSONL file was modified, stage it
|
|
64
|
+
if [ -f .beads/issues.jsonl ]; then
|
|
65
|
+
git add .beads/issues.jsonl 2>/dev/null || true
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
exit 0
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Pre-commit checks: Formatting, linting, type checking, and tests
|
|
4
|
+
# Prevents committing code that has formatting/linting/type errors or failing tests
|
|
5
|
+
#
|
|
6
|
+
# This is called from the main pre-commit hook after lock file checks
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
RED='\033[0;31m'
|
|
12
|
+
GREEN='\033[0;32m'
|
|
13
|
+
YELLOW='\033[1;33m'
|
|
14
|
+
NC='\033[0m'
|
|
15
|
+
|
|
16
|
+
echo -e "${YELLOW}[Pre-commit] Running code quality checks and tests...${NC}"
|
|
17
|
+
|
|
18
|
+
# Check if poetry is available
|
|
19
|
+
if ! command -v poetry &> /dev/null; then
|
|
20
|
+
echo -e "${YELLOW}[Pre-commit] poetry not found, skipping checks${NC}"
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Navigate to src directory where pyproject.toml is located
|
|
25
|
+
if [ ! -d "src" ]; then
|
|
26
|
+
echo -e "${YELLOW}[Pre-commit] src directory not found, skipping checks${NC}"
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Run Black formatter check
|
|
31
|
+
echo -e "${YELLOW}[Pre-commit] Checking code formatting (Black)...${NC}"
|
|
32
|
+
if ! poetry run black --check src/ > /tmp/black-output.txt 2>&1; then
|
|
33
|
+
echo -e "${RED}[Pre-commit] ❌ Black formatting check failed:${NC}"
|
|
34
|
+
cat /tmp/black-output.txt
|
|
35
|
+
echo -e "${YELLOW}Fix with: poetry run black src/${NC}"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
echo -e "${GREEN}[Pre-commit] ✅ Code formatting passed${NC}"
|
|
39
|
+
|
|
40
|
+
# Run Ruff linting
|
|
41
|
+
echo -e "${YELLOW}[Pre-commit] Running linter (Ruff)...${NC}"
|
|
42
|
+
if ! poetry run ruff check src/ > /tmp/ruff-output.txt 2>&1; then
|
|
43
|
+
echo -e "${RED}[Pre-commit] ❌ Ruff linting failed:${NC}"
|
|
44
|
+
cat /tmp/ruff-output.txt
|
|
45
|
+
echo -e "${YELLOW}Fix with: poetry run ruff check src/ --fix${NC}"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
echo -e "${GREEN}[Pre-commit] ✅ Linting passed${NC}"
|
|
49
|
+
|
|
50
|
+
cd src
|
|
51
|
+
|
|
52
|
+
# Run mypy
|
|
53
|
+
echo -e "${YELLOW}[Pre-commit] Type checking (mypy)...${NC}"
|
|
54
|
+
if ! poetry run mypy devloop/core/ devloop/agents/ > /tmp/mypy-output.txt 2>&1; then
|
|
55
|
+
echo -e "${RED}[Pre-commit] ❌ Type check failed:${NC}"
|
|
56
|
+
cat /tmp/mypy-output.txt
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
echo -e "${GREEN}[Pre-commit] ✅ Type checks passed${NC}"
|
|
60
|
+
|
|
61
|
+
# Run pytest (from parent directory where tests/ exists)
|
|
62
|
+
echo -e "${YELLOW}[Pre-commit] Running tests (pytest)...${NC}"
|
|
63
|
+
cd ..
|
|
64
|
+
if ! poetry run pytest tests/ -q > /tmp/pytest-output.txt 2>&1; then
|
|
65
|
+
echo -e "${RED}[Pre-commit] ❌ Tests failed:${NC}"
|
|
66
|
+
# Show last 50 lines of output
|
|
67
|
+
tail -50 /tmp/pytest-output.txt
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
echo -e "${GREEN}[Pre-commit] ✅ All tests passed${NC}"
|
|
71
|
+
|
|
72
|
+
exit 0
|
|
@@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional
|
|
|
11
11
|
|
|
12
12
|
from .event import Event, EventBus
|
|
13
13
|
from .feedback import FeedbackAPI
|
|
14
|
-
from .performance import PerformanceMonitor
|
|
14
|
+
from .performance import AgentResourceTracker, PerformanceMonitor
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@dataclass
|
|
@@ -79,12 +79,14 @@ class Agent(ABC):
|
|
|
79
79
|
event_bus: EventBus,
|
|
80
80
|
feedback_api: Optional[FeedbackAPI] = None,
|
|
81
81
|
performance_monitor: Optional[PerformanceMonitor] = None,
|
|
82
|
+
resource_tracker: Optional[AgentResourceTracker] = None,
|
|
82
83
|
):
|
|
83
84
|
self.name = name
|
|
84
85
|
self.triggers = triggers
|
|
85
86
|
self.event_bus = event_bus
|
|
86
87
|
self.feedback_api = feedback_api
|
|
87
88
|
self.performance_monitor = performance_monitor
|
|
89
|
+
self.resource_tracker = resource_tracker
|
|
88
90
|
self.enabled = True
|
|
89
91
|
self.logger = logging.getLogger(f"agent.{name}")
|
|
90
92
|
self._running = False
|
|
@@ -139,21 +141,33 @@ class Agent(ABC):
|
|
|
139
141
|
try:
|
|
140
142
|
operation_name = f"agent.{self.name}.handle"
|
|
141
143
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
# Mark agent as active for resource tracking
|
|
145
|
+
if self.resource_tracker:
|
|
146
|
+
self.resource_tracker.mark_agent_active(self.name)
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
if self.performance_monitor:
|
|
150
|
+
async with self.performance_monitor.monitor_operation(
|
|
151
|
+
operation_name,
|
|
152
|
+
metadata={
|
|
153
|
+
"event_type": event.type,
|
|
154
|
+
"agent_name": self.name,
|
|
155
|
+
},
|
|
156
|
+
) as metrics:
|
|
157
|
+
result = await self.handle(event)
|
|
158
|
+
metrics.complete(result.success, result.error)
|
|
159
|
+
|
|
160
|
+
# Update result duration from metrics
|
|
161
|
+
if metrics.duration:
|
|
162
|
+
result.duration = metrics.duration
|
|
163
|
+
else:
|
|
164
|
+
start_time = time.time()
|
|
147
165
|
result = await self.handle(event)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
else:
|
|
154
|
-
start_time = time.time()
|
|
155
|
-
result = await self.handle(event)
|
|
156
|
-
result.duration = time.time() - start_time
|
|
166
|
+
result.duration = time.time() - start_time
|
|
167
|
+
finally:
|
|
168
|
+
# Mark agent as inactive after handling
|
|
169
|
+
if self.resource_tracker:
|
|
170
|
+
self.resource_tracker.mark_agent_inactive(self.name)
|
|
157
171
|
|
|
158
172
|
# Update performance store if available
|
|
159
173
|
if self.feedback_api:
|