← back to jshudzina__PitLane-AI

Function bodies 217 total

All specs Real LLM only Function bodies
F1Agent.__init__ method · python · L44-L76 (33 LOC)
packages/pitlane-agent/src/pitlane_agent/agent.py
    def __init__(
        self,
        workspace_id: str | None = None,
        workspace_dir: Path | None = None,
        enable_tracing: bool | None = None,
        inject_temporal_context: bool = True,
    ):
        """Initialize the F1 agent.

        Args:
            workspace_id: Workspace identifier. Auto-generated if None.
            workspace_dir: Explicit workspace path. Derived from workspace_id if None.
            enable_tracing: Enable OpenTelemetry tracing. If None, uses PITLANE_TRACING_ENABLED env var.
            inject_temporal_context: Enable temporal context in system prompt. Default True.
        """
        self.workspace_id = workspace_id or generate_workspace_id()
        self.workspace_dir = workspace_dir or get_workspace_path(self.workspace_id)
        self.inject_temporal_context = inject_temporal_context
        self._agent_session_id: str | None = None  # Captured from Claude SDK

        # Verify workspace exists or create it
        if not self.worksp
F1Agent.chat method · python · L101-L180 (80 LOC)
packages/pitlane-agent/src/pitlane_agent/agent.py
    async def chat(self, message: str, resume_session_id: str | None = None) -> AsyncIterator[str]:
        """Process a chat message and yield response text chunks.

        Args:
            message: The user's question or message.
            resume_session_id: Optional SDK session ID to resume a previous conversation.

        Yields:
            Text chunks from the assistant's response.
        """
        import os

        # Set workspace ID as environment variable so skills can access it
        os.environ["PITLANE_WORKSPACE_ID"] = self.workspace_id

        # Configure hooks for tracing
        hooks = None
        if tracing.is_tracing_enabled():
            hooks = {
                "PreToolUse": [HookMatcher(matcher=None, hooks=[tracing.pre_tool_use_hook])],
                "PostToolUse": [HookMatcher(matcher=None, hooks=[tracing.post_tool_use_hook])],
            }

        # Create a wrapper for can_use_tool that has access to workspace context
        workspace_dir = st
F1Agent.chat_full method · python · L182-L195 (14 LOC)
packages/pitlane-agent/src/pitlane_agent/agent.py
    async def chat_full(self, message: str, resume_session_id: str | None = None) -> str:
        """Process a chat message and return the full response.

        Args:
            message: The user's question or message.
            resume_session_id: Optional SDK session ID to resume a previous conversation.

        Returns:
            The complete response text.
        """
        parts = []
        async for chunk in self.chat(message, resume_session_id=resume_session_id):
            parts.append(chunk)
        return "\n".join(parts) if parts else ""
validate_mutually_exclusive function · python · L31-L45 (15 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def validate_mutually_exclusive(ctx, param, value):
    """Validate that --drivers and --top-n are mutually exclusive."""
    # Get the other parameter's value from context
    if param.name == "drivers":
        other_value = ctx.params.get("top_n")
    elif param.name == "top_n":
        other_value = ctx.params.get("drivers")
    else:
        return value

    # Check if both are provided
    if value and other_value:
        raise click.BadParameter("Cannot specify both --drivers and --top-n options", ctx=ctx, param=param)

    return value
lap_times function · python · L67-L100 (34 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def lap_times(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
    drivers: tuple[str, ...],
):
    """Generate lap times chart for specified drivers."""
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        result = generate_lap_times_chart(
            year=year,
            gp=gp,
            session_type=session,
            drivers=list(drivers),
            workspace_dir=workspace_path,
            test_number=test_number,
            session_number=session_number,
        )
        result["workspace_id"] = workspace_id
        click.echo(json.dumps(result, indent=2))

    except Exception as e:
        click.echo(json.dum
lap_times_distribution function · python · L116-L150 (35 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def lap_times_distribution(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
    drivers: tuple[str, ...],
):
    """Generate lap times distribution chart showing statistical spread."""
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        drivers_list = list(drivers) if drivers else None
        result = generate_lap_times_distribution_chart(
            year=year,
            gp=gp,
            session_type=session,
            drivers=drivers_list,
            workspace_dir=workspace_path,
            test_number=test_number,
            session_number=session_number,
        )
        result["workspace_id"] = workspace_id
   
tyre_strategy function · python · L165-L199 (35 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def tyre_strategy(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
):
    """Generate tyre strategy visualization for a race."""
    # Default session to "R" when only --gp is provided (backwards compat)
    if gp is not None and session is None and test_number is None:
        session = "R"
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        result = generate_tyre_strategy_chart(
            year=year,
            gp=gp,
            session_type=session,
            workspace_dir=workspace_path,
            test_number=test_number,
            session_number=session_number,
        )
        result["workspace_id"] = workspace
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
speed_trace function · python · L221-L263 (43 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def speed_trace(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
    drivers: tuple[str, ...],
    annotate_corners: bool,
):
    """Generate speed trace comparison for fastest laps of specified drivers."""
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    if len(drivers) < 2:
        click.echo(json.dumps({"error": "Speed trace requires at least 2 drivers for comparison"}), err=True)
        sys.exit(1)
    if len(drivers) > 5:
        click.echo(json.dumps({"error": "Speed trace supports maximum 5 drivers for readability"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        result = generate_speed_trace_chart(
            year=year,
    
telemetry function · python · L285-L327 (43 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def telemetry(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
    drivers: tuple[str, ...],
    annotate_corners: bool,
):
    """Generate interactive telemetry chart (speed, RPM, gear, throttle, brake) for fastest laps."""
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    if len(drivers) < 2:
        click.echo(json.dumps({"error": "Telemetry requires at least 2 drivers for comparison"}), err=True)
        sys.exit(1)
    if len(drivers) > 5:
        click.echo(json.dumps({"error": "Telemetry supports maximum 5 drivers for readability"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        result = generate_telemetry_chart(
            yea
position_changes function · python · L351-L387 (37 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def position_changes(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
    drivers: tuple[str, ...],
    top_n: int | None,
):
    """Generate position changes chart showing driver positions throughout the race."""
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        drivers_list = list(drivers) if drivers else None
        result = generate_position_changes_chart(
            year=year,
            gp=gp,
            session_type=session,
            drivers=drivers_list,
            top_n=top_n,
            workspace_dir=workspace_path,
            test_number=test_number,
            session_number=session_number,
        )
 
track_map function · python · L397-L428 (32 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def track_map(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
):
    """Generate track map with numbered corner labels."""
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        result = generate_track_map_chart(
            year=year,
            gp=gp,
            session_type=session,
            workspace_dir=workspace_path,
            test_number=test_number,
            session_number=session_number,
        )
        result["workspace_id"] = workspace_id
        click.echo(json.dumps(result, indent=2))

    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
gear_shifts_map function · python · L444-L477 (34 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def gear_shifts_map(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
    drivers: tuple[str, ...],
):
    """Generate gear shift visualization on track map."""
    validate_session_or_test(gp, session, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        result = generate_gear_shifts_map_chart(
            year=year,
            gp=gp,
            session_type=session,
            drivers=list(drivers),
            workspace_dir=workspace_path,
            test_number=test_number,
            session_number=session_number,
        )
        result["workspace_id"] = workspace_id
        click.echo(json.dumps(result, indent=2))

    except Exception as e:
        click.e
championship_possibilities function · python · L495-L523 (29 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def championship_possibilities(workspace_id: str, year: int, championship: str, after_round: int | None):
    """Calculate who can still mathematically win the championship."""
    # Verify workspace exists
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        # Generate chart
        result = generate_championship_possibilities_chart(
            year=year,
            championship=championship,
            workspace_dir=workspace_path,
            after_round=after_round,
        )

        # Add session info to result
        result["workspace_id"] = workspace_id

        click.echo(json.dumps(result, indent=2))

    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
multi_lap function · python · L553-L623 (71 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def multi_lap(
    workspace_id: str,
    year: int,
    gp: str | None,
    session_type: str | None,
    test_number: int | None,
    session_number: int | None,
    driver: str,
    laps: tuple[str, ...],
    annotate_corners: bool,
):
    """Compare multiple laps for a single driver within a session.

    Each --lap value is either 'best' (fastest lap) or an integer lap number.
    Useful for comparing a driver's Q1/Q3 attempts, or stint pace across a race.

    Example (GP session):
      pitlane analyze multi-lap --year 2024 --gp Monaco --session Q
        --driver VER --lap best --lap 3

    Example (testing session):
      pitlane analyze multi-lap --year 2024 --test 1 --day 2
        --driver VER --lap best --lap 3
    """
    validate_session_or_test(gp, session_type, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

   
year_compare function · python · L657-L711 (55 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def year_compare(
    workspace_id: str,
    gp: str | None,
    session_type: str | None,
    test_number: int | None,
    session_number: int | None,
    driver: str,
    years: tuple[int, ...],
    annotate_corners: bool,
):
    """Compare a driver's best lap at the same track across multiple seasons.

    Useful for analysing the impact of regulation changes on lap time, braking,
    speed profiles, and driving technique across eras.

    Example (GP session):
      pitlane analyze year-compare --gp Monza --session Q
        --driver VER --years 2022 --years 2024

    Example (testing session):
      pitlane analyze year-compare --test 1 --day 2
        --driver VER --years 2022 --years 2024
    """
    validate_session_or_test(gp, session_type, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    if len(years) < 2:
        c
If a scraper extracted this row, it came from Repobility (https://repobility.com)
driver_laps function · python · L728-L769 (42 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def driver_laps(
    workspace_id: str,
    year: int,
    gp: str | None,
    session_type: str | None,
    test_number: int | None,
    session_number: int | None,
    driver: str,
):
    """Fetch per-lap data for a single driver — no chart generated.

    Returns structured JSON with lap times, tyre compounds, stint numbers, pit
    events, position, and whether each lap is race-representative (is_accurate).
    Use this before multi-lap to identify which lap numbers are worth comparing.

    Example (GP session):
      pitlane analyze driver-laps --year 2024 --gp Monaco --session R --driver VER

    Example (testing session):
      pitlane analyze driver-laps --year 2024 --test 1 --day 2 --driver VER
    """
    validate_session_or_test(gp, session_type, test_number, session_number)

    if not workspace_exists(workspace_id):
        click.echo(json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}), err=True)
        sys.exit(1)

    try:
        result
season_summary function · python · L782-L812 (31 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_analyze.py
def season_summary(workspace_id: str, year: int, summary_type: str):
    """Generate season summary visualization with per-round championship points.

    Loads results for each completed race (and sprint) via FastF1 and produces
    an interactive two-panel Plotly heatmap saved as an HTML file.

    Example:
      pitlane analyze season-summary --workspace-id $PITLANE_WORKSPACE_ID --year 2024
      pitlane analyze season-summary --workspace-id $PITLANE_WORKSPACE_ID --year 2024 --type constructors
    """
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        result = generate_season_summary_chart(
            year=year,
            summary_type=summary_type,
            workspace_dir=workspace_path,
        )
        result["workspace_id"] = workspace_id
    
_validate_standings_request function · python · L28-L58 (31 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def _validate_standings_request(workspace_id: str, year: int) -> Path:
    """Validate workspace and year for standings fetch commands.

    Args:
        workspace_id: Workspace ID
        year: Championship year

    Returns:
        Path to workspace directory

    Raises:
        SystemExit: If validation fails
    """
    # Verify workspace exists
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    # Validate year
    current_year = datetime.now().year
    if year < MIN_F1_YEAR or year > current_year + 2:
        click.echo(
            json.dumps({"error": f"Year must be between {MIN_F1_YEAR} and {current_year + 2}"}),
            err=True,
        )
        sys.exit(1)

    return get_workspace_path(workspace_id)
session_info function · python · L81-L136 (56 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def session_info(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
):
    """Fetch session information and store in workspace."""
    validate_session_or_test(gp, session, test_number, session_number)

    # Verify workspace exists
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        # Fetch session info
        info = get_session_info(
            year,
            gp=gp,
            session_type=session,
            test_number=test_number,
            session_number=session_number,
        )

        # Write to workspace
        output_file = build_data_path(
            workspace_path,
            "session_info",
            year=year,
            gp=gp,
      
driver_info function · python · L165-L217 (53 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def driver_info(
    workspace_id: str,
    driver_code: str | None,
    season: int | None,
    limit: int,
    offset: int,
):
    """Fetch driver information and store in workspace."""
    # Verify workspace exists
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    # Validate season if provided
    if season is not None:
        current_year = datetime.now().year
        if season < 1950 or season > current_year + 2:
            click.echo(
                json.dumps({"error": f"Season must be between 1950 and {current_year + 2}"}),
                err=True,
            )
            sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        # Fetch driver info
        info = get_driver_info(
            driver_code=driver_code,
            season=season,
            limit=limit,
            of
event_schedule function · python · L246-L297 (52 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def event_schedule(
    workspace_id: str,
    year: int,
    round_number: int | None,
    country: str | None,
    include_testing: bool,
):
    """Fetch event schedule and store in workspace."""
    # Verify workspace exists
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    # Validate year input
    current_year = datetime.now().year
    if year < 1950 or year > current_year + 2:
        click.echo(
            json.dumps({"error": f"Year must be between 1950 and {current_year + 2}"}),
            err=True,
        )
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        # Fetch schedule
        schedule = get_event_schedule(
            year,
            round_number=round_number,
            country=country,
            include_testing=include_testing,
        )

        # Write 
driver_standings function · python · L310-L336 (27 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def driver_standings(workspace_id: str, year: int, round_number: int | None):
    """Fetch driver championship standings and store in workspace."""
    # Validate request
    workspace_path = _validate_standings_request(workspace_id, year)

    try:
        # Fetch standings
        standings = get_driver_standings(year, round_number)

        # Write to workspace
        output_file = build_data_path(workspace_path, "driver_standings", year=year, round_number=round_number)

        with open(output_file, "w") as f:
            json.dump(standings, f, indent=2)

        # Return result
        result = {
            "data_file": str(output_file),
            "year": year,
            "round": standings["round"],
            "total_standings": standings["total_standings"],
        }
        click.echo(json.dumps(result, indent=2))

    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
constructor_standings function · python · L349-L375 (27 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def constructor_standings(workspace_id: str, year: int, round_number: int | None):
    """Fetch constructor championship standings and store in workspace."""
    # Validate request
    workspace_path = _validate_standings_request(workspace_id, year)

    try:
        # Fetch standings
        standings = get_constructor_standings(year, round_number)

        # Write to workspace
        output_file = build_data_path(workspace_path, "constructor_standings", year=year, round_number=round_number)

        with open(output_file, "w") as f:
            json.dump(standings, f, indent=2)

        # Return result
        result = {
            "data_file": str(output_file),
            "year": year,
            "round": standings["round"],
            "total_standings": standings["total_standings"],
        }
        click.echo(json.dumps(result, indent=2))

    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
race_control function · python · L434-L506 (73 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def race_control(
    workspace_id: str,
    year: int,
    gp: str | None,
    session: str | None,
    test_number: int | None,
    session_number: int | None,
    detail: str,
    category: str | None,
    flag_type: str | None,
    driver: str | None,
    lap_start: int | None,
    lap_end: int | None,
    sector: int | None,
):
    """Fetch race control messages and store in workspace."""
    validate_session_or_test(gp, session, test_number, session_number)

    # Verify workspace exists
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    workspace_path = get_workspace_path(workspace_id)

    try:
        # Fetch race control messages
        messages = get_race_control_messages(
            year=year,
            gp=gp,
            session_type=session,
            detail=detail,
            category=category,
      
season_summary function · python · L512-L534 (23 LOC)
packages/pitlane-agent/src/pitlane_agent/cli_fetch.py
def season_summary(workspace_id: str, year: int):
    """Fetch season summary with races ranked by wildness score."""
    workspace_path = _validate_standings_request(workspace_id, year)

    try:
        summary = get_season_summary(year)

        # Write to workspace
        output_file = build_data_path(workspace_path, "season_summary", year=year)
        with open(output_file, "w") as f:
            json.dump(summary, f, indent=2)

        # Return result
        result = {
            "data_file": str(output_file),
            "year": year,
            "total_races": summary["total_races"],
        }
        click.echo(json.dumps(result, indent=2))

    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
pitlane function · python · L32-L37 (6 LOC)
packages/pitlane-agent/src/pitlane_agent/cli.py
def pitlane():
    """PitLane AI - F1 data analysis tools powered by FastF1 and Claude."""
    # Suppress logging from underlying libraries
    logging.getLogger("fastf1").setLevel(logging.WARNING)
    # Suppress deprecation warnings from FastF1
    warnings.filterwarnings("ignore", category=FutureWarning, module="fastf1")
create function · python · L49-L59 (11 LOC)
packages/pitlane-agent/src/pitlane_agent/cli.py
def create(workspace_id: str | None, description: str | None):
    """Create a new workspace."""
    try:
        result = create_workspace(workspace_id=workspace_id, description=description)
        click.echo(json.dumps(result, indent=2))
    except ValueError as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
list function · python · L64-L75 (12 LOC)
packages/pitlane-agent/src/pitlane_agent/cli.py
def list(show_all: bool):
    """List workspaces."""
    try:
        workspaces = list_workspaces(show_all=show_all)
        result = {
            "total": len(workspaces),
            "workspaces": workspaces,
        }
        click.echo(json.dumps(result, indent=2))
    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
info function · python · L80-L90 (11 LOC)
packages/pitlane-agent/src/pitlane_agent/cli.py
def info(workspace_id: str):
    """Show workspace information."""
    try:
        info_data = get_workspace_info(workspace_id)
        click.echo(json.dumps(info_data, indent=2))
    except ValueError as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
clean function · python · L102-L124 (23 LOC)
packages/pitlane-agent/src/pitlane_agent/cli.py
def clean(older_than: int | None, all_workspaces: bool, yes: bool):
    """Clean old workspaces."""
    if not all_workspaces and older_than is None:
        click.echo(
            json.dumps({"error": "Must specify either --older-than or --all"}),
            err=True,
        )
        sys.exit(1)

    # Confirmation prompt
    if not yes:
        message = "Remove ALL workspaces?" if all_workspaces else f"Remove workspaces older than {older_than} days?"

        if not click.confirm(message):
            click.echo(json.dumps({"message": "Cancelled"}))
            return

    try:
        result = clean_workspaces(older_than_days=older_than, remove_all=all_workspaces)
        click.echo(json.dumps(result, indent=2))
    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
remove function · python · L130-L149 (20 LOC)
packages/pitlane-agent/src/pitlane_agent/cli.py
def remove(workspace_id: str, yes: bool):
    """Remove a specific workspace."""
    if not workspace_exists(workspace_id):
        click.echo(
            json.dumps({"error": f"Workspace does not exist for workspace ID: {workspace_id}"}),
            err=True,
        )
        sys.exit(1)

    # Confirmation prompt
    if not yes and not click.confirm(f"Remove workspace {workspace_id}?"):
        click.echo(json.dumps({"message": "Cancelled"}))
        return

    try:
        remove_workspace(workspace_id)
        click.echo(json.dumps({"message": f"Workspace {workspace_id} removed successfully"}))
    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
Source: Repobility analyzer · https://repobility.com
temporal_context function · python · L166-L191 (26 LOC)
packages/pitlane-agent/src/pitlane_agent/cli.py
def temporal_context(format: str, refresh: bool, verbosity: str):
    """Show current F1 temporal context.

    This command displays the current state of the F1 season including:
    - Current season year and phase (pre/in/post/off-season)
    - Active race weekend (if any)
    - Current or recent sessions
    - Next upcoming race
    - Last completed race
    """
    try:
        context = get_temporal_context(force_refresh=refresh)

        if format == "json":
            click.echo(json.dumps(context.to_dict(), indent=2, default=str))
        elif format == "prompt":
            prompt_text = format_for_system_prompt(context, verbosity=verbosity)
            click.echo(prompt_text)
        else:
            # Human-readable text format
            text_output = format_as_text(context)
            click.echo(text_output)

    except Exception as e:
        click.echo(json.dumps({"error": str(e)}), err=True)
        sys.exit(1)
_count_remaining_races function · python · L33-L63 (31 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/championship_possibilities.py
def _count_remaining_races(year: int, current_round: int) -> tuple[int, int]:
    """Count remaining races and sprint races in the season.

    Args:
        year: Championship year
        current_round: Current round number

    Returns:
        Tuple of (remaining_races, remaining_sprints)
    """
    schedule_data = get_event_schedule(year, include_testing=False)
    events = schedule_data["events"]

    remaining_races = 0
    remaining_sprints = 0

    for event in events:
        round_num = event["round"]
        # Only count races after the current round
        if round_num > current_round:
            # Check if it's a race weekend (has Race session)
            has_race = any(session["name"] == "Race" for session in event.get("sessions", []))
            if has_race:
                remaining_races += 1

            # Check if it's a sprint weekend (has Sprint session)
            has_sprint = any(session["name"] == "Sprint" for session in event.get("sessions", []))
       
_calculate_max_points_available function · python · L66-L85 (20 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/championship_possibilities.py
def _calculate_max_points_available(remaining_races: int, remaining_sprints: int, year: int) -> int:
    """Calculate maximum points available for remaining races.

    Args:
        remaining_races: Number of remaining races
        remaining_sprints: Number of remaining sprint races
        year: Championship year (affects whether fastest lap point is included)

    Returns:
        Maximum points available
    """
    # Fastest lap point was removed starting in 2025
    if year >= FASTEST_LAP_POINT_REMOVED_YEAR:
        max_race_points = MAX_RACE_POINTS_PER_WEEKEND_NO_FASTEST_LAP * remaining_races
    else:
        max_race_points = MAX_RACE_POINTS_PER_WEEKEND_WITH_FASTEST_LAP * remaining_races

    max_sprint_points = MAX_SPRINT_POINTS * remaining_sprints

    return max_race_points + max_sprint_points
_calculate_championship_scenarios function · python · L88-L156 (69 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/championship_possibilities.py
def _calculate_championship_scenarios(
    standings: list[dict],
    max_points_available: int,
    championship_type: str,
) -> tuple[list[dict], dict]:
    """Calculate championship scenarios for each competitor.

    Args:
        standings: List of standings dictionaries
        max_points_available: Maximum points available in remaining races
        championship_type: "drivers" or "constructors"

    Returns:
        Tuple of (competitor_stats, leader_info)
    """
    if not standings:
        return [], {}

    # Ensure standings are sorted by points descending (API should provide sorted data,
    # but we verify to ensure correct leader identification and points calculations)
    standings = sorted(standings, key=lambda x: x["points"], reverse=True)

    leader = standings[0]
    leader_points = leader["points"]

    # Get name field based on championship type
    name_field = "full_name" if championship_type == "drivers" else "constructor_name"

    leader_info = {
        "
_generate_championship_chart function · python · L159-L238 (80 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/championship_possibilities.py
def _generate_championship_chart(
    competitor_stats: list[dict],
    year: int,
    championship_type: str,
    ax: plt.Axes,
    after_round: int | None = None,
) -> None:
    """Generate horizontal bar chart for championship possibilities.

    Args:
        competitor_stats: List of competitor statistics
        year: Championship year
        championship_type: "drivers" or "constructors"
        ax: Matplotlib axes to plot on
        after_round: Optional round number for historical analysis
    """
    # Sort by current points for display
    competitor_stats = sorted(competitor_stats, key=lambda x: x["current_points"], reverse=False)

    names = [c["name"] for c in competitor_stats]
    current_points = [c["current_points"] for c in competitor_stats]
    max_points = [c["max_possible_points"] for c in competitor_stats]
    can_win = [c["can_win"] for c in competitor_stats]

    y_positions = np.arange(len(names))

    # Plot current points (filled bars)
    for i, (y_pos, cu
generate_championship_possibilities_chart function · python · L241-L328 (88 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/championship_possibilities.py
def generate_championship_possibilities_chart(
    year: int,
    championship: str = "drivers",
    workspace_dir: Path | None = None,
    after_round: int | None = None,
) -> dict:
    """Generate championship possibilities visualization.

    Args:
        year: Season year
        championship: Championship type - "drivers" or "constructors"
        workspace_dir: Workspace directory for outputs
        after_round: Optional round number to analyze historical "what if" scenarios

    Returns:
        Dictionary with chart metadata and championship statistics
    """
    championship = championship.lower()
    if championship not in ["drivers", "constructors"]:
        raise ValueError(f"Invalid championship type: {championship}. Must be 'drivers' or 'constructors'.")

    # Validate after_round parameter
    if after_round is not None and after_round <= 0:
        raise ValueError(f"after_round must be a positive integer, got: {after_round}")

    # Get standings (current or histor
_prepare_lap_entry function · python · L41-L88 (48 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/driver_lap_compare.py
def _prepare_lap_entry(lap, label: str, key: str, color: str) -> tuple[dict, dict]:
    """Build rendering entry and stats dict for a single lap.

    Returns:
        (entry, stats) where entry is consumed by _render_telemetry_chart and
        stats is returned to the caller for downstream use.

    Raises:
        ValueError: If car data is unavailable for the lap.
    """
    tel = lap.get_car_data().add_distance()
    if tel.empty:
        raise ValueError(f"No car telemetry data available for lap labelled '{label}'")

    tel["Brake"] = tel["Brake"].astype(int)

    analysis = analyze_telemetry(tel)

    tel["SuperClip"] = 0
    for zone in analysis["super_clipping_zones"]:
        mask = (tel["Distance"] >= zone["start_distance"]) & (tel["Distance"] <= zone["end_distance"])
        tel.loc[mask, "SuperClip"] = 1

    entry = {
        "key": key,
        "label": label,
        "telemetry": tel,
        "color": color,
        "style": _SOLID_STYLE,
    }

    stats = {
        
generate_multi_lap_chart function · python · L91-L184 (94 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/driver_lap_compare.py
def generate_multi_lap_chart(
    year: int,
    gp: str | None,
    session_type: str | None,
    driver: str,
    lap_specs: list[str | int],
    workspace_dir: Path,
    annotate_corners: bool = False,
    test_number: int | None = None,
    session_number: int | None = None,
) -> dict:
    """Compare multiple laps for a single driver within a session.

    Args:
        year: Season year
        gp: Grand Prix name (None when using testing session)
        session_type: Session identifier (Q, R, FP1, FP2, FP3, S, SQ; None when using testing session)
        driver: Driver abbreviation (e.g. "VER")
        lap_specs: List of lap specifiers — "best" for fastest lap or an integer lap number.
                   Must have between 2 and 6 entries.
        workspace_dir: Workspace directory for chart output
        annotate_corners: Whether to add corner markers
        test_number: Testing event number (mutually exclusive with gp/session_type)
        session_number: Day/session within t
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
generate_year_compare_chart function · python · L187-L288 (102 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/driver_lap_compare.py
def generate_year_compare_chart(
    gp: str | None,
    session_type: str | None,
    driver: str,
    years: list[int],
    workspace_dir: Path,
    annotate_corners: bool = False,
    test_number: int | None = None,
    session_number: int | None = None,
) -> dict:
    """Compare a driver's best lap at the same track across multiple seasons.

    Loads each session independently and picks the fastest lap for the driver.
    Circuit info is taken from the first session (same track layout assumed).

    Useful for analysing how regulation changes affect lap times, braking points,
    speed profiles, and driving technique over multiple seasons.

    Args:
        gp: Grand Prix name (same track must exist in all specified years; None for testing sessions)
        session_type: Session identifier (Q, R, FP1, FP2, FP3, S, SQ; None for testing sessions)
        driver: Driver abbreviation (e.g. "HAM")
        years: List of season years to compare. Must have between 2 and 6 entries.
     
_safe_int function · python · L17-L24 (8 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/driver_lap_list.py
def _safe_int(value) -> int | None:
    """Convert to int, returning None for NaN/NaT."""
    try:
        if pd.isna(value):
            return None
        return int(value)
    except (TypeError, ValueError):
        return None
_safe_float function · python · L27-L34 (8 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/driver_lap_list.py
def _safe_float(value) -> float | None:
    """Convert to float, returning None for NaN/NaT."""
    try:
        if pd.isna(value):
            return None
        return float(value)
    except (TypeError, ValueError):
        return None
_compute_stint_numbers function · python · L37-L63 (27 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/driver_lap_list.py
def _compute_stint_numbers(driver_laps: pd.DataFrame) -> list[int | None]:
    """Compute stint numbers from compound changes when FastF1 Stint column is absent.

    A new stint begins whenever the compound changes or a pit-out lap is detected.
    """
    stints: list[int | None] = []
    current_stint = 1
    prev_compound = None

    for _, lap in driver_laps.iterrows():
        compound = lap.get("Compound")
        pit_out = lap.get("PitOutTime")

        is_pit_out = not pd.isna(pit_out) if pit_out is not None else False
        compound_val = compound if (compound is not None and not pd.isna(compound)) else None

        if prev_compound is not None and compound_val != prev_compound:
            current_stint += 1
        elif is_pit_out and stints:
            # Pit-out on same compound (e.g. minor repair) counts as new stint
            current_stint += 1

        stints.append(current_stint)
        if compound_val is not None:
            prev_compound = compound_val

    r
generate_driver_lap_list function · python · L66-L195 (130 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/driver_lap_list.py
def generate_driver_lap_list(
    year: int,
    gp: str | None,
    session_type: str | None,
    driver: str,
    test_number: int | None = None,
    session_number: int | None = None,
) -> dict:
    """Return structured per-lap data for a single driver.

    Does not load telemetry — uses session.laps only, making this fast.

    Args:
        year: Season year
        gp: Grand Prix name (None when using testing session)
        session_type: Session identifier (Q, R, FP1, FP2, FP3, S, SQ; None for testing)
        driver: Driver abbreviation (e.g. "VER")
        test_number: Testing event number (mutually exclusive with gp/session_type)
        session_number: Day/session within testing event

    Returns:
        Dictionary with per-lap data, pit stop summary, and session metadata.

    Raises:
        ValueError: If the driver has no laps in the session.
    """
    session = load_session_or_testing(
        year, gp, session_type, telemetry=False, test_number=test_number, sessi
_rotate function · python · L31-L42 (12 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/gear_shifts_map.py
def _rotate(xy: np.ndarray, *, angle: float) -> np.ndarray:
    """Rotate 2D coordinates by the given angle in radians.

    Args:
        xy: Array-like of shape (2,) or (N, 2) with X/Y coordinates.
        angle: Rotation angle in radians.

    Returns:
        Rotated coordinates with the same shape as input.
    """
    rot_mat = np.array([[np.cos(angle), np.sin(angle)], [-np.sin(angle), np.cos(angle)]])
    return np.matmul(xy, rot_mat)
_format_lap_time function · python · L45-L57 (13 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/gear_shifts_map.py
def _format_lap_time(lap_time: pd.Timedelta) -> str:
    """Format lap time as MM:SS.mmm.

    Args:
        lap_time: Pandas Timedelta object representing lap time.

    Returns:
        Formatted lap time string (e.g., "1:23.456").
    """
    total_seconds = lap_time.total_seconds()
    minutes = int(total_seconds // 60)
    seconds = total_seconds % 60
    return f"{minutes}:{seconds:06.3f}"
_calculate_gear_statistics function · python · L60-L80 (21 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/gear_shifts_map.py
def _calculate_gear_statistics(telemetry: pd.DataFrame) -> dict:
    """Calculate gear usage statistics from telemetry.

    Args:
        telemetry: DataFrame with nGear column containing gear values.

    Returns:
        Dictionary with gear distribution, most used gear, highest gear, and total gear changes.
    """
    gear_counts = telemetry["nGear"].value_counts()
    total_points = len(telemetry)

    return {
        "gear_distribution": {
            int(gear): {"count": int(count), "percentage": round(float(count / total_points * 100), 1)}
            for gear, count in gear_counts.items()
        },
        "most_used_gear": int(gear_counts.idxmax()),
        "highest_gear": int(telemetry["nGear"].max()),
        "total_gear_changes": int((telemetry["nGear"].diff() != 0).sum()),
    }
If a scraper extracted this row, it came from Repobility (https://repobility.com)
generate_gear_shifts_map_chart function · python · L83-L274 (192 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/gear_shifts_map.py
def generate_gear_shifts_map_chart(
    year: int,
    gp: str,
    session_type: str,
    drivers: list[str],
    workspace_dir: Path,
    test_number: int | None = None,
    session_number: int | None = None,
) -> dict:
    """Generate gear shift visualization on track map for a single driver.

    Args:
        year: Season year
        gp: Grand Prix name (ignored for testing sessions)
        session_type: Session identifier (R, Q, FP1, etc., ignored for testing)
        drivers: List containing exactly 1 driver abbreviation
        workspace_dir: Workspace directory for outputs and cache
        test_number: Testing event number (e.g., 1 or 2)
        session_number: Session within testing event (e.g., 1, 2, or 3)

    Returns:
        Dictionary with chart metadata and gear statistics

    Raises:
        ValueError: If drivers list does not contain exactly 1 driver
        ValueError: If telemetry data is unavailable
    """
    # Validate driver count (exactly 1 driver require
generate_lap_times_distribution_chart function · python · L17-L185 (169 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/lap_times_distribution.py
def generate_lap_times_distribution_chart(
    year: int,
    gp: str,
    session_type: str,
    drivers: list[str] | None,
    workspace_dir: Path,
    test_number: int | None = None,
    session_number: int | None = None,
) -> dict:
    """Generate a lap times distribution plot using violin and swarm plots.

    Args:
        year: Season year
        gp: Grand Prix name (ignored for testing sessions)
        session_type: Session identifier (ignored for testing sessions)
        drivers: List of driver abbreviations to include, or None for top 10 finishers
        workspace_dir: Workspace directory for outputs and cache
        test_number: Testing event number (e.g., 1 or 2)
        session_number: Session within testing event (e.g., 1, 2, or 3)

    Returns:
        Dictionary with chart metadata and statistics
    """
    # Build output path (handle None drivers case specially)
    if drivers is None:
        output_path = build_chart_path(
            workspace_dir,
           
generate_lap_times_chart function · python · L16-L121 (106 LOC)
packages/pitlane-agent/src/pitlane_agent/commands/analyze/lap_times.py
def generate_lap_times_chart(
    year: int,
    gp: str,
    session_type: str,
    drivers: list[str],
    workspace_dir: Path,
    test_number: int | None = None,
    session_number: int | None = None,
) -> dict:
    """Generate a lap times scatter plot.

    Args:
        year: Season year
        gp: Grand Prix name (ignored for testing sessions)
        session_type: Session identifier (ignored for testing sessions)
        drivers: List of driver abbreviations to include
        workspace_dir: Workspace directory for outputs and cache
        test_number: Testing event number (e.g., 1 or 2)
        session_number: Session within testing event (e.g., 1, 2, or 3)

    Returns:
        Dictionary with chart metadata and statistics
    """
    # Build output path
    output_path = build_chart_path(
        workspace_dir,
        "lap_times",
        year,
        gp,
        session_type,
        drivers,
        test_number=test_number,
        session_number=session_number,
    )
page 1 / 5next ›