Skip to content
Merged
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
414 changes: 414 additions & 0 deletions docs/ralph-comparison-audit.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ dev = [
"pytest-asyncio>=0.21.0",
]

[project.scripts]
fireteam = "fireteam.runner:main"

[tool.setuptools]
packages = ["fireteam"]

Expand Down
57 changes: 49 additions & 8 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,72 @@
"""
Fireteam - Adaptive task execution using Claude Agent SDK.
Fireteam - Adaptive task execution using Claude Code CLI.

Minimal layer on top of SDK that adds:
Uses Claude Code CLI for execution, piggybacking on the user's
existing session and credits. No separate API key required.

Features:
- Complexity estimation (auto-select execution mode)
- Quality hooks (auto-run tests after code changes)
- Circuit breaker (warns on stuck loops)
- Rate limiting (API budget management)
- Dual-gate exit (executor + reviewer consensus)
- Session continuity via Claude Code
- Tmux-based autonomous execution

Usage:
Usage (programmatic):
from fireteam import execute, ExecutionMode

result = await execute(
project_dir="/path/to/project",
goal="Fix the bug in auth.py",
)

Usage (CLI - autonomous):
fireteam start --project-dir /path/to/project --goal "Fix all bugs"
fireteam list
fireteam attach fireteam-project
fireteam logs fireteam-project
fireteam kill fireteam-project
"""

from .api import execute
from .models import ExecutionMode, ExecutionResult
from .complexity import ComplexityLevel, estimate_complexity
from .hooks import QUALITY_HOOKS, AUTONOMOUS_HOOKS, create_test_hooks
from .claude_cli import CLISession, CLIResult, ClaudeCLI
from .circuit_breaker import CircuitBreaker, CircuitState, IterationMetrics, create_circuit_breaker
from .rate_limiter import RateLimiter, RateLimitExceeded, get_rate_limiter, reset_rate_limiter
from .runner import start_session, attach_session, kill_session, list_sessions, SessionInfo
from .prompt import Prompt, resolve_prompt

__all__ = [
# Main API
"execute",
# Models
"ExecutionMode",
"ExecutionResult",
# Complexity
"ComplexityLevel",
"estimate_complexity",
"QUALITY_HOOKS",
"AUTONOMOUS_HOOKS",
"create_test_hooks",
# CLI
"CLISession",
"CLIResult",
"ClaudeCLI",
# Circuit Breaker
"CircuitBreaker",
"CircuitState",
"IterationMetrics",
"create_circuit_breaker",
# Rate Limiter
"RateLimiter",
"RateLimitExceeded",
"get_rate_limiter",
"reset_rate_limiter",
# Runner (tmux-based autonomous execution)
"start_session",
"attach_session",
"kill_session",
"list_sessions",
"SessionInfo",
# Prompt
"Prompt",
"resolve_prompt",
]
73 changes: 56 additions & 17 deletions src/api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""
Public API for fireteam library.

Provides adaptive task execution using Claude Agent SDK primitives.
Minimal layer on top of SDK - complexity estimation + execution mode selection.
Provides adaptive task execution using Claude Code CLI.
Piggybacks on user's Claude Code session for unified billing.

Usage:
import fireteam
Expand All @@ -18,10 +18,12 @@
from pathlib import Path

from . import config
from .claude_cli import CLISession
from .circuit_breaker import CircuitBreaker, create_circuit_breaker
from .complexity import ComplexityLevel, estimate_complexity
from .hooks import QUALITY_HOOKS, create_test_hooks
from .models import ExecutionMode, ExecutionResult, LoopConfig
from .loops import single_turn, moderate_loop, full_loop
from .models import ExecutionMode, ExecutionResult, LoopConfig
from .rate_limiter import RateLimiter, get_rate_limiter


# Map complexity levels to execution modes
Expand All @@ -39,40 +41,58 @@ async def execute(
goal: str,
mode: ExecutionMode | None = None,
context: str = "",
run_tests: bool = True,
test_command: list[str] | None = None,
max_iterations: int | None = None,
calls_per_hour: int | None = None,
session: CLISession | None = None,
circuit_breaker: CircuitBreaker | None = None,
logger: logging.Logger | None = None,
) -> ExecutionResult:
"""
Execute a task with appropriate complexity handling.

Uses Claude Code CLI, piggybacking on the user's existing
session and credits. No separate API key required.

Args:
project_dir: Path to the project directory
goal: Task description
mode: Execution mode (None = auto-detect from complexity)
context: Additional context (crash logs, etc.)
run_tests: Run tests after code changes (default: True)
test_command: Custom test command (auto-detected if None)
max_iterations: Maximum loop iterations for MODERATE/FULL modes (None = infinite)
calls_per_hour: Rate limit for API calls (default: 100)
session: Optional CLI session for continuity across calls
circuit_breaker: Optional circuit breaker for stuck loop detection
logger: Optional logger

Returns:
ExecutionResult with success status and output

Features:
- Claude Code session piggybacking (unified billing)
- Adaptive complexity routing
- Circuit breaker warnings for stuck loops
- Rate limiting for API budget
- Dual-gate exit detection
- Session continuity via Claude Code
"""
project_dir = Path(project_dir).resolve()
log = logger or logging.getLogger("fireteam")

# Configure quality hooks
hooks = None
if run_tests:
hooks = create_test_hooks(test_command=test_command) if test_command else QUALITY_HOOKS
log.info("Quality hooks enabled")
# Initialize session for continuity
session = session or CLISession()

# Initialize rate limiter
rate_limiter = get_rate_limiter(calls_per_hour=calls_per_hour)

# Initialize circuit breaker
breaker = circuit_breaker or create_circuit_breaker()

# Auto-detect mode if not specified
if mode is None:
log.info("Estimating task complexity...")
complexity = await estimate_complexity(goal, context, project_dir=project_dir)
complexity = await estimate_complexity(
goal, context, project_dir=project_dir, session=session
)
mode = COMPLEXITY_TO_MODE[complexity]
log.info(f"Complexity: {complexity.value} -> Mode: {mode.value}")

Expand All @@ -81,23 +101,42 @@ async def execute(

# Dispatch based on mode
if mode == ExecutionMode.SINGLE_TURN:
return await single_turn(project_dir, goal, context, hooks, log)
return await single_turn(
project_dir, goal, context,
session=session,
rate_limiter=rate_limiter,
log=log,
)

elif mode == ExecutionMode.MODERATE:
cfg = LoopConfig(
max_iterations=effective_max_iterations,
parallel_reviewers=1,
majority_required=1,
)
return await moderate_loop(project_dir, goal, context, hooks, cfg, log)
return await moderate_loop(
project_dir, goal, context,
session=session,
rate_limiter=rate_limiter,
circuit_breaker=breaker,
cfg=cfg,
log=log,
)

elif mode == ExecutionMode.FULL:
cfg = LoopConfig(
max_iterations=effective_max_iterations,
parallel_reviewers=3,
majority_required=2,
)
return await full_loop(project_dir, goal, context, hooks, cfg, log)
return await full_loop(
project_dir, goal, context,
session=session,
rate_limiter=rate_limiter,
circuit_breaker=breaker,
cfg=cfg,
log=log,
)

else:
return ExecutionResult(success=False, mode=mode, error=f"Unknown mode: {mode}")
Loading