devloop 0.3.1__tar.gz → 0.3.3__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.1 → devloop-0.3.3}/PKG-INFO +1 -1
- {devloop-0.3.1 → devloop-0.3.3}/pyproject.toml +1 -1
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/main.py +85 -4
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/templates/claude_commands/agent-summary.md +1 -1
- devloop-0.3.3/src/devloop/cli/templates/git_hooks/pre-commit +70 -0
- devloop-0.3.3/src/devloop/cli/templates/git_hooks/pre-commit-checks +72 -0
- devloop-0.3.3/src/devloop/cli/templates/git_hooks/pre-push +114 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/agent.py +29 -15
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/auto_fix.py +151 -10
- devloop-0.3.3/src/devloop/core/backup_manager.py +409 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/config.py +93 -2
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/manager.py +121 -3
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/performance.py +114 -0
- {devloop-0.3.1 → devloop-0.3.3}/LICENSE +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/README.md +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/__init__.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/__init__.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/agent_health_monitor.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/ci_monitor.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/code_rabbit.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/doc_lifecycle.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/echo.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/file_logger.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/formatter.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/git_commit_assistant.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/linter.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/performance_profiler.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/sandbox_helper.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/security_scanner.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/snyk.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/test_runner.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/agents/type_checker.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/__init__.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/commands/__init__.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/commands/custom_agents.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/commands/feedback.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/commands/summary.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/main_v1.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/pyodide_installer.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/templates/claude_commands/README.md +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/templates/claude_commands/devloop-findings.md +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/cli/templates/claude_commands/devloop-status.md +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/collectors/__init__.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/collectors/base.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/collectors/filesystem.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/collectors/git.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/collectors/manager.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/collectors/process.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/collectors/system.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/__init__.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/agent_template.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/amp_integration.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/context.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/context_store.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/contextual_feedback.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/custom_agent.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/debug_trace.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/event.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/event_store.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/feedback.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/learning.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/operational_health.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/proactive_feedback.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/summary_formatter.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/core/summary_generator.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/__init__.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/audit_logger.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/bubblewrap_sandbox.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/cgroups_helper.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/factory.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/no_sandbox.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/package.json +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/pyodide_runner.js +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/pyodide_sandbox.py +0 -0
- {devloop-0.3.1 → devloop-0.3.3}/src/devloop/security/sandbox.py +0 -0
|
@@ -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)]}
|
|
@@ -558,10 +563,37 @@ This project uses background agents and Beads for task management.
|
|
|
558
563
|
commands_copied.append(template_file.stem)
|
|
559
564
|
|
|
560
565
|
if commands_copied:
|
|
561
|
-
console.print(
|
|
566
|
+
console.print("\n[green]✓[/green] Created Claude Code slash commands:")
|
|
562
567
|
for cmd in commands_copied:
|
|
563
568
|
console.print(f" • /{cmd}")
|
|
564
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
|
+
|
|
565
597
|
console.print("\n[green]✓[/green] Initialized!")
|
|
566
598
|
console.print("\nNext steps:")
|
|
567
599
|
console.print(f" 1. Review/edit: [cyan]{claude_dir / 'agents.json'}[/cyan]")
|
|
@@ -633,5 +665,54 @@ def version():
|
|
|
633
665
|
console.print(f"DevLoop v{__version__}")
|
|
634
666
|
|
|
635
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
|
+
|
|
636
717
|
if __name__ == "__main__":
|
|
637
718
|
app()
|
|
@@ -4,7 +4,7 @@ description: Show intelligent summary of recent dev-agent findings
|
|
|
4
4
|
|
|
5
5
|
Generate a summary of recent devloop agent findings by running:
|
|
6
6
|
```bash
|
|
7
|
-
|
|
7
|
+
devloop summary agent-summary recent
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
The summary should include:
|
|
@@ -0,0 +1,70 @@
|
|
|
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 (only in Poetry projects)
|
|
13
|
+
if [ -f "poetry.lock" ]; then
|
|
14
|
+
if git diff --cached --name-only | grep -q "pyproject.toml"; then
|
|
15
|
+
if ! git diff --cached --name-only | grep -q "poetry.lock"; then
|
|
16
|
+
echo "ERROR: pyproject.toml changed but poetry.lock not updated"
|
|
17
|
+
echo "This will cause CI failure: 'poetry.lock was last generated' error"
|
|
18
|
+
echo ""
|
|
19
|
+
echo "Fix: Run 'poetry lock' and stage both files"
|
|
20
|
+
echo " poetry lock"
|
|
21
|
+
echo " git add poetry.lock"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
fi
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Run type checks and tests (only if there are Python changes)
|
|
28
|
+
if git diff --cached --name-only | grep -qE '\.py$'; then
|
|
29
|
+
if [ -x ".git/hooks/pre-commit-checks" ]; then
|
|
30
|
+
".git/hooks/pre-commit-checks"
|
|
31
|
+
EXIT_CODE=$?
|
|
32
|
+
if [ $EXIT_CODE -ne 0 ]; then
|
|
33
|
+
exit $EXIT_CODE
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Run existing hook if present
|
|
39
|
+
if [ -x ".git/hooks/pre-commit.old" ]; then
|
|
40
|
+
".git/hooks/pre-commit.old" "$@"
|
|
41
|
+
EXIT_CODE=$?
|
|
42
|
+
if [ $EXIT_CODE -ne 0 ]; then
|
|
43
|
+
exit $EXIT_CODE
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Check if bd is available
|
|
48
|
+
if ! command -v bd >/dev/null 2>&1; then
|
|
49
|
+
echo "Warning: bd command not found, skipping pre-commit flush" >&2
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Check if we're in a bd workspace
|
|
54
|
+
if [ ! -d .beads ]; then
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Flush pending changes to JSONL
|
|
59
|
+
if ! bd sync --flush-only >/dev/null 2>&1; then
|
|
60
|
+
echo "Error: Failed to flush bd changes to JSONL" >&2
|
|
61
|
+
echo "Run 'bd sync --flush-only' manually to diagnose" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# If the JSONL file was modified, stage it
|
|
66
|
+
if [ -f .beads/issues.jsonl ]; then
|
|
67
|
+
git add .beads/issues.jsonl 2>/dev/null || true
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
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
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Pre-push hook: Verify CI passes before pushing to remote
|
|
3
|
+
# Prevents pushing code that fails local checks
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# Colors for output
|
|
8
|
+
RED='\033[0;31m'
|
|
9
|
+
GREEN='\033[0;32m'
|
|
10
|
+
YELLOW='\033[1;33m'
|
|
11
|
+
NC='\033[0m' # No Color
|
|
12
|
+
|
|
13
|
+
echo -e "${YELLOW}[CI Check] Running pre-push verification...${NC}"
|
|
14
|
+
|
|
15
|
+
# Check if gh CLI is available
|
|
16
|
+
if ! command -v gh &> /dev/null; then
|
|
17
|
+
echo -e "${YELLOW}[CI Check] gh CLI not found, skipping remote CI check${NC}"
|
|
18
|
+
echo -e "${YELLOW}[CI Check] Install GitHub CLI to enable CI verification: https://cli.github.com${NC}"
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Check if jq is available
|
|
23
|
+
if ! command -v jq &> /dev/null; then
|
|
24
|
+
echo -e "${YELLOW}[CI Check] jq not found, skipping remote CI check${NC}"
|
|
25
|
+
echo -e "${YELLOW}[CI Check] Install jq to enable CI verification: https://jqlang.github.io/jq/download/${NC}"
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Get the branch being pushed
|
|
30
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
31
|
+
|
|
32
|
+
echo -e "${YELLOW}[CI Check] Checking CI status for branch: $BRANCH${NC}"
|
|
33
|
+
|
|
34
|
+
# Check recent CI runs on this branch (get last completed run)
|
|
35
|
+
LAST_RUN=$(gh run list --branch "$BRANCH" --limit 1 --json status,conclusion --jq '.[0]' 2>/dev/null || echo "")
|
|
36
|
+
|
|
37
|
+
if [ -z "$LAST_RUN" ]; then
|
|
38
|
+
echo -e "${YELLOW}[CI Check] No previous CI runs found${NC}"
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
STATUS=$(echo "$LAST_RUN" | jq -r '.status' 2>/dev/null || echo "")
|
|
43
|
+
CONCLUSION=$(echo "$LAST_RUN" | jq -r '.conclusion' 2>/dev/null || echo "")
|
|
44
|
+
|
|
45
|
+
# If run is in progress, wait for completion (up to 2 minutes)
|
|
46
|
+
if [ "$STATUS" != "completed" ]; then
|
|
47
|
+
echo -e "${YELLOW}[CI Check] Previous CI run in progress, waiting for completion...${NC}"
|
|
48
|
+
|
|
49
|
+
WAIT_TIME=0
|
|
50
|
+
MAX_WAIT=120 # 2 minutes
|
|
51
|
+
POLL_INTERVAL=5
|
|
52
|
+
|
|
53
|
+
while [ $WAIT_TIME -lt $MAX_WAIT ]; do
|
|
54
|
+
sleep $POLL_INTERVAL
|
|
55
|
+
WAIT_TIME=$((WAIT_TIME + POLL_INTERVAL))
|
|
56
|
+
|
|
57
|
+
LAST_RUN=$(gh run list --branch "$BRANCH" --limit 1 --json status,conclusion --jq '.[0]' 2>/dev/null || echo "")
|
|
58
|
+
STATUS=$(echo "$LAST_RUN" | jq -r '.status' 2>/dev/null || echo "")
|
|
59
|
+
CONCLUSION=$(echo "$LAST_RUN" | jq -r '.conclusion' 2>/dev/null || echo "")
|
|
60
|
+
|
|
61
|
+
if [ "$STATUS" = "completed" ]; then
|
|
62
|
+
echo -e "${YELLOW}[CI Check] CI completed with status: $CONCLUSION${NC}"
|
|
63
|
+
break
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
echo -e "${YELLOW}[CI Check] Still waiting... ($WAIT_TIME/$MAX_WAIT seconds)${NC}"
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
# If still not completed, inform user
|
|
70
|
+
if [ "$STATUS" != "completed" ]; then
|
|
71
|
+
echo -e "${YELLOW}[CI Check] CI still running after 2 minutes${NC}"
|
|
72
|
+
echo -e "${YELLOW}[CI Check] You can push and monitor at: https://github.com/$(git config --get remote.origin.url | sed 's/.*github.com[:/]\(.*\)\.git/\1/')/actions${NC}"
|
|
73
|
+
exit 0
|
|
74
|
+
fi
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Check conclusion
|
|
78
|
+
if [ "$CONCLUSION" = "failure" ] || [ "$CONCLUSION" = "timed_out" ] || [ "$CONCLUSION" = "cancelled" ]; then
|
|
79
|
+
echo -e "${RED}[CI Check] ❌ Last CI run failed with status: $CONCLUSION${NC}"
|
|
80
|
+
echo -e "${RED}[CI Check] View the failed run: gh run list --branch $BRANCH${NC}"
|
|
81
|
+
echo -e "${RED}[CI Check] Please fix CI issues before pushing${NC}"
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
if [ "$CONCLUSION" = "success" ]; then
|
|
86
|
+
echo -e "${GREEN}[CI Check] ✅ Latest CI run passed${NC}"
|
|
87
|
+
|
|
88
|
+
# After CI passes, extract DevLoop findings and create Beads issues
|
|
89
|
+
echo -e "${YELLOW}[Findings] Extracting DevLoop findings...${NC}"
|
|
90
|
+
|
|
91
|
+
# Find the extract-findings-to-beads script
|
|
92
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
93
|
+
FINDINGS_SCRIPT="$SCRIPT_DIR/../../.agents/hooks/extract-findings-to-beads"
|
|
94
|
+
|
|
95
|
+
if [ -x "$FINDINGS_SCRIPT" ]; then
|
|
96
|
+
# Get current git commit hash to link findings
|
|
97
|
+
CURRENT_COMMIT=$(git rev-parse HEAD)
|
|
98
|
+
|
|
99
|
+
# Extract findings and create Beads issues
|
|
100
|
+
if "$FINDINGS_SCRIPT"; then
|
|
101
|
+
echo -e "${GREEN}[Findings] ✅ DevLoop findings processed${NC}"
|
|
102
|
+
else
|
|
103
|
+
echo -e "${YELLOW}[Findings] ⚠️ Could not process DevLoop findings (non-blocking)${NC}"
|
|
104
|
+
fi
|
|
105
|
+
else
|
|
106
|
+
echo -e "${YELLOW}[Findings] 📝 Extract-findings script not found (optional)${NC}"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
exit 0
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# For any other status, allow push
|
|
113
|
+
echo -e "${YELLOW}[CI Check] Unable to verify CI status (status: $CONCLUSION), allowing push${NC}"
|
|
114
|
+
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:
|