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.
Files changed (172) hide show
  1. byte/__init__.py +4 -0
  2. byte/bootstrap.py +91 -0
  3. byte/container.py +90 -0
  4. byte/context.py +22 -0
  5. byte/core/__init__.py +4 -0
  6. byte/core/array_store.py +82 -0
  7. byte/core/cli.py +22 -0
  8. byte/core/config/__init__.py +0 -0
  9. byte/core/config/config.py +81 -0
  10. byte/core/event_bus.py +92 -0
  11. byte/core/exceptions.py +19 -0
  12. byte/core/initializer.py +209 -0
  13. byte/core/logging.py +34 -0
  14. byte/core/mixins/__init__.py +0 -0
  15. byte/core/mixins/bootable.py +66 -0
  16. byte/core/mixins/conditionable.py +53 -0
  17. byte/core/mixins/configurable.py +19 -0
  18. byte/core/mixins/eventable.py +34 -0
  19. byte/core/mixins/injectable.py +28 -0
  20. byte/core/mixins/user_interactive.py +105 -0
  21. byte/core/service/base_service.py +58 -0
  22. byte/core/service_provider.py +103 -0
  23. byte/core/task_manager.py +28 -0
  24. byte/core/utils/__init__.py +108 -0
  25. byte/core/utils/save_fixture.py +27 -0
  26. byte/domain/__init__.py +0 -0
  27. byte/domain/agent/command/__init__.py +0 -0
  28. byte/domain/agent/command/ask_command.py +34 -0
  29. byte/domain/agent/implementations/__init__.py +0 -0
  30. byte/domain/agent/implementations/ask/__init__.py +0 -0
  31. byte/domain/agent/implementations/ask/agent.py +113 -0
  32. byte/domain/agent/implementations/ask/prompts.py +24 -0
  33. byte/domain/agent/implementations/base.py +168 -0
  34. byte/domain/agent/implementations/cleaner/__init__.py +0 -0
  35. byte/domain/agent/implementations/cleaner/agent.py +163 -0
  36. byte/domain/agent/implementations/cleaner/prompt.py +30 -0
  37. byte/domain/agent/implementations/coder/__init__.py +0 -0
  38. byte/domain/agent/implementations/coder/agent.py +116 -0
  39. byte/domain/agent/implementations/coder/prompts.py +30 -0
  40. byte/domain/agent/implementations/commit/__init__.py +0 -0
  41. byte/domain/agent/implementations/commit/agent.py +84 -0
  42. byte/domain/agent/implementations/commit/prompt.py +32 -0
  43. byte/domain/agent/implementations/copy/__init__.py +0 -0
  44. byte/domain/agent/implementations/copy/agent.py +44 -0
  45. byte/domain/agent/implementations/fixer/__init__.py +0 -0
  46. byte/domain/agent/implementations/fixer/agent.py +29 -0
  47. byte/domain/agent/implementations/fixer/prompts.py +30 -0
  48. byte/domain/agent/implementations/research/__init__.py +0 -0
  49. byte/domain/agent/implementations/research/agent.py +101 -0
  50. byte/domain/agent/implementations/research/prompts.py +37 -0
  51. byte/domain/agent/nodes/assistant_node.py +46 -0
  52. byte/domain/agent/nodes/base_node.py +12 -0
  53. byte/domain/agent/nodes/copy_node.py +168 -0
  54. byte/domain/agent/nodes/end_node.py +29 -0
  55. byte/domain/agent/nodes/lint_node.py +18 -0
  56. byte/domain/agent/nodes/parse_blocks_node.py +58 -0
  57. byte/domain/agent/nodes/start_node.py +34 -0
  58. byte/domain/agent/nodes/tool_node.py +51 -0
  59. byte/domain/agent/schemas.py +25 -0
  60. byte/domain/agent/service/agent_service.py +72 -0
  61. byte/domain/agent/service_provider.py +71 -0
  62. byte/domain/agent/state.py +57 -0
  63. byte/domain/analytics/service/agent_analytics_service.py +199 -0
  64. byte/domain/analytics/service_provider.py +44 -0
  65. byte/domain/cli/__init__.py +0 -0
  66. byte/domain/cli/config.py +16 -0
  67. byte/domain/cli/rich/heading.py +0 -0
  68. byte/domain/cli/rich/markdown.py +48 -0
  69. byte/domain/cli/rich/menu.py +563 -0
  70. byte/domain/cli/rich/rune_spinner.py +73 -0
  71. byte/domain/cli/schemas.py +156 -0
  72. byte/domain/cli/service/command_registry.py +122 -0
  73. byte/domain/cli/service/console_service.py +305 -0
  74. byte/domain/cli/service/interactions_service.py +159 -0
  75. byte/domain/cli/service/prompt_toolkit_service.py +193 -0
  76. byte/domain/cli/service/stream_rendering_service.py +271 -0
  77. byte/domain/cli/service_provider.py +71 -0
  78. byte/domain/cli/utils/formatters.py +122 -0
  79. byte/domain/development/__init__.py +0 -0
  80. byte/domain/development/command/__init__.py +0 -0
  81. byte/domain/development/command/save_recording_command.py +28 -0
  82. byte/domain/development/command/start_recording_command.py +20 -0
  83. byte/domain/development/command/test_command.py +37 -0
  84. byte/domain/development/service_provider.py +33 -0
  85. byte/domain/edit_format/__init__.py +0 -0
  86. byte/domain/edit_format/command/__init__.py +0 -0
  87. byte/domain/edit_format/command/copy_command.py +34 -0
  88. byte/domain/edit_format/config.py +10 -0
  89. byte/domain/edit_format/exceptions.py +44 -0
  90. byte/domain/edit_format/models.py +81 -0
  91. byte/domain/edit_format/service/__init__.py +0 -0
  92. byte/domain/edit_format/service/edit_block_prompt.py +271 -0
  93. byte/domain/edit_format/service/edit_block_service.py +355 -0
  94. byte/domain/edit_format/service/edit_format_service.py +85 -0
  95. byte/domain/edit_format/service/shell_command_prompt.py +96 -0
  96. byte/domain/edit_format/service/shell_command_service.py +211 -0
  97. byte/domain/edit_format/service_provider.py +53 -0
  98. byte/domain/files/__init__.py +0 -0
  99. byte/domain/files/command/__init__.py +0 -0
  100. byte/domain/files/command/add_file_command.py +57 -0
  101. byte/domain/files/command/add_read_only_file_command.py +53 -0
  102. byte/domain/files/command/drop_file_command.py +62 -0
  103. byte/domain/files/command/list_files_command.py +64 -0
  104. byte/domain/files/config.py +18 -0
  105. byte/domain/files/schemas.py +37 -0
  106. byte/domain/files/service/__init__.py +0 -0
  107. byte/domain/files/service/discovery_service.py +163 -0
  108. byte/domain/files/service/file_service.py +363 -0
  109. byte/domain/files/service/ignore_service.py +93 -0
  110. byte/domain/files/service/watcher_service.py +356 -0
  111. byte/domain/files/service_provider.py +68 -0
  112. byte/domain/git/__init__.py +0 -0
  113. byte/domain/git/command/__init__.py +0 -0
  114. byte/domain/git/command/commit_command.py +68 -0
  115. byte/domain/git/service/git_service.py +143 -0
  116. byte/domain/git/service_provider.py +22 -0
  117. byte/domain/knowledge/__init__.py +0 -0
  118. byte/domain/knowledge/command/__init__.py +0 -0
  119. byte/domain/knowledge/command/context_drop_command.py +55 -0
  120. byte/domain/knowledge/command/context_list_command.py +36 -0
  121. byte/domain/knowledge/command/web_command.py +96 -0
  122. byte/domain/knowledge/service/cli_context_display_service.py +53 -0
  123. byte/domain/knowledge/service/convention_context_service.py +67 -0
  124. byte/domain/knowledge/service/session_context_service.py +89 -0
  125. byte/domain/knowledge/service_provider.py +64 -0
  126. byte/domain/lint/__init__.py +0 -0
  127. byte/domain/lint/command/__init__.py +0 -0
  128. byte/domain/lint/command/lint_command.py +45 -0
  129. byte/domain/lint/config.py +19 -0
  130. byte/domain/lint/exceptions.py +12 -0
  131. byte/domain/lint/service/__init__.py +0 -0
  132. byte/domain/lint/service/lint_service.py +285 -0
  133. byte/domain/lint/service_provider.py +23 -0
  134. byte/domain/lint/types.py +23 -0
  135. byte/domain/llm/config.py +61 -0
  136. byte/domain/llm/schemas.py +188 -0
  137. byte/domain/llm/service/llm_service.py +71 -0
  138. byte/domain/llm/service_provider.py +36 -0
  139. byte/domain/mcp/__init__.py +0 -0
  140. byte/domain/mcp/command/__init__.py +0 -0
  141. byte/domain/mcp/command/mcp_tool_command.py +111 -0
  142. byte/domain/mcp/config.py +48 -0
  143. byte/domain/mcp/service/__init__.py +0 -0
  144. byte/domain/mcp/service/mcp_service.py +101 -0
  145. byte/domain/mcp/service_provider.py +28 -0
  146. byte/domain/memory/__init__.py +0 -0
  147. byte/domain/memory/command/__init__.py +0 -0
  148. byte/domain/memory/command/clear_command.py +45 -0
  149. byte/domain/memory/command/reset_command.py +47 -0
  150. byte/domain/memory/service/memory_service.py +79 -0
  151. byte/domain/memory/service_provider.py +24 -0
  152. byte/domain/system/command/__init__.py +0 -0
  153. byte/domain/system/command/exit_command.py +25 -0
  154. byte/domain/system/command/initilizie_command.py +78 -0
  155. byte/domain/system/service/system_context_service.py +54 -0
  156. byte/domain/system/service_provider.py +45 -0
  157. byte/domain/tools/__init__.py +3 -0
  158. byte/domain/tools/read_file.py +50 -0
  159. byte/domain/tools/ripgrep_search.py +71 -0
  160. byte/domain/tools/service_provider.py +13 -0
  161. byte/domain/tools/user_confirm.py +20 -0
  162. byte/domain/web/__init__.py +0 -0
  163. byte/domain/web/config.py +21 -0
  164. byte/domain/web/exceptions.py +17 -0
  165. byte/domain/web/service/__init__.py +0 -0
  166. byte/domain/web/service/chromium_service.py +55 -0
  167. byte/domain/web/service_provider.py +17 -0
  168. byte/main.py +74 -0
  169. byte_ai_cli-0.1.8.dist-info/METADATA +166 -0
  170. byte_ai_cli-0.1.8.dist-info/RECORD +172 -0
  171. byte_ai_cli-0.1.8.dist-info/WHEEL +4 -0
  172. byte_ai_cli-0.1.8.dist-info/entry_points.txt +3 -0
byte/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from byte.core.logging import log
2
+ from byte.core.utils import dd, dump
3
+
4
+ __all__ = ["dd", "dump", "log"]
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
@@ -0,0 +1,4 @@
1
+ from byte.core.logging import log
2
+ from byte.core.utils import dd, dump
3
+
4
+ __all__ = ["dd", "dump", "log"]
@@ -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
@@ -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