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.
Files changed (74) hide show
  1. {devloop-0.3.0 → devloop-0.3.2}/PKG-INFO +1 -1
  2. {devloop-0.3.0 → devloop-0.3.2}/pyproject.toml +2 -1
  3. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/__init__.py +1 -1
  4. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/type_checker.py +0 -1
  5. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/main.py +106 -3
  6. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/pyodide_installer.py +1 -1
  7. devloop-0.3.2/src/devloop/cli/templates/claude_commands/README.md +73 -0
  8. devloop-0.3.2/src/devloop/cli/templates/claude_commands/agent-summary.md +17 -0
  9. devloop-0.3.2/src/devloop/cli/templates/claude_commands/devloop-findings.md +20 -0
  10. devloop-0.3.2/src/devloop/cli/templates/claude_commands/devloop-status.md +12 -0
  11. devloop-0.3.2/src/devloop/cli/templates/git_hooks/pre-commit +68 -0
  12. devloop-0.3.2/src/devloop/cli/templates/git_hooks/pre-commit-checks +72 -0
  13. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/agent.py +29 -15
  14. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/auto_fix.py +151 -10
  15. devloop-0.3.2/src/devloop/core/backup_manager.py +409 -0
  16. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/config.py +93 -2
  17. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/manager.py +121 -3
  18. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/performance.py +114 -0
  19. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/cgroups_helper.py +3 -3
  20. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/no_sandbox.py +0 -1
  21. {devloop-0.3.0 → devloop-0.3.2}/LICENSE +0 -0
  22. {devloop-0.3.0 → devloop-0.3.2}/README.md +0 -0
  23. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/__init__.py +0 -0
  24. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/agent_health_monitor.py +0 -0
  25. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/ci_monitor.py +0 -0
  26. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/code_rabbit.py +0 -0
  27. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/doc_lifecycle.py +0 -0
  28. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/echo.py +0 -0
  29. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/file_logger.py +0 -0
  30. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/formatter.py +0 -0
  31. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/git_commit_assistant.py +0 -0
  32. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/linter.py +0 -0
  33. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/performance_profiler.py +0 -0
  34. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/sandbox_helper.py +0 -0
  35. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/security_scanner.py +0 -0
  36. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/snyk.py +0 -0
  37. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/agents/test_runner.py +0 -0
  38. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/__init__.py +0 -0
  39. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/__init__.py +0 -0
  40. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/custom_agents.py +0 -0
  41. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/feedback.py +0 -0
  42. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/commands/summary.py +0 -0
  43. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/cli/main_v1.py +0 -0
  44. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/__init__.py +0 -0
  45. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/base.py +0 -0
  46. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/filesystem.py +0 -0
  47. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/git.py +0 -0
  48. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/manager.py +0 -0
  49. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/process.py +0 -0
  50. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/collectors/system.py +0 -0
  51. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/__init__.py +0 -0
  52. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/agent_template.py +0 -0
  53. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/amp_integration.py +0 -0
  54. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/context.py +0 -0
  55. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/context_store.py +0 -0
  56. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/contextual_feedback.py +0 -0
  57. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/custom_agent.py +0 -0
  58. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/debug_trace.py +0 -0
  59. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/event.py +0 -0
  60. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/event_store.py +0 -0
  61. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/feedback.py +0 -0
  62. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/learning.py +0 -0
  63. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/operational_health.py +0 -0
  64. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/proactive_feedback.py +0 -0
  65. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/summary_formatter.py +0 -0
  66. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/core/summary_generator.py +0 -0
  67. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/__init__.py +0 -0
  68. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/audit_logger.py +0 -0
  69. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/bubblewrap_sandbox.py +0 -0
  70. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/factory.py +0 -0
  71. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/package.json +0 -0
  72. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/pyodide_runner.js +0 -0
  73. {devloop-0.3.0 → devloop-0.3.2}/src/devloop/security/pyodide_sandbox.py +0 -0
  74. {devloop-0.3.0 → devloop-0.3.2}/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.0
3
+ Version: 0.3.2
4
4
  Summary: Intelligent background agents for development workflow automation
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "devloop"
3
- version = "0.3.0"
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]
@@ -1,3 +1,3 @@
1
1
  """DevLoop - Background agents for development workflow automation."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.3.1"
@@ -177,7 +177,6 @@ class TypeCheckerAgent(Agent):
177
177
  """Run MyPy type checker."""
178
178
  try:
179
179
  # Check if mypy is available
180
- import asyncio
181
180
 
182
181
  check_result = await self.sandbox.run_sandboxed(
183
182
  [sys.executable, "-c", "import mypy"],
@@ -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
- # Create agent manager with project directory
293
- agent_manager = AgentManager(event_bus, project_dir=path)
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(f"\n[cyan]Pyodide WASM Sandbox Setup[/cyan]")
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
- if self.performance_monitor:
143
- async with self.performance_monitor.monitor_operation(
144
- operation_name,
145
- metadata={"event_type": event.type, "agent_name": self.name},
146
- ) as metrics:
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
- metrics.complete(result.success, result.error)
149
-
150
- # Update result duration from metrics
151
- if metrics.duration:
152
- result.duration = metrics.duration
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: