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
56 changes: 53 additions & 3 deletions src/vibecore/context.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Optional, Protocol, runtime_checkable

from vibecore.tools.python.manager import PythonExecutionManager
from vibecore.tools.todo.manager import TodoManager
Expand All @@ -10,8 +10,53 @@
from vibecore.tools.path_validator import PathValidator


@runtime_checkable
class BasicToolContext(Protocol):
"""Context that does not impose any additional requirements."""

...


@runtime_checkable
class TodoToolContext(Protocol):
"""Context required by todo tools."""

todo_manager: TodoManager


@runtime_checkable
class PythonToolContext(Protocol):
"""Context required by Python execution tools."""

python_manager: PythonExecutionManager


@runtime_checkable
class PathValidatedContext(Protocol):
"""Context that provides a path validator for file-system tools."""

path_validator: "PathValidator"


@runtime_checkable
class AppAwareContext(Protocol):
"""Context that exposes the optional Textual app for streaming updates."""

app: Optional["VibecoreApp"]


@runtime_checkable
class VibecoreContext(TodoToolContext, PythonToolContext, PathValidatedContext, AppAwareContext, Protocol):
"""Protocol describing the full context required by Vibecore agents."""

allowed_directories: list[Path]

def reset_state(self) -> None:
"""Reset state between sessions."""


@dataclass
class VibecoreContext:
class DefaultVibecoreContext:
todo_manager: TodoManager = field(default_factory=TodoManager)
python_manager: PythonExecutionManager = field(default_factory=PythonExecutionManager)
app: Optional["VibecoreApp"] = None
Expand All @@ -20,7 +65,7 @@ class VibecoreContext:
allowed_directories: list[Path] = field(default_factory=list)
path_validator: "PathValidator" = field(init=False) # Always initialized, never None

def __post_init__(self):
def __post_init__(self) -> None:
"""Initialize path validator with allowed directories."""
from vibecore.tools.path_validator import PathValidator

Expand Down Expand Up @@ -55,3 +100,8 @@ def reset_state(self) -> None:
from vibecore.tools.path_validator import PathValidator

self.path_validator = PathValidator(self.allowed_directories)


if TYPE_CHECKING:
# Ensure DefaultVibecoreContext conforms to the VibecoreContext protocol for static analyzers
_default_context: VibecoreContext = DefaultVibecoreContext()
4 changes: 2 additions & 2 deletions src/vibecore/flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from textual.pilot import Pilot
from typing_extensions import TypeVar

from vibecore.context import VibecoreContext
from vibecore.context import DefaultVibecoreContext
from vibecore.main import AppIsExiting, VibecoreApp
from vibecore.session import JSONLSession
from vibecore.settings import settings
Expand Down Expand Up @@ -265,7 +265,7 @@ async def run(self, inputs: list[str] | None = None, shutdown: bool = False) ->

class Vibecore(Generic[TWorkflowReturn]):
def __init__(self, disable_user_input: bool = True) -> None:
self.context = VibecoreContext()
self.context = DefaultVibecoreContext()
self.workflow_logic: DecoratedCallable[TWorkflowReturn] | None = None
self.disable_user_input = disable_user_input
self.runner: VibecoreRunnerBase[TWorkflowReturn] = VibecoreRunnerBase(self)
Expand Down
18 changes: 13 additions & 5 deletions src/vibecore/tools/file/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

from agents import RunContextWrapper

from vibecore.context import VibecoreContext
from vibecore.context import PathValidatedContext
from vibecore.settings import settings
from vibecore.tools.file.utils import PathValidationError

from .utils import format_line_with_number


async def read_file(
ctx: RunContextWrapper[VibecoreContext], file_path: str, offset: int | None = None, limit: int | None = None
ctx: RunContextWrapper[PathValidatedContext], file_path: str, offset: int | None = None, limit: int | None = None
) -> str:
"""Read a file and return its contents in cat -n format.

Expand Down Expand Up @@ -100,7 +100,11 @@ async def read_file(


async def edit_file(
ctx: RunContextWrapper[VibecoreContext], file_path: str, old_string: str, new_string: str, replace_all: bool = False
ctx: RunContextWrapper[PathValidatedContext],
file_path: str,
old_string: str,
new_string: str,
replace_all: bool = False,
) -> str:
"""Edit a file by replacing strings.

Expand Down Expand Up @@ -182,7 +186,11 @@ async def edit_file(
return f"Error: Unexpected error editing file: {e}"


async def multi_edit_file(ctx: RunContextWrapper[VibecoreContext], file_path: str, edits: list[dict[str, Any]]) -> str:
async def multi_edit_file(
ctx: RunContextWrapper[PathValidatedContext],
file_path: str,
edits: list[dict[str, Any]],
) -> str:
"""Edit a file by applying multiple replacements sequentially.

Args:
Expand Down Expand Up @@ -270,7 +278,7 @@ async def multi_edit_file(ctx: RunContextWrapper[VibecoreContext], file_path: st
return f"Error: Unexpected error editing file: {e}"


async def write_file(ctx: RunContextWrapper[VibecoreContext], file_path: str, content: str) -> str:
async def write_file(ctx: RunContextWrapper[PathValidatedContext], file_path: str, content: str) -> str:
"""Write content to a file.

Args:
Expand Down
10 changes: 5 additions & 5 deletions src/vibecore/tools/file/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from agents import RunContextWrapper, function_tool
from pydantic import BaseModel

from vibecore.context import VibecoreContext
from vibecore.context import PathValidatedContext

from .executor import edit_file, multi_edit_file, read_file, write_file

Expand All @@ -18,7 +18,7 @@ class EditOperation(BaseModel):

@function_tool
async def read(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
file_path: str,
offset: int | None = None,
limit: int | None = None,
Expand Down Expand Up @@ -54,7 +54,7 @@ async def read(

@function_tool
async def edit(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
file_path: str,
old_string: str,
new_string: str,
Expand Down Expand Up @@ -91,7 +91,7 @@ async def edit(

@function_tool
async def multi_edit(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
file_path: str,
edits: list[EditOperation],
) -> str:
Expand Down Expand Up @@ -158,7 +158,7 @@ async def multi_edit(

@function_tool
async def write(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
file_path: str,
content: str,
) -> str:
Expand Down
4 changes: 2 additions & 2 deletions src/vibecore/tools/python/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from agents import RunContextWrapper

from vibecore.context import VibecoreContext
from vibecore.context import PythonToolContext

try:
from PIL import Image # type: ignore[import-not-found]
Expand All @@ -16,7 +16,7 @@
TERM_IMAGE_AVAILABLE = False


async def execute_python_helper(ctx: RunContextWrapper[VibecoreContext], code: str) -> str:
async def execute_python_helper(ctx: RunContextWrapper[PythonToolContext], code: str) -> str:
"""Helper function to execute Python code.

This is the actual implementation extracted from the tool decorator.
Expand Down
4 changes: 2 additions & 2 deletions src/vibecore/tools/python/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from agents import RunContextWrapper, function_tool

from vibecore.context import VibecoreContext
from vibecore.context import PythonToolContext

from .helpers import execute_python_helper


@function_tool
async def execute_python(ctx: RunContextWrapper[VibecoreContext], code: str) -> str:
async def execute_python(ctx: RunContextWrapper[PythonToolContext], code: str) -> str:
"""Execute Python code with persistent context across the session.

The execution environment maintains state between calls, allowing you to:
Expand Down
10 changes: 5 additions & 5 deletions src/vibecore/tools/shell/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

from agents import RunContextWrapper

from vibecore.context import VibecoreContext
from vibecore.context import PathValidatedContext
from vibecore.settings import settings
from vibecore.tools.file.utils import PathValidationError, validate_file_path


async def bash_executor(
ctx: RunContextWrapper[VibecoreContext], command: str, timeout: int | None = None
ctx: RunContextWrapper[PathValidatedContext], command: str, timeout: int | None = None
) -> tuple[str, int]:
"""Execute a bash command asynchronously.

Expand Down Expand Up @@ -80,7 +80,7 @@ async def bash_executor(
return f"Error executing command: {e}", 1


async def glob_files(ctx: RunContextWrapper[VibecoreContext], pattern: str, path: str | None = None) -> list[str]:
async def glob_files(ctx: RunContextWrapper[PathValidatedContext], pattern: str, path: str | None = None) -> list[str]:
"""Find files matching a glob pattern.

Args:
Expand Down Expand Up @@ -142,7 +142,7 @@ async def glob_files(ctx: RunContextWrapper[VibecoreContext], pattern: str, path


async def grep_files(
ctx: RunContextWrapper[VibecoreContext], pattern: str, path: str | None = None, include: str | None = None
ctx: RunContextWrapper[PathValidatedContext], pattern: str, path: str | None = None, include: str | None = None
) -> list[str]:
"""Search file contents using regular expressions.

Expand Down Expand Up @@ -226,7 +226,7 @@ async def grep_files(


async def list_directory(
ctx: RunContextWrapper[VibecoreContext], path: str, ignore: list[str] | None = None
ctx: RunContextWrapper[PathValidatedContext], path: str, ignore: list[str] | None = None
) -> list[str]:
"""List files and directories in a given path.

Expand Down
10 changes: 5 additions & 5 deletions src/vibecore/tools/shell/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

from agents import RunContextWrapper, function_tool

from vibecore.context import VibecoreContext
from vibecore.context import PathValidatedContext

from .executor import bash_executor, glob_files, grep_files, list_directory


@function_tool
async def bash(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
command: str,
timeout: int | None = None,
description: str | None = None,
Expand Down Expand Up @@ -66,7 +66,7 @@ async def bash(

@function_tool
async def glob(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
pattern: str,
path: str | None = None,
) -> str:
Expand Down Expand Up @@ -98,7 +98,7 @@ async def glob(

@function_tool
async def grep(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
pattern: str,
path: str | None = None,
include: str | None = None,
Expand Down Expand Up @@ -132,7 +132,7 @@ async def grep(

@function_tool
async def ls(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[PathValidatedContext],
path: str,
ignore: list[str] | None = None,
) -> str:
Expand Down
6 changes: 3 additions & 3 deletions src/vibecore/tools/todo/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

from agents import RunContextWrapper, function_tool

from vibecore.context import VibecoreContext
from vibecore.context import TodoToolContext

from .models import TodoItem


@function_tool
async def todo_read(ctx: RunContextWrapper[VibecoreContext]) -> list[dict[str, Any]]:
async def todo_read(ctx: RunContextWrapper[TodoToolContext]) -> list[dict[str, Any]]:
"""Use this tool to read the current to-do list for the session. This tool should be used proactively and
frequently to ensure that you are aware of the status of the current task list. You should make use of this
tool as often as possible, especially in the following situations:
Expand Down Expand Up @@ -38,7 +38,7 @@ async def todo_read(ctx: RunContextWrapper[VibecoreContext]) -> list[dict[str, A


@function_tool
async def todo_write(ctx: RunContextWrapper[VibecoreContext], todos: list[TodoItem]) -> str:
async def todo_write(ctx: RunContextWrapper[TodoToolContext], todos: list[TodoItem]) -> str:
"""Use this tool to create and manage a structured task list for your current coding session. This helps you
track progress, organize complex tasks, and demonstrate thoroughness to the user. It also helps the user
understand the progress of the task and overall progress of their requests.
Expand Down
4 changes: 2 additions & 2 deletions src/vibecore/tools/webfetch/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

from agents import RunContextWrapper, function_tool

from vibecore.context import VibecoreContext
from vibecore.context import BasicToolContext

from .executor import fetch_url
from .models import WebFetchParams


@function_tool
async def webfetch(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[BasicToolContext],
url: str,
timeout: int = 30,
follow_redirects: bool = True,
Expand Down
4 changes: 2 additions & 2 deletions src/vibecore/tools/websearch/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

from agents import RunContextWrapper, function_tool

from vibecore.context import VibecoreContext
from vibecore.context import BasicToolContext

from .executor import perform_websearch
from .models import SearchParams


@function_tool
async def websearch(
ctx: RunContextWrapper[VibecoreContext],
ctx: RunContextWrapper[BasicToolContext],
query: str,
max_results: int = 5,
region: str | None = None,
Expand Down
Loading