Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions bounty-hunter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# SolFoundry β€” Full Autonomous Bounty-Hunting Agent

> **Bounty:** #861 | **Tier:** T3 | **Reward:** 1,000,000 $FNDRY

A fully autonomous multi-agent system that discovers, analyzes, implements, tests, and submits bounty solutions without human intervention.

## Architecture

```
bounty-hunter/
β”œβ”€β”€ pyproject.toml # Python package configuration
β”œβ”€β”€ README.md # This file
β”œβ”€β”€ bounty_hunter/
β”‚ β”œβ”€β”€ __init__.py # Package init, exports
β”‚ β”œβ”€β”€ __main__.py # CLI entry point
β”‚ β”œβ”€β”€ config.py # Environment & configuration
β”‚ β”œβ”€β”€ discoverer.py # Bounty discovery via GitHub API
β”‚ β”œβ”€β”€ planner.py # Requirements analysis β†’ implementation plan
β”‚ β”œβ”€β”€ implementer.py # File creation and modification
β”‚ β”œβ”€β”€ tester.py # Test execution & validation
β”‚ β”œβ”€β”€ submitter.py # Git operations & PR submission
β”‚ └── orchestrator.py # Full pipeline coordinator
└── tests/
β”œβ”€β”€ __init__.py
└── test_bounty_hunter.py # Unit & integration tests
```

## Pipeline

| Step | Component | Description |
|------|-----------|-------------|
| 1 | **Discoverer** | Finds bounty issues via `gh` CLI, parses tier/reward |
| 2 | **Planner** | Analyzes acceptance criteria, generates task list |
| 3 | **Implementer** | Creates/modifies files per the plan |
| 4 | **Tester** | Runs pytest, validates code quality, checks PR format |
| 5 | **Submitter** | Branches, commits, pushes, and submits PR via `gh` |
| 6 | **Orchestrator** | Coordinates all steps with logging and error handling |

## Usage

### CLI

```bash
# Install
pip install -e bounty-hunter/

# List open bounties
bounty-hunter discover

# Auto-hunt the best available bounty
bounty-hunter hunt

# Target a specific issue
bounty-hunter hunt --issue 861

# Dry run (plan only)
bounty-hunter hunt --dry-run

# Validate a PR body
bounty-hunter validate --file .pr_body.md
```

### Python API

```python
from bounty_hunter import Orchestrator

orch = Orchestrator()
bounties = orch.run_discover_only() # List bounties
orch.run_full_pipeline(target_issue=861) # Full pipeline
```

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `WALLET_ADDRESS` | `7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU` | Solana wallet for $FNDRY payout |
| `REPO_OWNER` | `SolFoundry` | GitHub repo owner |
| `REPO_NAME` | `solfoundry` | GitHub repo name |
| `LOG_LEVEL` | `INFO` | Logging verbosity |

## Multi-LLM Orchestration

The agent uses a modular architecture that supports multi-LLM analysis:

- **Planner** analyzes bounty requirements using rule-based extraction and optional LLM endpoints
- **Discoverer** validates claim status before committing to a bounty
- **Tester** runs quality gates including code style and missing CI warnings

## Test Coverage

```bash
cd bounty-hunter && python3 -m pytest tests/ -v
```

Unit tests cover: config, discovery parsing, planning logic, PR validation, PR body generation, and the full import chain.

## Quality Gates

- βœ… All PRs include `Closes #N` and wallet address
- βœ… Tests run before PR submission
- βœ… Code quality check (no AI slop / excessive TODOs)
- βœ… No hardcoded secrets
- βœ… Follows SolFoundry PR template exactly
23 changes: 23 additions & 0 deletions bounty-hunter/bounty_hunter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Bounty Hunter β€” autonomous SolFoundry bounty agent."""

__version__ = "0.1.0"
__all__ = [
"Config",
"BountyDiscoverer",
"Planner",
"Implementer",
"Tester",
"Submitter",
"Orchestrator",
"Bounty",
"SolutionPlan",
"EnvConfig",
]

from bounty_hunter.config import Config, EnvConfig
from bounty_hunter.discoverer import BountyDiscoverer, Bounty
from bounty_hunter.planner import Planner, SolutionPlan
from bounty_hunter.implementer import Implementer
from bounty_hunter.tester import Tester
from bounty_hunter.submitter import Submitter
from bounty_hunter.orchestrator import Orchestrator
105 changes: 105 additions & 0 deletions bounty-hunter/bounty_hunter/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""Bounty Hunter CLI β€” autonomous bounty hunting for SolFoundry."""

import argparse
import logging
import sys

from bounty_hunter.config import Config
from bounty_hunter.orchestrator import Orchestrator


def setup_logging(verbose: bool = False):
level = logging.DEBUG if verbose else logging.INFO
logging.basicConfig(
level=level,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%H:%M:%S",
)


def main():
parser = argparse.ArgumentParser(
description="SolFoundry Autonomous Bounty Hunter Agent",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
bounty-hunter discover # List all open bounties
bounty-hunter hunt # Auto-select best bounty & run full pipeline
bounty-hunter hunt --issue 861 # Target specific issue
bounty-hunter validate # Validate PR formatting
bounty-hunter --version # Show version
""",
)

parser.add_argument("--verbose", "-v", action="store_true", help="Enable debug logging")
parser.add_argument("--version", action="store_true", help="Show version and exit")

subparsers = parser.add_subparsers(dest="command", help="Available commands")

# discover
subparsers.add_parser("discover", help="List available bounty issues")

# hunt
hunt_parser = subparsers.add_parser("hunt", help="Run the full autonomous pipeline")
hunt_parser.add_argument("--issue", "-i", type=int, help="Target specific issue number")
hunt_parser.add_argument("--dry-run", action="store_true", help="Plan only, no implementation")

# validate
validate_parser = subparsers.add_parser("validate", help="Validate a PR body")
validate_parser.add_argument("--file", "-f", help="Path to PR body file to validate")
validate_parser.add_argument("--text", "-t", help="PR body text to validate")

args = parser.parse_args()

if args.version:
from bounty_hunter import __version__
print(f"bounty-hunter v{__version__}")
return 0

setup_logging(args.verbose)

config = Config()
orchestrator = Orchestrator(config)

if args.command == "discover":
bounties = orchestrator.run_discover_only()
print(f"\nFound {len(bounties)} available bounties:")
for b in bounties:
print(f" #{b.number:4d} [{b.tier:2s}] {b.reward:12s} β€” {b.title[:70]}")
return 0

elif args.command == "hunt":
if args.dry_run:
bounties = orchestrator.run_discover_only()
if bounties:
print(f"\nDry-run mode. Would target: #{bounties[0].number}")
return 0

success = orchestrator.run_full_pipeline(target_issue=args.issue)
return 0 if success else 1

elif args.command == "validate":
from bounty_hunter.tester import Tester
if args.file:
with open(args.file) as f:
text = f.read()
elif args.text:
text = args.text
else:
print("Provide either --file or --text")
return 1

result = Tester.validate_pr_content(text)
print(result.summary)
for f in result.failures:
print(f" βœ— {f}")
return 0 if result.passed else 1

else:
parser.print_help()
return 0


if __name__ == "__main__":
sys.exit(main())
39 changes: 39 additions & 0 deletions bounty-hunter/bounty_hunter/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Configuration and environment management for the bounty hunter."""

import os
from dataclasses import dataclass, field


@dataclass
class EnvConfig:
"""Environment variable configuration."""

github_token: str = ""
wallet_address: str = ""
repo_owner: str = "SolFoundry"
repo_name: str = "solfoundry"
llm_api_key: str = ""
llm_endpoint: str = ""
log_level: str = "INFO"

@classmethod
def from_env(cls) -> "EnvConfig":
return cls(
github_token=os.environ.get("GITHUB_TOKEN", ""),
wallet_address=os.environ.get("WALLET_ADDRESS", "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"),
repo_owner=os.environ.get("REPO_OWNER", "SolFoundry"),
repo_name=os.environ.get("REPO_NAME", "solfoundry"),
llm_api_key=os.environ.get("LLM_API_KEY", ""),
llm_endpoint=os.environ.get("LLM_ENDPOINT", ""),
log_level=os.environ.get("LOG_LEVEL", "INFO"),
)


@dataclass
class Config:
"""Full configuration."""

env: EnvConfig = field(default_factory=EnvConfig.from_env)
max_retries: int = 3
pr_branch_prefix: str = "feat/bounty"
verbose: bool = False
Loading