byte-ai-cli 0.1.8__py3-none-any.whl
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.
- byte/__init__.py +4 -0
- byte/bootstrap.py +91 -0
- byte/container.py +90 -0
- byte/context.py +22 -0
- byte/core/__init__.py +4 -0
- byte/core/array_store.py +82 -0
- byte/core/cli.py +22 -0
- byte/core/config/__init__.py +0 -0
- byte/core/config/config.py +81 -0
- byte/core/event_bus.py +92 -0
- byte/core/exceptions.py +19 -0
- byte/core/initializer.py +209 -0
- byte/core/logging.py +34 -0
- byte/core/mixins/__init__.py +0 -0
- byte/core/mixins/bootable.py +66 -0
- byte/core/mixins/conditionable.py +53 -0
- byte/core/mixins/configurable.py +19 -0
- byte/core/mixins/eventable.py +34 -0
- byte/core/mixins/injectable.py +28 -0
- byte/core/mixins/user_interactive.py +105 -0
- byte/core/service/base_service.py +58 -0
- byte/core/service_provider.py +103 -0
- byte/core/task_manager.py +28 -0
- byte/core/utils/__init__.py +108 -0
- byte/core/utils/save_fixture.py +27 -0
- byte/domain/__init__.py +0 -0
- byte/domain/agent/command/__init__.py +0 -0
- byte/domain/agent/command/ask_command.py +34 -0
- byte/domain/agent/implementations/__init__.py +0 -0
- byte/domain/agent/implementations/ask/__init__.py +0 -0
- byte/domain/agent/implementations/ask/agent.py +113 -0
- byte/domain/agent/implementations/ask/prompts.py +24 -0
- byte/domain/agent/implementations/base.py +168 -0
- byte/domain/agent/implementations/cleaner/__init__.py +0 -0
- byte/domain/agent/implementations/cleaner/agent.py +163 -0
- byte/domain/agent/implementations/cleaner/prompt.py +30 -0
- byte/domain/agent/implementations/coder/__init__.py +0 -0
- byte/domain/agent/implementations/coder/agent.py +116 -0
- byte/domain/agent/implementations/coder/prompts.py +30 -0
- byte/domain/agent/implementations/commit/__init__.py +0 -0
- byte/domain/agent/implementations/commit/agent.py +84 -0
- byte/domain/agent/implementations/commit/prompt.py +32 -0
- byte/domain/agent/implementations/copy/__init__.py +0 -0
- byte/domain/agent/implementations/copy/agent.py +44 -0
- byte/domain/agent/implementations/fixer/__init__.py +0 -0
- byte/domain/agent/implementations/fixer/agent.py +29 -0
- byte/domain/agent/implementations/fixer/prompts.py +30 -0
- byte/domain/agent/implementations/research/__init__.py +0 -0
- byte/domain/agent/implementations/research/agent.py +101 -0
- byte/domain/agent/implementations/research/prompts.py +37 -0
- byte/domain/agent/nodes/assistant_node.py +46 -0
- byte/domain/agent/nodes/base_node.py +12 -0
- byte/domain/agent/nodes/copy_node.py +168 -0
- byte/domain/agent/nodes/end_node.py +29 -0
- byte/domain/agent/nodes/lint_node.py +18 -0
- byte/domain/agent/nodes/parse_blocks_node.py +58 -0
- byte/domain/agent/nodes/start_node.py +34 -0
- byte/domain/agent/nodes/tool_node.py +51 -0
- byte/domain/agent/schemas.py +25 -0
- byte/domain/agent/service/agent_service.py +72 -0
- byte/domain/agent/service_provider.py +71 -0
- byte/domain/agent/state.py +57 -0
- byte/domain/analytics/service/agent_analytics_service.py +199 -0
- byte/domain/analytics/service_provider.py +44 -0
- byte/domain/cli/__init__.py +0 -0
- byte/domain/cli/config.py +16 -0
- byte/domain/cli/rich/heading.py +0 -0
- byte/domain/cli/rich/markdown.py +48 -0
- byte/domain/cli/rich/menu.py +563 -0
- byte/domain/cli/rich/rune_spinner.py +73 -0
- byte/domain/cli/schemas.py +156 -0
- byte/domain/cli/service/command_registry.py +122 -0
- byte/domain/cli/service/console_service.py +305 -0
- byte/domain/cli/service/interactions_service.py +159 -0
- byte/domain/cli/service/prompt_toolkit_service.py +193 -0
- byte/domain/cli/service/stream_rendering_service.py +271 -0
- byte/domain/cli/service_provider.py +71 -0
- byte/domain/cli/utils/formatters.py +122 -0
- byte/domain/development/__init__.py +0 -0
- byte/domain/development/command/__init__.py +0 -0
- byte/domain/development/command/save_recording_command.py +28 -0
- byte/domain/development/command/start_recording_command.py +20 -0
- byte/domain/development/command/test_command.py +37 -0
- byte/domain/development/service_provider.py +33 -0
- byte/domain/edit_format/__init__.py +0 -0
- byte/domain/edit_format/command/__init__.py +0 -0
- byte/domain/edit_format/command/copy_command.py +34 -0
- byte/domain/edit_format/config.py +10 -0
- byte/domain/edit_format/exceptions.py +44 -0
- byte/domain/edit_format/models.py +81 -0
- byte/domain/edit_format/service/__init__.py +0 -0
- byte/domain/edit_format/service/edit_block_prompt.py +271 -0
- byte/domain/edit_format/service/edit_block_service.py +355 -0
- byte/domain/edit_format/service/edit_format_service.py +85 -0
- byte/domain/edit_format/service/shell_command_prompt.py +96 -0
- byte/domain/edit_format/service/shell_command_service.py +211 -0
- byte/domain/edit_format/service_provider.py +53 -0
- byte/domain/files/__init__.py +0 -0
- byte/domain/files/command/__init__.py +0 -0
- byte/domain/files/command/add_file_command.py +57 -0
- byte/domain/files/command/add_read_only_file_command.py +53 -0
- byte/domain/files/command/drop_file_command.py +62 -0
- byte/domain/files/command/list_files_command.py +64 -0
- byte/domain/files/config.py +18 -0
- byte/domain/files/schemas.py +37 -0
- byte/domain/files/service/__init__.py +0 -0
- byte/domain/files/service/discovery_service.py +163 -0
- byte/domain/files/service/file_service.py +363 -0
- byte/domain/files/service/ignore_service.py +93 -0
- byte/domain/files/service/watcher_service.py +356 -0
- byte/domain/files/service_provider.py +68 -0
- byte/domain/git/__init__.py +0 -0
- byte/domain/git/command/__init__.py +0 -0
- byte/domain/git/command/commit_command.py +68 -0
- byte/domain/git/service/git_service.py +143 -0
- byte/domain/git/service_provider.py +22 -0
- byte/domain/knowledge/__init__.py +0 -0
- byte/domain/knowledge/command/__init__.py +0 -0
- byte/domain/knowledge/command/context_drop_command.py +55 -0
- byte/domain/knowledge/command/context_list_command.py +36 -0
- byte/domain/knowledge/command/web_command.py +96 -0
- byte/domain/knowledge/service/cli_context_display_service.py +53 -0
- byte/domain/knowledge/service/convention_context_service.py +67 -0
- byte/domain/knowledge/service/session_context_service.py +89 -0
- byte/domain/knowledge/service_provider.py +64 -0
- byte/domain/lint/__init__.py +0 -0
- byte/domain/lint/command/__init__.py +0 -0
- byte/domain/lint/command/lint_command.py +45 -0
- byte/domain/lint/config.py +19 -0
- byte/domain/lint/exceptions.py +12 -0
- byte/domain/lint/service/__init__.py +0 -0
- byte/domain/lint/service/lint_service.py +285 -0
- byte/domain/lint/service_provider.py +23 -0
- byte/domain/lint/types.py +23 -0
- byte/domain/llm/config.py +61 -0
- byte/domain/llm/schemas.py +188 -0
- byte/domain/llm/service/llm_service.py +71 -0
- byte/domain/llm/service_provider.py +36 -0
- byte/domain/mcp/__init__.py +0 -0
- byte/domain/mcp/command/__init__.py +0 -0
- byte/domain/mcp/command/mcp_tool_command.py +111 -0
- byte/domain/mcp/config.py +48 -0
- byte/domain/mcp/service/__init__.py +0 -0
- byte/domain/mcp/service/mcp_service.py +101 -0
- byte/domain/mcp/service_provider.py +28 -0
- byte/domain/memory/__init__.py +0 -0
- byte/domain/memory/command/__init__.py +0 -0
- byte/domain/memory/command/clear_command.py +45 -0
- byte/domain/memory/command/reset_command.py +47 -0
- byte/domain/memory/service/memory_service.py +79 -0
- byte/domain/memory/service_provider.py +24 -0
- byte/domain/system/command/__init__.py +0 -0
- byte/domain/system/command/exit_command.py +25 -0
- byte/domain/system/command/initilizie_command.py +78 -0
- byte/domain/system/service/system_context_service.py +54 -0
- byte/domain/system/service_provider.py +45 -0
- byte/domain/tools/__init__.py +3 -0
- byte/domain/tools/read_file.py +50 -0
- byte/domain/tools/ripgrep_search.py +71 -0
- byte/domain/tools/service_provider.py +13 -0
- byte/domain/tools/user_confirm.py +20 -0
- byte/domain/web/__init__.py +0 -0
- byte/domain/web/config.py +21 -0
- byte/domain/web/exceptions.py +17 -0
- byte/domain/web/service/__init__.py +0 -0
- byte/domain/web/service/chromium_service.py +55 -0
- byte/domain/web/service_provider.py +17 -0
- byte/main.py +74 -0
- byte_ai_cli-0.1.8.dist-info/METADATA +166 -0
- byte_ai_cli-0.1.8.dist-info/RECORD +172 -0
- byte_ai_cli-0.1.8.dist-info/WHEEL +4 -0
- byte_ai_cli-0.1.8.dist-info/entry_points.txt +3 -0
byte/__init__.py
ADDED
byte/bootstrap.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from byte.container import app
|
|
2
|
+
from byte.core.config.config import ByteConfg
|
|
3
|
+
from byte.core.event_bus import EventBus
|
|
4
|
+
from byte.core.task_manager import TaskManager
|
|
5
|
+
from byte.domain.agent.service_provider import AgentServiceProvider
|
|
6
|
+
from byte.domain.analytics.service_provider import AnalyticsProvider
|
|
7
|
+
from byte.domain.cli.service.command_registry import CommandRegistry
|
|
8
|
+
from byte.domain.cli.service.console_service import ConsoleService
|
|
9
|
+
from byte.domain.cli.service_provider import CLIServiceProvider
|
|
10
|
+
from byte.domain.development.service_provider import DevelopmentProvider
|
|
11
|
+
from byte.domain.edit_format.service_provider import EditFormatProvider
|
|
12
|
+
from byte.domain.files.service_provider import FileServiceProvider
|
|
13
|
+
from byte.domain.git.service_provider import GitServiceProvider
|
|
14
|
+
from byte.domain.knowledge.service_provider import KnowledgeServiceProvider
|
|
15
|
+
from byte.domain.lint.service_provider import LintServiceProvider
|
|
16
|
+
from byte.domain.llm.service_provider import LLMServiceProvider
|
|
17
|
+
from byte.domain.mcp.service_provider import MCPServiceProvider
|
|
18
|
+
from byte.domain.memory.service_provider import MemoryServiceProvider
|
|
19
|
+
from byte.domain.system.service_provider import SystemServiceProvider
|
|
20
|
+
from byte.domain.tools.service_provider import ToolsServiceProvider
|
|
21
|
+
from byte.domain.web.service_provider import WebServiceProvider
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def bootstrap(config: ByteConfg):
|
|
25
|
+
"""Initialize and configure the application's dependency injection container.
|
|
26
|
+
|
|
27
|
+
Follows a two-phase initialization pattern: register all services first,
|
|
28
|
+
then boot them. This ensures all dependencies are available during the
|
|
29
|
+
boot phase when services may need to reference each other.
|
|
30
|
+
|
|
31
|
+
Returns the fully configured container ready for use.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
app.singleton(EventBus)
|
|
35
|
+
app.singleton(TaskManager)
|
|
36
|
+
|
|
37
|
+
# Make the global command registry available through dependency injection
|
|
38
|
+
app.singleton(CommandRegistry)
|
|
39
|
+
|
|
40
|
+
# Boot config as early as possible
|
|
41
|
+
app.singleton(ByteConfg, lambda: config)
|
|
42
|
+
|
|
43
|
+
# Setup console early
|
|
44
|
+
app.singleton(ConsoleService)
|
|
45
|
+
|
|
46
|
+
# Order matters: ConfigServiceProvider must be early since other services
|
|
47
|
+
# may need configuration access during their boot phase
|
|
48
|
+
|
|
49
|
+
service_providers = [
|
|
50
|
+
CLIServiceProvider(), # Console and prompt services
|
|
51
|
+
MemoryServiceProvider(), # Short-term conversation memory
|
|
52
|
+
KnowledgeServiceProvider(),
|
|
53
|
+
FileServiceProvider(), # File context management
|
|
54
|
+
ToolsServiceProvider(), # File context management
|
|
55
|
+
LLMServiceProvider(), # Language model integration
|
|
56
|
+
GitServiceProvider(), # Git repository operations
|
|
57
|
+
LintServiceProvider(), # Code linting functionality
|
|
58
|
+
AgentServiceProvider(), # Agent routing and management
|
|
59
|
+
MCPServiceProvider(),
|
|
60
|
+
AnalyticsProvider(),
|
|
61
|
+
EditFormatProvider(),
|
|
62
|
+
WebServiceProvider(),
|
|
63
|
+
SystemServiceProvider(), # Core system commands
|
|
64
|
+
DevelopmentProvider(), # Core system commands
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# Phase 1: Register all service bindings in the container
|
|
68
|
+
# This makes services available for dependency resolution
|
|
69
|
+
for provider in service_providers:
|
|
70
|
+
await provider.register_services(app)
|
|
71
|
+
await provider.register_commands(app)
|
|
72
|
+
await provider.register(app)
|
|
73
|
+
|
|
74
|
+
# Phase 2: Boot services after all are registered
|
|
75
|
+
# This allows services to safely reference dependencies during initialization
|
|
76
|
+
for provider in service_providers:
|
|
77
|
+
await provider.boot_commands(app)
|
|
78
|
+
await provider.boot(app)
|
|
79
|
+
|
|
80
|
+
# Store service providers for shutdown
|
|
81
|
+
app._service_providers = service_providers
|
|
82
|
+
|
|
83
|
+
return app
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def shutdown(container):
|
|
87
|
+
"""Shutdown all service providers in reverse order."""
|
|
88
|
+
if hasattr(container, "_service_providers"):
|
|
89
|
+
# Shutdown in reverse order (opposite of boot)
|
|
90
|
+
for provider in reversed(container._service_providers):
|
|
91
|
+
await provider.shutdown(container)
|
byte/container.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any, Callable, Dict, Optional, Type, TypeVar
|
|
3
|
+
|
|
4
|
+
from byte.core.mixins.bootable import Bootable
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Container:
|
|
10
|
+
"""Simple dependency injection container for managing service bindings.
|
|
11
|
+
|
|
12
|
+
Implements the Service Locator pattern with support for both transient
|
|
13
|
+
and singleton lifetimes. Services are resolved lazily on first access.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self._singletons: Dict[Type, Callable[[], Any]] = {}
|
|
18
|
+
self._transients: Dict[Type, Callable[[], Any]] = {}
|
|
19
|
+
self._instances: Dict[Type, Any] = {}
|
|
20
|
+
self._service_providers = []
|
|
21
|
+
|
|
22
|
+
def bind(self, service_class: Type[T], concrete: Optional[Callable[[], T]] = None) -> None:
|
|
23
|
+
"""Register a transient service binding.
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
container.bind(FileService, lambda: FileService(container))
|
|
27
|
+
container.bind(FileService) # Auto-creates with container
|
|
28
|
+
"""
|
|
29
|
+
if concrete is None:
|
|
30
|
+
|
|
31
|
+
def concrete():
|
|
32
|
+
return service_class(self) # pyright: ignore[reportCallIssue]
|
|
33
|
+
|
|
34
|
+
self._transients[service_class] = concrete
|
|
35
|
+
|
|
36
|
+
def singleton(self, service_class: Type[T], concrete: Optional[Callable[[], T]] = None) -> None:
|
|
37
|
+
"""Register a singleton service binding.
|
|
38
|
+
|
|
39
|
+
Usage:
|
|
40
|
+
container.singleton(FileService, lambda: FileService(container))
|
|
41
|
+
container.singleton(FileService) # Auto-creates with container
|
|
42
|
+
"""
|
|
43
|
+
if concrete is None:
|
|
44
|
+
# Auto-create factory for class
|
|
45
|
+
def concrete():
|
|
46
|
+
return service_class(self) # pyright: ignore[reportCallIssue]
|
|
47
|
+
|
|
48
|
+
self._singletons[service_class] = concrete
|
|
49
|
+
|
|
50
|
+
async def make(self, service_class: Type[T], **kwargs) -> T:
|
|
51
|
+
"""Resolve a service from the container.
|
|
52
|
+
|
|
53
|
+
Usage:
|
|
54
|
+
file_service = await container.make(FileService)
|
|
55
|
+
"""
|
|
56
|
+
# Return cached singleton instance if available
|
|
57
|
+
if service_class in self._instances:
|
|
58
|
+
return self._instances[service_class]
|
|
59
|
+
|
|
60
|
+
# Try to create from singleton bindings
|
|
61
|
+
if service_class in self._singletons:
|
|
62
|
+
factory = self._singletons[service_class]
|
|
63
|
+
instance = await self._create_instance(factory, **kwargs)
|
|
64
|
+
self._instances[service_class] = instance # Cache it
|
|
65
|
+
return instance
|
|
66
|
+
|
|
67
|
+
# Try to create from transient bindings
|
|
68
|
+
if service_class in self._transients:
|
|
69
|
+
factory = self._transients[service_class]
|
|
70
|
+
return await self._create_instance(factory, **kwargs) # Don't cache
|
|
71
|
+
|
|
72
|
+
raise ValueError(f"No binding found for {service_class.__name__}")
|
|
73
|
+
|
|
74
|
+
async def _create_instance(self, factory: Callable, **kwargs) -> Any:
|
|
75
|
+
"""Helper to create and boot instances."""
|
|
76
|
+
if asyncio.iscoroutinefunction(factory):
|
|
77
|
+
instance = await factory()
|
|
78
|
+
else:
|
|
79
|
+
instance = factory()
|
|
80
|
+
|
|
81
|
+
if isinstance(instance, Bootable):
|
|
82
|
+
await instance.ensure_booted(**kwargs)
|
|
83
|
+
|
|
84
|
+
return instance
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Global application container instance
|
|
88
|
+
# Using a global container simplifies service access across the application
|
|
89
|
+
# while maintaining the benefits of dependency injection
|
|
90
|
+
app = Container()
|
byte/context.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from contextvars import ContextVar
|
|
2
|
+
from typing import Optional, Type, TypeVar
|
|
3
|
+
|
|
4
|
+
from byte.container import Container
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
container_context: ContextVar[Optional["Container"]] = ContextVar("container", default=None)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_container() -> "Container":
|
|
12
|
+
"""Get the current container from context."""
|
|
13
|
+
container = container_context.get()
|
|
14
|
+
if container is None:
|
|
15
|
+
raise RuntimeError("No container available in current context")
|
|
16
|
+
return container
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def make[T](service_class: Type[T]) -> T:
|
|
20
|
+
"""Convenience method to get a service from the current container context."""
|
|
21
|
+
container = get_container()
|
|
22
|
+
return await container.make(service_class)
|
byte/core/__init__.py
ADDED
byte/core/array_store.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
|
|
3
|
+
from byte.core.mixins.conditionable import Conditionable
|
|
4
|
+
from byte.core.utils import value
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ArrayStore(Conditionable):
|
|
8
|
+
"""Dictionary-based store with fluent interface and conditional execution.
|
|
9
|
+
|
|
10
|
+
Provides a chainable API for managing key-value data with support for
|
|
11
|
+
conditional operations, merging, and store manipulation. Extends Conditionable
|
|
12
|
+
for when/unless conditional execution patterns.
|
|
13
|
+
Usage: `store = ArrayStore({"key": "value"}).add("new", 42).when(True, ...)`
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, data: Optional[dict[str, Any]] = None):
|
|
17
|
+
"""Initialize the array store with optional initial data.
|
|
18
|
+
|
|
19
|
+
Usage: `store = ArrayStore({"initial": "data"})`
|
|
20
|
+
"""
|
|
21
|
+
self._data: dict[str, Any] = data if data is not None else {}
|
|
22
|
+
|
|
23
|
+
def add(self, key: str, val: Any) -> "ArrayStore":
|
|
24
|
+
"""Add an item to the store, evaluating callable values.
|
|
25
|
+
|
|
26
|
+
Usage: `store.add("key", "value")` or `store.add("key", lambda: compute())`
|
|
27
|
+
"""
|
|
28
|
+
self._data[key] = value(val)
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
32
|
+
"""Retrieve a single item from the store with optional default.
|
|
33
|
+
|
|
34
|
+
Usage: `val = store.get("key", "default_value")`
|
|
35
|
+
"""
|
|
36
|
+
return self._data.get(key, default)
|
|
37
|
+
|
|
38
|
+
def all(self) -> dict[str, Any]:
|
|
39
|
+
"""Retrieve all items in the store.
|
|
40
|
+
|
|
41
|
+
Usage: `data = store.all()`
|
|
42
|
+
"""
|
|
43
|
+
return self._data
|
|
44
|
+
|
|
45
|
+
def is_not_empty(self) -> bool:
|
|
46
|
+
"""Determine if the store contains any items.
|
|
47
|
+
|
|
48
|
+
Usage: `if store.is_not_empty(): ...`
|
|
49
|
+
"""
|
|
50
|
+
return not self.is_empty()
|
|
51
|
+
|
|
52
|
+
def is_empty(self) -> bool:
|
|
53
|
+
"""Determine if the store is empty.
|
|
54
|
+
|
|
55
|
+
Usage: `if store.is_empty(): ...`
|
|
56
|
+
"""
|
|
57
|
+
return len(self._data) == 0
|
|
58
|
+
|
|
59
|
+
def merge(self, *arrays: dict[str, Any]) -> "ArrayStore":
|
|
60
|
+
"""Merge one or more dictionaries into the store.
|
|
61
|
+
|
|
62
|
+
Usage: `store.merge({"key1": "val1"}, {"key2": "val2"})`
|
|
63
|
+
"""
|
|
64
|
+
for array in arrays:
|
|
65
|
+
self._data.update(array)
|
|
66
|
+
return self
|
|
67
|
+
|
|
68
|
+
def remove(self, key: str) -> "ArrayStore":
|
|
69
|
+
"""Remove an item from the store by key.
|
|
70
|
+
|
|
71
|
+
Usage: `store.remove("unwanted_key")`
|
|
72
|
+
"""
|
|
73
|
+
self._data.pop(key, None)
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
def set(self, data: dict[str, Any]) -> "ArrayStore":
|
|
77
|
+
"""Overwrite the entire store with new data.
|
|
78
|
+
|
|
79
|
+
Usage: `store.set({"new": "data"})`
|
|
80
|
+
"""
|
|
81
|
+
self._data = data
|
|
82
|
+
return self
|
byte/core/cli.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from byte.core.config.config import ByteConfg
|
|
4
|
+
from byte.core.initializer import FirstBootInitializer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.command()
|
|
8
|
+
def cli():
|
|
9
|
+
"""Byte CLI Assistant"""
|
|
10
|
+
from byte.main import run
|
|
11
|
+
|
|
12
|
+
# Check for first boot before bootstrapping
|
|
13
|
+
initializer = FirstBootInitializer()
|
|
14
|
+
if initializer.is_first_boot():
|
|
15
|
+
initializer.run_if_needed()
|
|
16
|
+
|
|
17
|
+
config = ByteConfg()
|
|
18
|
+
run(config)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
cli()
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
import git
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
from pydantic_settings import (
|
|
8
|
+
BaseSettings,
|
|
9
|
+
PydanticBaseSettingsSource,
|
|
10
|
+
SettingsConfigDict,
|
|
11
|
+
YamlConfigSettingsSource,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from byte.domain.cli.config import CLIConfig
|
|
15
|
+
from byte.domain.edit_format.config import EditFormatConfig
|
|
16
|
+
from byte.domain.files.config import FilesConfig
|
|
17
|
+
from byte.domain.lint.config import LintConfig
|
|
18
|
+
from byte.domain.llm.config import LLMConfig
|
|
19
|
+
from byte.domain.mcp.config import MCPServer
|
|
20
|
+
from byte.domain.web.config import WebConfig
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _find_project_root() -> Path:
|
|
24
|
+
"""Find git repository root directory.
|
|
25
|
+
|
|
26
|
+
Raises InvalidGitRepositoryError if not in a git repository.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
# Use git library to find repository root
|
|
30
|
+
repo = git.Repo(search_parent_directories=True)
|
|
31
|
+
return Path(repo.working_dir)
|
|
32
|
+
except git.InvalidGitRepositoryError:
|
|
33
|
+
raise git.InvalidGitRepositoryError(
|
|
34
|
+
"Byte requires a git repository. Please run 'git init' or navigate to a git repository."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
PROJECT_ROOT = _find_project_root()
|
|
39
|
+
BYTE_DIR: Path = PROJECT_ROOT / ".byte"
|
|
40
|
+
BYTE_DIR.mkdir(exist_ok=True)
|
|
41
|
+
|
|
42
|
+
BYTE_CACHE_DIR: Path = BYTE_DIR / "cache"
|
|
43
|
+
BYTE_CACHE_DIR.mkdir(exist_ok=True)
|
|
44
|
+
|
|
45
|
+
BYTE_CONFIG_FILE = BYTE_DIR / "config.yaml"
|
|
46
|
+
|
|
47
|
+
# Load our dotenv
|
|
48
|
+
DOTENV_PATH = PROJECT_ROOT / ".env"
|
|
49
|
+
load_dotenv(DOTENV_PATH)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ByteConfg(BaseSettings):
|
|
53
|
+
model_config = SettingsConfigDict(
|
|
54
|
+
env_nested_delimiter="_",
|
|
55
|
+
env_nested_max_split=1,
|
|
56
|
+
env_prefix="BYTE_",
|
|
57
|
+
yaml_file=BYTE_CONFIG_FILE,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
project_root: Path = Field(default=PROJECT_ROOT, exclude=True)
|
|
61
|
+
byte_dir: Path = Field(default=BYTE_DIR, exclude=True)
|
|
62
|
+
byte_cache_dir: Path = Field(default=BYTE_CACHE_DIR, exclude=True)
|
|
63
|
+
|
|
64
|
+
cli: CLIConfig = CLIConfig()
|
|
65
|
+
llm: LLMConfig = LLMConfig()
|
|
66
|
+
lint: LintConfig = LintConfig()
|
|
67
|
+
files: FilesConfig = FilesConfig()
|
|
68
|
+
edit_format: EditFormatConfig = EditFormatConfig()
|
|
69
|
+
web: WebConfig = WebConfig()
|
|
70
|
+
mcp: List[MCPServer] = []
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def settings_customise_sources(
|
|
74
|
+
cls,
|
|
75
|
+
settings_cls: type[BaseSettings],
|
|
76
|
+
init_settings: PydanticBaseSettingsSource,
|
|
77
|
+
env_settings: PydanticBaseSettingsSource,
|
|
78
|
+
dotenv_settings: PydanticBaseSettingsSource,
|
|
79
|
+
file_secret_settings: PydanticBaseSettingsSource,
|
|
80
|
+
) -> tuple[PydanticBaseSettingsSource, ...]:
|
|
81
|
+
return (YamlConfigSettingsSource(settings_cls),)
|
byte/core/event_bus.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any, Callable, Dict, List, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydantic.dataclasses import dataclass as pydantic_dataclass
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EventType(Enum):
|
|
12
|
+
PRE_PROMPT_TOOLKIT = "pre_prompt_toolkit"
|
|
13
|
+
POST_PROMPT_TOOLKIT = "post_prompt_toolkit"
|
|
14
|
+
|
|
15
|
+
GENERATE_FILE_CONTEXT = "generate_file_context"
|
|
16
|
+
|
|
17
|
+
FILE_ADDED = "file_added"
|
|
18
|
+
|
|
19
|
+
PRE_AGENT_EXECUTION = "pre_agent_execution"
|
|
20
|
+
POST_AGENT_EXECUTION = "post_agent_execution"
|
|
21
|
+
|
|
22
|
+
END_NODE = "end_node"
|
|
23
|
+
|
|
24
|
+
PRE_ASSISTANT_NODE = "pre_assistant_node"
|
|
25
|
+
POST_ASSISTANT_NODE = "post_assistant_node"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pydantic_dataclass
|
|
29
|
+
class Payload:
|
|
30
|
+
"""Generic event payload that can carry any data."""
|
|
31
|
+
|
|
32
|
+
event_type: EventType
|
|
33
|
+
data: Dict[str, Any]
|
|
34
|
+
timestamp: float = 0.0
|
|
35
|
+
|
|
36
|
+
def __post_init__(self):
|
|
37
|
+
if self.timestamp == 0.0:
|
|
38
|
+
self.timestamp = time.time()
|
|
39
|
+
|
|
40
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
41
|
+
"""Get data value with optional default."""
|
|
42
|
+
return self.data.get(key, default)
|
|
43
|
+
|
|
44
|
+
def set(self, key: str, value: Any) -> "Payload":
|
|
45
|
+
"""Return new Payload with updated data."""
|
|
46
|
+
new_data = self.data.copy()
|
|
47
|
+
new_data[key] = value
|
|
48
|
+
return Payload(event_type=self.event_type, data=new_data, timestamp=self.timestamp)
|
|
49
|
+
|
|
50
|
+
def update(self, updates: Dict[str, Any]) -> "Payload":
|
|
51
|
+
"""Return new Payload with multiple updates."""
|
|
52
|
+
new_data = self.data.copy()
|
|
53
|
+
new_data.update(updates)
|
|
54
|
+
return Payload(event_type=self.event_type, data=new_data, timestamp=self.timestamp)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class EventBus:
|
|
58
|
+
"""Simple event system with typed Pydantic payloads."""
|
|
59
|
+
|
|
60
|
+
def __init__(self, container=None, **kwargs):
|
|
61
|
+
self.container = container
|
|
62
|
+
self._listeners: Dict[str, List[Callable]] = {}
|
|
63
|
+
|
|
64
|
+
def on(self, event_name: str, callback: Callable):
|
|
65
|
+
"""Register a listener for an event."""
|
|
66
|
+
if event_name not in self._listeners:
|
|
67
|
+
self._listeners[event_name] = []
|
|
68
|
+
self._listeners[event_name].append(callback)
|
|
69
|
+
|
|
70
|
+
async def emit(self, payload: Payload) -> Payload:
|
|
71
|
+
"""Emit an event using the payload's event_type."""
|
|
72
|
+
event_name = payload.event_type.value
|
|
73
|
+
|
|
74
|
+
if event_name not in self._listeners:
|
|
75
|
+
return payload
|
|
76
|
+
|
|
77
|
+
current_payload = payload
|
|
78
|
+
|
|
79
|
+
for listener in self._listeners[event_name]:
|
|
80
|
+
try:
|
|
81
|
+
if asyncio.iscoroutinefunction(listener):
|
|
82
|
+
result = await listener(current_payload)
|
|
83
|
+
else:
|
|
84
|
+
result = listener(current_payload)
|
|
85
|
+
|
|
86
|
+
if result is not None:
|
|
87
|
+
current_payload = result
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f"Error in event listener for '{event_name}': {e}")
|
|
91
|
+
|
|
92
|
+
return current_payload
|
byte/core/exceptions.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class ByteException(Exception):
|
|
2
|
+
"""Base exception for all Byte operations.
|
|
3
|
+
|
|
4
|
+
All custom exceptions in the Byte project should inherit from this class.
|
|
5
|
+
This provides a common base for catching any Byte-specific errors.
|
|
6
|
+
Usage: `except ByteException as e: ...`
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ByteConfigException(ByteException):
|
|
13
|
+
"""Base exception for configuration-related errors.
|
|
14
|
+
|
|
15
|
+
Raised when configuration validation fails or required settings are missing.
|
|
16
|
+
Usage: `except ByteConfigException as e: ...`
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
pass
|