bear-utils 0.8.23__tar.gz → 0.8.24__tar.gz

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 (119) hide show
  1. {bear_utils-0.8.23 → bear_utils-0.8.24}/.bumpversion.cfg +1 -1
  2. {bear_utils-0.8.23 → bear_utils-0.8.24}/PKG-INFO +3 -2
  3. {bear_utils-0.8.23 → bear_utils-0.8.24}/README.md +1 -1
  4. {bear_utils-0.8.23 → bear_utils-0.8.24}/pyproject.toml +2 -1
  5. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/prompt_helpers.py +25 -18
  6. bear_utils-0.8.24/src/bear_utils/cli/typer_bridge.py +90 -0
  7. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_function_response.py +7 -7
  8. {bear_utils-0.8.23 → bear_utils-0.8.24}/uv.lock +34 -1
  9. {bear_utils-0.8.23 → bear_utils-0.8.24}/.gitignore +0 -0
  10. {bear_utils-0.8.23 → bear_utils-0.8.24}/.python-version +0 -0
  11. {bear_utils-0.8.23 → bear_utils-0.8.24}/AGENTS.md +0 -0
  12. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/coverage.ini +0 -0
  13. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/default.toml +0 -0
  14. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/git-changelog.toml +0 -0
  15. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/pytest.ini +0 -0
  16. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/ruff.toml +0 -0
  17. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/vscode/launch.json +0 -0
  18. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/vscode/settings.json +0 -0
  19. {bear_utils-0.8.23 → bear_utils-0.8.24}/config/vscode/tasks.json +0 -0
  20. {bear_utils-0.8.23 → bear_utils-0.8.24}/directory_structure.txt +0 -0
  21. {bear_utils-0.8.23 → bear_utils-0.8.24}/directory_structure.xml +0 -0
  22. {bear_utils-0.8.23 → bear_utils-0.8.24}/maskfile.md +0 -0
  23. {bear_utils-0.8.23 → bear_utils-0.8.24}/noxfile.py +0 -0
  24. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/__init__.py +0 -0
  25. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/__main__.py +0 -0
  26. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/_internal/__init__.py +0 -0
  27. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/_internal/cli.py +0 -0
  28. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/_internal/debug.py +0 -0
  29. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/__init__.py +0 -0
  30. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/__init__.py +0 -0
  31. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_common.py +0 -0
  32. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_config.py +0 -0
  33. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_parsers.py +0 -0
  34. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_types.py +0 -0
  35. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cache/__init__.py +0 -0
  36. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/__init__.py +0 -0
  37. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/commands.py +0 -0
  38. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/__init__.py +0 -0
  39. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/_base_command.py +0 -0
  40. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/_base_shell.py +0 -0
  41. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/_common.py +0 -0
  42. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/__init__.py +0 -0
  43. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/config_manager.py +0 -0
  44. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/dir_manager.py +0 -0
  45. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/settings_manager.py +0 -0
  46. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/__init__.py +0 -0
  47. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/_exceptions.py +0 -0
  48. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/_lazy_typing.py +0 -0
  49. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/date_related.py +0 -0
  50. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/server.py +0 -0
  51. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/time_related.py +0 -0
  52. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/database/__init__.py +0 -0
  53. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/database/_db_manager.py +0 -0
  54. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/events/__init__.py +0 -0
  55. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/events/events_class.py +0 -0
  56. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/events/events_module.py +0 -0
  57. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/__init__.py +0 -0
  58. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/_async_helpers.py +0 -0
  59. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/_tools.py +0 -0
  60. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/platform_utils.py +0 -0
  61. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/responses/__init__.py +0 -0
  62. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/responses/function_response.py +0 -0
  63. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/wrappers/__init__.py +0 -0
  64. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/wrappers/add_methods.py +0 -0
  65. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/__init__.py +0 -0
  66. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/__init__.py +0 -0
  67. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/_base_file_handler.py +0 -0
  68. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/file_handler_factory.py +0 -0
  69. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/json_file_handler.py +0 -0
  70. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/log_file_handler.py +0 -0
  71. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/toml_file_handler.py +0 -0
  72. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/txt_file_handler.py +0 -0
  73. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/yaml_file_handler.py +0 -0
  74. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/ignore_parser.py +0 -0
  75. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/graphics/__init__.py +0 -0
  76. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/graphics/bear_gradient.py +0 -0
  77. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/graphics/image_helpers.py +0 -0
  78. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/__init__.py +0 -0
  79. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/__init__.py +0 -0
  80. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/_settings.py +0 -0
  81. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/_types.py +0 -0
  82. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_app.py +0 -0
  83. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_color_picker.py +0 -0
  84. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_file_handler.py +0 -0
  85. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_input_dialog.py +0 -0
  86. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/__init__.py +0 -0
  87. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_common.py +0 -0
  88. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_console_junk.py +0 -0
  89. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_log_level.py +0 -0
  90. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_styles.py +0 -0
  91. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/logger_protocol.py +0 -0
  92. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/__init__.py +0 -0
  93. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/_level_sin.py +0 -0
  94. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/base_logger.py +0 -0
  95. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/base_logger.pyi +0 -0
  96. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/basic_logger/__init__.py +0 -0
  97. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/basic_logger/logger.py +0 -0
  98. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/basic_logger/logger.pyi +0 -0
  99. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/buffer_logger.py +0 -0
  100. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/console_logger.py +0 -0
  101. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/console_logger.pyi +0 -0
  102. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/fastapi_logger.py +0 -0
  103. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/file_logger.py +0 -0
  104. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/simple_logger.py +0 -0
  105. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/sub_logger.py +0 -0
  106. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/sub_logger.pyi +0 -0
  107. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/monitoring/__init__.py +0 -0
  108. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/monitoring/_common.py +0 -0
  109. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/monitoring/host_monitor.py +0 -0
  110. {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/time/__init__.py +0 -0
  111. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/__init__.py +0 -0
  112. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_add_ord_suffix.py +0 -0
  113. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_clipboard.py +0 -0
  114. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_database_manager.py +0 -0
  115. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_default_shell.py +0 -0
  116. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_gradient.py +0 -0
  117. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_logger.py +0 -0
  118. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_platform_utils.py +0 -0
  119. {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_prompt_helpers.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 0.8.23
2
+ current_version = 0.8.24
3
3
 
4
4
  [bumpversion:file:pyproject.toml]
5
5
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bear-utils
3
- Version: 0.8.23
3
+ Version: 0.8.24
4
4
  Summary: Various utilities for Bear programmers, including a rich logging utility, a disk cache, and a SQLite database wrapper amongst other things.
5
5
  Author-email: chaz <bright.lid5647@fastmail.com>
6
6
  Requires-Python: >=3.12
@@ -19,12 +19,13 @@ Requires-Dist: singleton-base>=1.0.5
19
19
  Requires-Dist: sqlalchemy<3.0.0,>=2.0.40
20
20
  Requires-Dist: tinydb>=4.8.2
21
21
  Requires-Dist: toml>=0.10.2
22
+ Requires-Dist: typer>=0.16.0
22
23
  Requires-Dist: uvicorn>=0.35.0
23
24
  Provides-Extra: gui
24
25
  Requires-Dist: pyqt6>=6.9.0; extra == 'gui'
25
26
  Description-Content-Type: text/markdown
26
27
 
27
- # Bear Utils v# Bear Utils v0.8.23
28
+ # Bear Utils v# Bear Utils v0.8.24
28
29
 
29
30
  Personal set of tools and utilities for Python projects, focusing on modularity and ease of use. This library includes components for caching, database management, logging, time handling, file operations, CLI prompts, image processing, clipboard interaction, gradient utilities, event systems, and async helpers.
30
31
 
@@ -1,4 +1,4 @@
1
- # Bear Utils v# Bear Utils v0.8.23
1
+ # Bear Utils v# Bear Utils v0.8.24
2
2
 
3
3
  Personal set of tools and utilities for Python projects, focusing on modularity and ease of use. This library includes components for caching, database management, logging, time handling, file operations, CLI prompts, image processing, clipboard interaction, gradient utilities, event systems, and async helpers.
4
4
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bear-utils"
3
- version = "0.8.23"
3
+ version = "0.8.24"
4
4
  description = "Various utilities for Bear programmers, including a rich logging utility, a disk cache, and a SQLite database wrapper amongst other things."
5
5
  authors = [{ name = "chaz", email = "bright.lid5647@fastmail.com" }]
6
6
  readme = "README.md"
@@ -22,6 +22,7 @@ dependencies = [
22
22
  "fastapi>=0.116.0",
23
23
  "uvicorn>=0.35.0",
24
24
  "bear-epoch-time>=1.1.1",
25
+ "typer>=0.16.0",
25
26
  ]
26
27
 
27
28
  [project.optional-dependencies]
@@ -10,7 +10,11 @@ from bear_utils.constants._exceptions import UserCancelledError
10
10
  from bear_utils.constants._lazy_typing import OptBool, OptFloat, OptInt, OptStr
11
11
  from bear_utils.logger_manager import get_console
12
12
 
13
- # TODO: ehhhhhhhh, it is okay
13
+
14
+ def _parse_exit(value: str) -> bool:
15
+ """Parse a string into a boolean indicating if the user wants to exit."""
16
+ lower_value: str = value.lower().strip()
17
+ return lower_value in ("exit", "quit", "q")
14
18
 
15
19
 
16
20
  def _parse_bool(value: str) -> bool:
@@ -70,7 +74,7 @@ def ask_question(question: str, expected_type: type, default: Any = None) -> Any
70
74
  UserCancelledError: If the user cancels input with Ctrl+C
71
75
  ValueError: If an unsupported type is specified
72
76
  """
73
- console, sub = get_console("prompt_helpers.py")
77
+ console, _ = get_console("prompt_helpers.py")
74
78
 
75
79
  try:
76
80
  while True:
@@ -80,14 +84,14 @@ def ask_question(question: str, expected_type: type, default: Any = None) -> Any
80
84
  if not response:
81
85
  if default is not None:
82
86
  return default
83
- sub.error("Input required. Please enter a value.")
87
+ console.error("Input required. Please enter a value.")
84
88
  continue
85
89
  try:
86
90
  result: str | int | float | bool = _convert_value(response, expected_type)
87
- sub.verbose(f"{expected_type.__name__} detected")
91
+ console.verbose(f"{expected_type.__name__} detected")
88
92
  return result
89
93
  except ValueError as e:
90
- sub.error(f"Invalid input: {e}. Please enter a valid {expected_type.__name__}.")
94
+ console.error(f"Invalid input: {e}. Please enter a valid {expected_type.__name__}.")
91
95
 
92
96
  except KeyboardInterrupt:
93
97
  raise UserCancelledError("User cancelled input") from None
@@ -108,15 +112,13 @@ def ask_yes_no(question: str, default: bool | None = None) -> bool | None:
108
112
  try:
109
113
  while True:
110
114
  console.print(question)
111
- response = prompt("> ").strip().lower()
112
-
115
+ response: str = prompt("> ").strip().lower()
113
116
  if not response:
114
117
  if default is not None:
115
118
  return default
116
119
  sub.error("Please enter 'yes', 'no', or 'exit'.")
117
120
  continue
118
-
119
- if response in ("exit", "quit"):
121
+ if _parse_exit(response):
120
122
  return None
121
123
  try:
122
124
  return _parse_bool(response)
@@ -128,7 +130,10 @@ def ask_yes_no(question: str, default: bool | None = None) -> bool | None:
128
130
 
129
131
 
130
132
  def restricted_prompt(
131
- question: str, valid_options: list[str], exit_command: str = "exit", case_sensitive: bool = False
133
+ question: str,
134
+ valid_options: list[str],
135
+ exit_command: str = "exit",
136
+ case_sensitive: bool = False,
132
137
  ) -> str | None:
133
138
  """Continuously prompt the user until they provide a valid response or exit.
134
139
 
@@ -141,12 +146,12 @@ def restricted_prompt(
141
146
  Returns:
142
147
  The user's response or None if they chose to exit
143
148
  """
144
- console, sub = get_console("prompt_helpers.py")
145
- completer_options = [*valid_options, exit_command]
149
+ console, _ = get_console("prompt_helpers.py")
150
+ completer_options: list[str] = [*valid_options, exit_command]
146
151
  completer = WordCompleter(completer_options)
147
152
 
148
- comparison_options = valid_options if case_sensitive else [opt.lower() for opt in valid_options]
149
- comparison_exit = exit_command if case_sensitive else exit_command.lower()
153
+ comparison_options: list[str] = valid_options if case_sensitive else [opt.lower() for opt in valid_options]
154
+ comparison_exit: str = exit_command if case_sensitive else exit_command.lower()
150
155
 
151
156
  class OptionValidator(Validator):
152
157
  def validate(self, document: Any) -> None:
@@ -162,11 +167,14 @@ def restricted_prompt(
162
167
  while True:
163
168
  console.print(question)
164
169
  response: str = prompt(
165
- "> ", completer=completer, validator=OptionValidator(), complete_while_typing=True
170
+ "> ",
171
+ completer=completer,
172
+ validator=OptionValidator(),
173
+ complete_while_typing=True,
166
174
  ).strip()
167
175
  comparison_response: str = response if case_sensitive else response.lower()
168
176
  if not response:
169
- sub.error("Please enter a valid option or 'exit'.")
177
+ console.error("Please enter a valid option or 'exit'.")
170
178
  continue
171
179
  if comparison_response == comparison_exit:
172
180
  return None
@@ -175,7 +183,6 @@ def restricted_prompt(
175
183
  idx: int = comparison_options.index(comparison_response)
176
184
  return valid_options[idx]
177
185
  return response
178
-
179
186
  except KeyboardInterrupt:
180
- sub.warning("KeyboardInterrupt: Exiting the prompt.")
187
+ console.warning("KeyboardInterrupt: Exiting the prompt.")
181
188
  return None
@@ -0,0 +1,90 @@
1
+ """A simple bridge for augmenting Typer with alias support and command execution for interactive use."""
2
+
3
+ from collections.abc import Callable
4
+ import shlex
5
+ from typing import Any, TypedDict
6
+
7
+ from rich.console import Console
8
+ from singleton_base import SingletonBase
9
+ from typer import Exit, Typer
10
+ from typer.models import CommandInfo
11
+
12
+ from bear_utils.logger_manager import AsyncLoggerProtocol, LoggerProtocol
13
+
14
+
15
+ class CommandMeta(TypedDict):
16
+ """Metadata for a Typer command."""
17
+
18
+ name: str
19
+ help: str
20
+ hidden: bool
21
+
22
+
23
+ def get_command_meta(command: CommandInfo) -> CommandMeta:
24
+ """Extract metadata from a Typer command."""
25
+ return {
26
+ "name": command.name or (command.callback.__name__ if command.callback else "unknown"),
27
+ "help": (command.callback.__doc__ if command.callback else None) or "No description available",
28
+ "hidden": command.hidden,
29
+ }
30
+
31
+
32
+ # TODO: Add support for usage statements for a more robust help system
33
+
34
+
35
+ class TyperBridge(SingletonBase):
36
+ """Simple bridge for Typer command execution."""
37
+
38
+ def __init__(self, typer_app: Typer, console: AsyncLoggerProtocol | LoggerProtocol | Console) -> None:
39
+ """Initialize the TyperBridge with a Typer app instance."""
40
+ self.app: Typer = typer_app
41
+ self.console: AsyncLoggerProtocol | LoggerProtocol | Console = console or Console()
42
+ self.command_meta: dict[str, CommandMeta] = {}
43
+
44
+ def metadata(self, *alias_names: str) -> Callable[..., Callable[..., Any]]:
45
+ """Register aliases as hidden Typer commands."""
46
+
47
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
48
+ for alias in alias_names:
49
+ self.app.command(name=alias, hidden=True)(func)
50
+ return func
51
+
52
+ return decorator
53
+
54
+ def execute_command(self, command_string: str) -> bool:
55
+ """Execute command via Typer. Return True if successful."""
56
+ try:
57
+ parts: list[str] = shlex.split(command_string.strip())
58
+ if not parts:
59
+ return False
60
+ self.app(parts, standalone_mode=False)
61
+ return True
62
+ except Exit:
63
+ return True
64
+ except Exception as e:
65
+ if isinstance(self.console, Console):
66
+ self.console.print(f"[red]Error executing command: {e}[/red]")
67
+ else:
68
+ self.console.error(f"Error executing command: {e}", exc_info=True)
69
+ return False
70
+
71
+ def bootstrap_command_meta(self) -> None:
72
+ """Bootstrap command metadata from the Typer app."""
73
+ if not self.command_meta:
74
+ for cmd in self.app.registered_commands:
75
+ cmd_meta: CommandMeta = get_command_meta(command=cmd)
76
+ self.command_meta[cmd_meta["name"]] = cmd_meta
77
+
78
+ def get_all_command_info(self, show_hidden: bool = False) -> dict[str, CommandMeta]:
79
+ """Get all command information from the Typer app."""
80
+ if not self.command_meta:
81
+ self.bootstrap_command_meta()
82
+ if not show_hidden:
83
+ return {name: meta for name, meta in self.command_meta.items() if not meta["hidden"]}
84
+ return self.command_meta
85
+
86
+ def get_command_info(self, command_name: str) -> CommandMeta | None:
87
+ """Get metadata for a specific command."""
88
+ if not self.command_meta:
89
+ self.bootstrap_command_meta()
90
+ return self.command_meta.get(command_name)
@@ -597,11 +597,11 @@ class TestConstants:
597
597
 
598
598
  def test_success_constant(self):
599
599
  """Test SUCCESS constant values."""
600
- assert SUCCESS == ["name", "success"]
600
+ assert SUCCESS == ["name", "success", "number_of_tasks"]
601
601
 
602
602
  def test_failure_constant(self):
603
603
  """Test FAILURE constant values."""
604
- assert FAILURE == ["name"]
604
+ assert FAILURE == ["name", "number_of_tasks"]
605
605
 
606
606
 
607
607
  class TestComplexScenarios:
@@ -635,13 +635,13 @@ class TestComplexScenarios:
635
635
  response.successful("Cleared 5 completed tasks")
636
636
  response.add(content="Operation summary generated")
637
637
 
638
- result = response.done(to_dict=True, suppress=SUCCESS)
638
+ result = response.done(to_dict=True)
639
639
 
640
640
  # SUCCESS suppresses "name" and "success", so check what actually remains
641
641
  assert "content" in result
642
642
  assert "number_of_tasks" not in result # This will only be here in failures
643
- assert "name" not in result
644
- assert "success" not in result
643
+ assert "name" in result
644
+ assert "success" in result
645
645
 
646
646
  def test_mcp_server_response_format_error(self):
647
647
  """Test formatting response for MCP server consumption."""
@@ -651,12 +651,12 @@ class TestComplexScenarios:
651
651
  response.fail("Failed to clear tasks")
652
652
  response.add(content="Error summary generated")
653
653
 
654
- result = response.done(to_dict=True, suppress=FAILURE)
654
+ result = response.done(to_dict=True)
655
655
 
656
656
  # SUCCESS suppresses "name" and "success", so check what actually remains
657
657
  assert "content" in result
658
658
  assert "number_of_tasks" in result
659
- assert "name" not in result
659
+ assert "name" in result
660
660
  assert "success" in result
661
661
 
662
662
  def test_error_handling_in_add_method(self):
@@ -73,7 +73,7 @@ wheels = [
73
73
 
74
74
  [[package]]
75
75
  name = "bear-utils"
76
- version = "0.8.19"
76
+ version = "0.8.23"
77
77
  source = { editable = "." }
78
78
  dependencies = [
79
79
  { name = "bear-epoch-time" },
@@ -91,9 +91,15 @@ dependencies = [
91
91
  { name = "sqlalchemy" },
92
92
  { name = "tinydb" },
93
93
  { name = "toml" },
94
+ { name = "typer" },
94
95
  { name = "uvicorn" },
95
96
  ]
96
97
 
98
+ [package.optional-dependencies]
99
+ gui = [
100
+ { name = "pyqt6" },
101
+ ]
102
+
97
103
  [package.dev-dependencies]
98
104
  ci = [
99
105
  { name = "bump2version" },
@@ -129,14 +135,17 @@ requires-dist = [
129
135
  { name = "prompt-toolkit", specifier = ">=3.0.51,<4.0.0" },
130
136
  { name = "pydantic", specifier = ">=2.11.5" },
131
137
  { name = "pyglm", specifier = ">=2.8.2,<3.0.0" },
138
+ { name = "pyqt6", marker = "extra == 'gui'", specifier = ">=6.9.0" },
132
139
  { name = "pyyaml", specifier = ">=6.0.2" },
133
140
  { name = "rich", specifier = ">=14.0.0,<15.0.0" },
134
141
  { name = "singleton-base", specifier = ">=1.0.5" },
135
142
  { name = "sqlalchemy", specifier = ">=2.0.40,<3.0.0" },
136
143
  { name = "tinydb", specifier = ">=4.8.2" },
137
144
  { name = "toml", specifier = ">=0.10.2" },
145
+ { name = "typer", specifier = ">=0.16.0" },
138
146
  { name = "uvicorn", specifier = ">=0.35.0" },
139
147
  ]
148
+ provides-extras = ["gui"]
140
149
 
141
150
  [package.metadata.requires-dev]
142
151
  ci = [
@@ -1252,6 +1261,15 @@ wheels = [
1252
1261
  { url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912, upload_time = "2025-01-24T13:19:24.949Z" },
1253
1262
  ]
1254
1263
 
1264
+ [[package]]
1265
+ name = "shellingham"
1266
+ version = "1.5.4"
1267
+ source = { registry = "https://pypi.org/simple/" }
1268
+ sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload_time = "2023-10-24T04:13:40.426Z" }
1269
+ wheels = [
1270
+ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload_time = "2023-10-24T04:13:38.866Z" },
1271
+ ]
1272
+
1255
1273
  [[package]]
1256
1274
  name = "singleton-base"
1257
1275
  version = "1.0.8"
@@ -1370,6 +1388,21 @@ wheels = [
1370
1388
  { url = "https://files.pythonhosted.org/packages/56/56/2d2a9139ec6c470836fc2b8c0a5c3c781d50b22c90c47c3f578902080a83/type_lens-0.2.3-py3-none-any.whl", hash = "sha256:3c1850545de595a5d5df4e24f8ea8c0f6d9aff42317ca9941d5069b9ad5c1e2e", size = 14288, upload_time = "2024-10-02T14:52:43.191Z" },
1371
1389
  ]
1372
1390
 
1391
+ [[package]]
1392
+ name = "typer"
1393
+ version = "0.16.0"
1394
+ source = { registry = "https://pypi.org/simple/" }
1395
+ dependencies = [
1396
+ { name = "click" },
1397
+ { name = "rich" },
1398
+ { name = "shellingham" },
1399
+ { name = "typing-extensions" },
1400
+ ]
1401
+ sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload_time = "2025-05-26T14:30:31.824Z" }
1402
+ wheels = [
1403
+ { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload_time = "2025-05-26T14:30:30.523Z" },
1404
+ ]
1405
+
1373
1406
  [[package]]
1374
1407
  name = "types-markdown"
1375
1408
  version = "3.8.0.20250708"
File without changes
File without changes
File without changes
File without changes
File without changes