falyx 0.1.48__tar.gz → 0.1.50__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 (62) hide show
  1. {falyx-0.1.48 → falyx-0.1.50}/PKG-INFO +1 -1
  2. falyx-0.1.50/falyx/.coverage +0 -0
  3. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/base.py +1 -1
  4. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/menu_action.py +4 -1
  5. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/prompt_menu_action.py +4 -1
  6. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/select_file_action.py +4 -1
  7. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/selection_action.py +4 -1
  8. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/user_input_action.py +4 -1
  9. {falyx-0.1.48 → falyx-0.1.50}/falyx/bottom_bar.py +1 -1
  10. {falyx-0.1.48 → falyx-0.1.50}/falyx/command.py +1 -1
  11. {falyx-0.1.48 → falyx-0.1.50}/falyx/config.py +1 -2
  12. {falyx-0.1.48 → falyx-0.1.50}/falyx/context.py +15 -2
  13. {falyx-0.1.48 → falyx-0.1.50}/falyx/debug.py +1 -1
  14. {falyx-0.1.48 → falyx-0.1.50}/falyx/execution_registry.py +98 -18
  15. {falyx-0.1.48 → falyx-0.1.50}/falyx/falyx.py +70 -21
  16. {falyx-0.1.48 → falyx-0.1.50}/falyx/init.py +1 -1
  17. {falyx-0.1.48 → falyx-0.1.50}/falyx/parsers/argparse.py +5 -2
  18. {falyx-0.1.48 → falyx-0.1.50}/falyx/selection.py +5 -5
  19. falyx-0.1.50/falyx/version.py +1 -0
  20. {falyx-0.1.48 → falyx-0.1.50}/pyproject.toml +1 -1
  21. falyx-0.1.48/falyx/version.py +0 -1
  22. {falyx-0.1.48 → falyx-0.1.50}/LICENSE +0 -0
  23. {falyx-0.1.48 → falyx-0.1.50}/README.md +0 -0
  24. {falyx-0.1.48 → falyx-0.1.50}/falyx/.pytyped +0 -0
  25. {falyx-0.1.48 → falyx-0.1.50}/falyx/__init__.py +0 -0
  26. {falyx-0.1.48 → falyx-0.1.50}/falyx/__main__.py +0 -0
  27. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/.pytyped +0 -0
  28. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/__init__.py +0 -0
  29. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/action.py +0 -0
  30. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/action_factory.py +0 -0
  31. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/action_group.py +0 -0
  32. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/chained_action.py +0 -0
  33. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/fallback_action.py +0 -0
  34. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/http_action.py +0 -0
  35. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/io_action.py +0 -0
  36. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/literal_input_action.py +0 -0
  37. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/mixins.py +0 -0
  38. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/process_action.py +0 -0
  39. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/process_pool_action.py +0 -0
  40. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/signal_action.py +0 -0
  41. {falyx-0.1.48 → falyx-0.1.50}/falyx/action/types.py +0 -0
  42. {falyx-0.1.48 → falyx-0.1.50}/falyx/exceptions.py +0 -0
  43. {falyx-0.1.48 → falyx-0.1.50}/falyx/hook_manager.py +0 -0
  44. {falyx-0.1.48 → falyx-0.1.50}/falyx/hooks.py +0 -0
  45. {falyx-0.1.48 → falyx-0.1.50}/falyx/logger.py +0 -0
  46. {falyx-0.1.48 → falyx-0.1.50}/falyx/menu.py +0 -0
  47. {falyx-0.1.48 → falyx-0.1.50}/falyx/options_manager.py +0 -0
  48. {falyx-0.1.48 → falyx-0.1.50}/falyx/parsers/.pytyped +0 -0
  49. {falyx-0.1.48 → falyx-0.1.50}/falyx/parsers/__init__.py +0 -0
  50. {falyx-0.1.48 → falyx-0.1.50}/falyx/parsers/parsers.py +0 -0
  51. {falyx-0.1.48 → falyx-0.1.50}/falyx/parsers/signature.py +0 -0
  52. {falyx-0.1.48 → falyx-0.1.50}/falyx/parsers/utils.py +0 -0
  53. {falyx-0.1.48 → falyx-0.1.50}/falyx/prompt_utils.py +0 -0
  54. {falyx-0.1.48 → falyx-0.1.50}/falyx/protocols.py +0 -0
  55. {falyx-0.1.48 → falyx-0.1.50}/falyx/retry.py +0 -0
  56. {falyx-0.1.48 → falyx-0.1.50}/falyx/retry_utils.py +0 -0
  57. {falyx-0.1.48 → falyx-0.1.50}/falyx/signals.py +0 -0
  58. {falyx-0.1.48 → falyx-0.1.50}/falyx/tagged_table.py +0 -0
  59. {falyx-0.1.48 → falyx-0.1.50}/falyx/themes/__init__.py +0 -0
  60. {falyx-0.1.48 → falyx-0.1.50}/falyx/themes/colors.py +0 -0
  61. {falyx-0.1.48 → falyx-0.1.50}/falyx/utils.py +0 -0
  62. {falyx-0.1.48 → falyx-0.1.50}/falyx/validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: falyx
3
- Version: 0.1.48
3
+ Version: 0.1.50
4
4
  Summary: Reliable and introspectable async CLI action framework.
5
5
  License: MIT
6
6
  Author: Roland Thomas Jr
Binary file
@@ -74,7 +74,7 @@ class BaseAction(ABC):
74
74
  self.inject_into: str = inject_into
75
75
  self._never_prompt: bool = never_prompt
76
76
  self._skip_in_chain: bool = False
77
- self.console = Console(color_system="auto")
77
+ self.console = Console(color_system="truecolor")
78
78
  self.options_manager: OptionsManager | None = None
79
79
 
80
80
  if logging_hooks:
@@ -51,7 +51,10 @@ class MenuAction(BaseAction):
51
51
  self.columns = columns
52
52
  self.prompt_message = prompt_message
53
53
  self.default_selection = default_selection
54
- self.console = console or Console(color_system="auto")
54
+ if isinstance(console, Console):
55
+ self.console = console
56
+ elif console:
57
+ raise ValueError("`console` must be an instance of `rich.console.Console`")
55
58
  self.prompt_session = prompt_session or PromptSession()
56
59
  self.include_reserved = include_reserved
57
60
  self.show_table = show_table
@@ -43,7 +43,10 @@ class PromptMenuAction(BaseAction):
43
43
  self.menu_options = menu_options
44
44
  self.prompt_message = prompt_message
45
45
  self.default_selection = default_selection
46
- self.console = console or Console(color_system="auto")
46
+ if isinstance(console, Console):
47
+ self.console = console
48
+ elif console:
49
+ raise ValueError("`console` must be an instance of `rich.console.Console`")
47
50
  self.prompt_session = prompt_session or PromptSession()
48
51
  self.include_reserved = include_reserved
49
52
 
@@ -76,7 +76,10 @@ class SelectFileAction(BaseAction):
76
76
  self.prompt_message = prompt_message
77
77
  self.suffix_filter = suffix_filter
78
78
  self.style = style
79
- self.console = console or Console(color_system="auto")
79
+ if isinstance(console, Console):
80
+ self.console = console
81
+ elif console:
82
+ raise ValueError("`console` must be an instance of `rich.console.Console`")
80
83
  self.prompt_session = prompt_session or PromptSession()
81
84
  self.return_type = self._coerce_return_type(return_type)
82
85
 
@@ -67,7 +67,10 @@ class SelectionAction(BaseAction):
67
67
  self.return_type: SelectionReturnType = self._coerce_return_type(return_type)
68
68
  self.title = title
69
69
  self.columns = columns
70
- self.console = console or Console(color_system="auto")
70
+ if isinstance(console, Console):
71
+ self.console = console
72
+ elif console:
73
+ raise ValueError("`console` must be an instance of `rich.console.Console`")
71
74
  self.prompt_session = prompt_session or PromptSession()
72
75
  self.default_selection = default_selection
73
76
  self.prompt_message = prompt_message
@@ -40,7 +40,10 @@ class UserInputAction(BaseAction):
40
40
  )
41
41
  self.prompt_text = prompt_text
42
42
  self.validator = validator
43
- self.console = console or Console(color_system="auto")
43
+ if isinstance(console, Console):
44
+ self.console = console
45
+ elif console:
46
+ raise ValueError("`console` must be an instance of `rich.console.Console`")
44
47
  self.prompt_session = prompt_session or PromptSession()
45
48
 
46
49
  def get_infer_target(self) -> tuple[None, None]:
@@ -30,7 +30,7 @@ class BottomBar:
30
30
  key_validator: Callable[[str], bool] | None = None,
31
31
  ) -> None:
32
32
  self.columns = columns
33
- self.console = Console(color_system="auto")
33
+ self.console = Console(color_system="truecolor")
34
34
  self._named_items: dict[str, Callable[[], HTML]] = {}
35
35
  self._value_getters: dict[str, Callable[[], Any]] = CaseInsensitiveDict()
36
36
  self.toggle_keys: list[str] = []
@@ -44,7 +44,7 @@ from falyx.signals import CancelSignal
44
44
  from falyx.themes import OneColors
45
45
  from falyx.utils import ensure_async
46
46
 
47
- console = Console(color_system="auto")
47
+ console = Console(color_system="truecolor")
48
48
 
49
49
 
50
50
  class Command(BaseModel):
@@ -18,11 +18,10 @@ from falyx.action.base import BaseAction
18
18
  from falyx.command import Command
19
19
  from falyx.falyx import Falyx
20
20
  from falyx.logger import logger
21
- from falyx.parsers import CommandArgumentParser
22
21
  from falyx.retry import RetryPolicy
23
22
  from falyx.themes import OneColors
24
23
 
25
- console = Console(color_system="auto")
24
+ console = Console(color_system="truecolor")
26
25
 
27
26
 
28
27
  def wrap_if_needed(obj: Any, name=None) -> BaseAction | Command:
@@ -70,7 +70,7 @@ class ExecutionContext(BaseModel):
70
70
 
71
71
  name: str
72
72
  args: tuple = ()
73
- kwargs: dict = {}
73
+ kwargs: dict = Field(default_factory=dict)
74
74
  action: Any
75
75
  result: Any | None = None
76
76
  exception: Exception | None = None
@@ -80,8 +80,10 @@ class ExecutionContext(BaseModel):
80
80
  start_wall: datetime | None = None
81
81
  end_wall: datetime | None = None
82
82
 
83
+ index: int | None = None
84
+
83
85
  extra: dict[str, Any] = Field(default_factory=dict)
84
- console: Console = Field(default_factory=lambda: Console(color_system="auto"))
86
+ console: Console = Field(default_factory=lambda: Console(color_system="truecolor"))
85
87
 
86
88
  shared_context: SharedContext | None = None
87
89
 
@@ -118,6 +120,17 @@ class ExecutionContext(BaseModel):
118
120
  def status(self) -> str:
119
121
  return "OK" if self.success else "ERROR"
120
122
 
123
+ @property
124
+ def signature(self) -> str:
125
+ """
126
+ Returns a string representation of the action signature, including
127
+ its name and arguments.
128
+ """
129
+ args = ", ".join(map(repr, self.args))
130
+ kwargs = ", ".join(f"{key}={value!r}" for key, value in self.kwargs.items())
131
+ signature = ", ".join(filter(None, [args, kwargs]))
132
+ return f"{self.name} ({signature})"
133
+
121
134
  def as_dict(self) -> dict:
122
135
  return {
123
136
  "name": self.name,
@@ -8,7 +8,7 @@ from falyx.logger import logger
8
8
  def log_before(context: ExecutionContext):
9
9
  """Log the start of an action."""
10
10
  args = ", ".join(map(repr, context.args))
11
- kwargs = ", ".join(f"{k}={v!r}" for k, v in context.kwargs.items())
11
+ kwargs = ", ".join(f"{key}={value!r}" for key, value in context.kwargs.items())
12
12
  signature = ", ".join(filter(None, [args, kwargs]))
13
13
  logger.info("[%s] Starting -> %s(%s)", context.name, context.action, signature)
14
14
 
@@ -29,7 +29,8 @@ from __future__ import annotations
29
29
 
30
30
  from collections import defaultdict
31
31
  from datetime import datetime
32
- from typing import Dict, List
32
+ from threading import Lock
33
+ from typing import Literal
33
34
 
34
35
  from rich import box
35
36
  from rich.console import Console
@@ -70,23 +71,30 @@ class ExecutionRegistry:
70
71
  ExecutionRegistry.summary()
71
72
  """
72
73
 
73
- _store_by_name: Dict[str, List[ExecutionContext]] = defaultdict(list)
74
- _store_all: List[ExecutionContext] = []
75
- _console = Console(color_system="auto")
74
+ _store_by_name: dict[str, list[ExecutionContext]] = defaultdict(list)
75
+ _store_by_index: dict[int, ExecutionContext] = {}
76
+ _store_all: list[ExecutionContext] = []
77
+ _console = Console(color_system="truecolor")
78
+ _index = 0
79
+ _lock = Lock()
76
80
 
77
81
  @classmethod
78
82
  def record(cls, context: ExecutionContext):
79
83
  """Record an execution context."""
80
84
  logger.debug(context.to_log_line())
85
+ with cls._lock:
86
+ context.index = cls._index
87
+ cls._store_by_index[cls._index] = context
88
+ cls._index += 1
81
89
  cls._store_by_name[context.name].append(context)
82
90
  cls._store_all.append(context)
83
91
 
84
92
  @classmethod
85
- def get_all(cls) -> List[ExecutionContext]:
93
+ def get_all(cls) -> list[ExecutionContext]:
86
94
  return cls._store_all
87
95
 
88
96
  @classmethod
89
- def get_by_name(cls, name: str) -> List[ExecutionContext]:
97
+ def get_by_name(cls, name: str) -> list[ExecutionContext]:
90
98
  return cls._store_by_name.get(name, [])
91
99
 
92
100
  @classmethod
@@ -97,11 +105,79 @@ class ExecutionRegistry:
97
105
  def clear(cls):
98
106
  cls._store_by_name.clear()
99
107
  cls._store_all.clear()
108
+ cls._store_by_index.clear()
100
109
 
101
110
  @classmethod
102
- def summary(cls):
103
- table = Table(title="📊 Execution History", expand=True, box=box.SIMPLE)
104
-
111
+ def summary(
112
+ cls,
113
+ name: str = "",
114
+ index: int | None = None,
115
+ result: int | None = None,
116
+ clear: bool = False,
117
+ last_result: bool = False,
118
+ status: Literal["all", "success", "error"] = "all",
119
+ ):
120
+ if clear:
121
+ cls.clear()
122
+ cls._console.print(f"[{OneColors.GREEN}]✅ Execution history cleared.")
123
+ return
124
+
125
+ if last_result:
126
+ for ctx in reversed(cls._store_all):
127
+ if ctx.name.upper() not in [
128
+ "HISTORY",
129
+ "HELP",
130
+ "EXIT",
131
+ "VIEW EXECUTION HISTORY",
132
+ "BACK",
133
+ ]:
134
+ cls._console.print(ctx.result)
135
+ return
136
+ cls._console.print(
137
+ f"[{OneColors.DARK_RED}]❌ No valid executions found to display last result."
138
+ )
139
+ return
140
+
141
+ if result is not None and result >= 0:
142
+ try:
143
+ result_context = cls._store_by_index[result]
144
+ except KeyError:
145
+ cls._console.print(
146
+ f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
147
+ )
148
+ return
149
+ cls._console.print(f"{result_context.signature}:")
150
+ if result_context.exception:
151
+ cls._console.print(result_context.exception)
152
+ else:
153
+ cls._console.print(result_context.result)
154
+ return
155
+
156
+ if name:
157
+ contexts = cls.get_by_name(name)
158
+ if not contexts:
159
+ cls._console.print(
160
+ f"[{OneColors.DARK_RED}]❌ No executions found for action '{name}'."
161
+ )
162
+ return
163
+ title = f"📊 Execution History for '{contexts[0].name}'"
164
+ elif index is not None and index >= 0:
165
+ try:
166
+ contexts = [cls._store_by_index[index]]
167
+ print(contexts)
168
+ except KeyError:
169
+ cls._console.print(
170
+ f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
171
+ )
172
+ return
173
+ title = f"📊 Execution History for Index {index}"
174
+ else:
175
+ contexts = cls.get_all()
176
+ title = "📊 Execution History"
177
+
178
+ table = Table(title=title, expand=True, box=box.SIMPLE)
179
+
180
+ table.add_column("Index", justify="right", style="dim")
105
181
  table.add_column("Name", style="bold cyan")
106
182
  table.add_column("Start", justify="right", style="dim")
107
183
  table.add_column("End", justify="right", style="dim")
@@ -109,7 +185,7 @@ class ExecutionRegistry:
109
185
  table.add_column("Status", style="bold")
110
186
  table.add_column("Result / Exception", overflow="fold")
111
187
 
112
- for ctx in cls.get_all():
188
+ for ctx in contexts:
113
189
  start = (
114
190
  datetime.fromtimestamp(ctx.start_time).strftime("%H:%M:%S")
115
191
  if ctx.start_time
@@ -122,15 +198,19 @@ class ExecutionRegistry:
122
198
  )
123
199
  duration = f"{ctx.duration:.3f}s" if ctx.duration else "n/a"
124
200
 
125
- if ctx.exception:
126
- status = f"[{OneColors.DARK_RED}]❌ Error"
127
- result = repr(ctx.exception)
201
+ if ctx.exception and status.lower() in ["all", "error"]:
202
+ final_status = f"[{OneColors.DARK_RED}]❌ Error"
203
+ final_result = repr(ctx.exception)
204
+ elif status.lower() in ["all", "success"]:
205
+ final_status = f"[{OneColors.GREEN}]✅ Success"
206
+ final_result = repr(ctx.result)
207
+ if len(final_result) > 1000:
208
+ final_result = f"{final_result[:1000]}..."
128
209
  else:
129
- status = f"[{OneColors.GREEN}]✅ Success"
130
- result = repr(ctx.result)
131
- if len(result) > 1000:
132
- result = f"{result[:1000]}..."
210
+ continue
133
211
 
134
- table.add_row(ctx.name, start, end, duration, status, result)
212
+ table.add_row(
213
+ str(ctx.index), ctx.name, start, end, duration, final_status, final_result
214
+ )
135
215
 
136
216
  cls._console.print(table)
@@ -201,7 +201,7 @@ class Falyx:
201
201
  self.help_command: Command | None = (
202
202
  self._get_help_command() if include_help_command else None
203
203
  )
204
- self.console: Console = Console(color_system="auto", theme=get_nord_theme())
204
+ self.console: Console = Console(color_system="truecolor", theme=get_nord_theme())
205
205
  self.welcome_message: str | Markdown | dict[str, Any] = welcome_message
206
206
  self.exit_message: str | Markdown | dict[str, Any] = exit_message
207
207
  self.hooks: HookManager = HookManager()
@@ -300,6 +300,40 @@ class Falyx:
300
300
 
301
301
  def _get_history_command(self) -> Command:
302
302
  """Returns the history command for the menu."""
303
+ parser = CommandArgumentParser(
304
+ command_key="Y",
305
+ command_description="History",
306
+ command_style=OneColors.DARK_YELLOW,
307
+ aliases=["HISTORY"],
308
+ )
309
+ parser.add_argument(
310
+ "-n",
311
+ "--name",
312
+ help="Filter by execution name.",
313
+ )
314
+ parser.add_argument(
315
+ "-i",
316
+ "--index",
317
+ type=int,
318
+ help="Filter by execution index (0-based).",
319
+ )
320
+ parser.add_argument(
321
+ "-s",
322
+ "--status",
323
+ choices=["all", "success", "error"],
324
+ default="all",
325
+ help="Filter by execution status (default: all).",
326
+ )
327
+ parser.add_argument(
328
+ "-c",
329
+ "--clear",
330
+ action="store_true",
331
+ help="Clear the Execution History.",
332
+ )
333
+ parser.add_argument("-r", "--result", type=int, help="Get the result by index")
334
+ parser.add_argument(
335
+ "-l", "--last-result", action="store_true", help="Get the last result"
336
+ )
303
337
  return Command(
304
338
  key="Y",
305
339
  description="History",
@@ -307,6 +341,8 @@ class Falyx:
307
341
  action=Action(name="View Execution History", action=er.summary),
308
342
  style=OneColors.DARK_YELLOW,
309
343
  simple_help_signature=True,
344
+ arg_parser=parser,
345
+ help_text="View the execution history of commands.",
310
346
  )
311
347
 
312
348
  async def _show_help(self, tag: str = "") -> None:
@@ -760,7 +796,12 @@ class Falyx:
760
796
  def table(self) -> Table:
761
797
  """Creates or returns a custom table to display the menu commands."""
762
798
  if callable(self.custom_table):
763
- return self.custom_table(self)
799
+ custom_table = self.custom_table(self)
800
+ if not isinstance(custom_table, Table):
801
+ raise FalyxError(
802
+ "custom_table must return an instance of rich.table.Table."
803
+ )
804
+ return custom_table
764
805
  elif isinstance(self.custom_table, Table):
765
806
  return self.custom_table
766
807
  else:
@@ -798,21 +839,31 @@ class Falyx:
798
839
 
799
840
  choice = choice.upper()
800
841
  name_map = self._name_map
842
+ run_command = None
801
843
  if name_map.get(choice):
844
+ run_command = name_map[choice]
845
+ else:
846
+ prefix_matches = [
847
+ cmd for key, cmd in name_map.items() if key.startswith(choice)
848
+ ]
849
+ if len(prefix_matches) == 1:
850
+ run_command = prefix_matches[0]
851
+
852
+ if run_command:
802
853
  if not from_validate:
803
- logger.info("Command '%s' selected.", choice)
854
+ logger.info("Command '%s' selected.", run_command.key)
804
855
  if is_preview:
805
- return True, name_map[choice], args, kwargs
856
+ return True, run_command, args, kwargs
806
857
  elif self.mode in {FalyxMode.RUN, FalyxMode.RUN_ALL, FalyxMode.PREVIEW}:
807
- return False, name_map[choice], args, kwargs
858
+ return False, run_command, args, kwargs
808
859
  try:
809
- args, kwargs = await name_map[choice].parse_args(
810
- input_args, from_validate
811
- )
860
+ args, kwargs = await run_command.parse_args(input_args, from_validate)
812
861
  except (CommandArgumentError, Exception) as error:
813
862
  if not from_validate:
814
- name_map[choice].show_help()
815
- self.console.print(f"[{OneColors.DARK_RED}]❌ [{choice}]: {error}")
863
+ run_command.show_help()
864
+ self.console.print(
865
+ f"[{OneColors.DARK_RED}]❌ [{run_command.key}]: {error}"
866
+ )
816
867
  else:
817
868
  raise ValidationError(
818
869
  message=str(error), cursor_position=len(raw_choices)
@@ -820,11 +871,7 @@ class Falyx:
820
871
  return is_preview, None, args, kwargs
821
872
  except HelpSignal:
822
873
  return True, None, args, kwargs
823
- return is_preview, name_map[choice], args, kwargs
824
-
825
- prefix_matches = [cmd for key, cmd in name_map.items() if key.startswith(choice)]
826
- if len(prefix_matches) == 1:
827
- return is_preview, prefix_matches[0], args, kwargs
874
+ return is_preview, run_command, args, kwargs
828
875
 
829
876
  fuzzy_matches = get_close_matches(choice, list(name_map.keys()), n=3, cutoff=0.7)
830
877
  if fuzzy_matches:
@@ -854,12 +901,14 @@ class Falyx:
854
901
  )
855
902
  return is_preview, None, args, kwargs
856
903
 
857
- def _create_context(self, selected_command: Command) -> ExecutionContext:
858
- """Creates a context dictionary for the selected command."""
904
+ def _create_context(
905
+ self, selected_command: Command, args: tuple, kwargs: dict[str, Any]
906
+ ) -> ExecutionContext:
907
+ """Creates an ExecutionContext object for the selected command."""
859
908
  return ExecutionContext(
860
909
  name=selected_command.description,
861
- args=tuple(),
862
- kwargs={},
910
+ args=args,
911
+ kwargs=kwargs,
863
912
  action=selected_command,
864
913
  )
865
914
 
@@ -893,7 +942,7 @@ class Falyx:
893
942
  logger.info("Back selected: exiting %s", self.get_title())
894
943
  return False
895
944
 
896
- context = self._create_context(selected_command)
945
+ context = self._create_context(selected_command, args, kwargs)
897
946
  context.start_timer()
898
947
  try:
899
948
  await self.hooks.trigger(HookType.BEFORE, context)
@@ -938,7 +987,7 @@ class Falyx:
938
987
  selected_command.description,
939
988
  )
940
989
 
941
- context = self._create_context(selected_command)
990
+ context = self._create_context(selected_command, args, kwargs)
942
991
  context.start_timer()
943
992
  try:
944
993
  await self.hooks.trigger(HookType.BEFORE, context)
@@ -98,7 +98,7 @@ commands:
98
98
  aliases: [clean, cleanup]
99
99
  """
100
100
 
101
- console = Console(color_system="auto")
101
+ console = Console(color_system="truecolor")
102
102
 
103
103
 
104
104
  def init_project(name: str) -> None:
@@ -159,7 +159,7 @@ class CommandArgumentParser:
159
159
  aliases: list[str] | None = None,
160
160
  ) -> None:
161
161
  """Initialize the CommandArgumentParser."""
162
- self.console = Console(color_system="auto")
162
+ self.console = Console(color_system="truecolor")
163
163
  self.command_key: str = command_key
164
164
  self.command_description: str = command_description
165
165
  self.command_style: str = command_style
@@ -629,7 +629,10 @@ class CommandArgumentParser:
629
629
  consumed_positional_indicies.add(j)
630
630
 
631
631
  if i < len(args):
632
- raise CommandArgumentError(f"Unexpected positional argument: {args[i:]}")
632
+ plural = "s" if len(args[i:]) > 1 else ""
633
+ raise CommandArgumentError(
634
+ f"Unexpected positional argument{plural}: {', '.join(args[i:])}"
635
+ )
633
636
 
634
637
  return i
635
638
 
@@ -273,7 +273,7 @@ async def prompt_for_index(
273
273
  show_table: bool = True,
274
274
  ) -> int:
275
275
  prompt_session = prompt_session or PromptSession()
276
- console = console or Console(color_system="auto")
276
+ console = console or Console(color_system="truecolor")
277
277
 
278
278
  if show_table:
279
279
  console.print(table, justify="center")
@@ -298,7 +298,7 @@ async def prompt_for_selection(
298
298
  ) -> str:
299
299
  """Prompt the user to select a key from a set of options. Return the selected key."""
300
300
  prompt_session = prompt_session or PromptSession()
301
- console = console or Console(color_system="auto")
301
+ console = console or Console(color_system="truecolor")
302
302
 
303
303
  if show_table:
304
304
  console.print(table, justify="center")
@@ -351,7 +351,7 @@ async def select_value_from_list(
351
351
  highlight=highlight,
352
352
  )
353
353
  prompt_session = prompt_session or PromptSession()
354
- console = console or Console(color_system="auto")
354
+ console = console or Console(color_system="truecolor")
355
355
 
356
356
  selection_index = await prompt_for_index(
357
357
  len(selections) - 1,
@@ -376,7 +376,7 @@ async def select_key_from_dict(
376
376
  ) -> Any:
377
377
  """Prompt for a key from a dict, returns the key."""
378
378
  prompt_session = prompt_session or PromptSession()
379
- console = console or Console(color_system="auto")
379
+ console = console or Console(color_system="truecolor")
380
380
 
381
381
  console.print(table, justify="center")
382
382
 
@@ -401,7 +401,7 @@ async def select_value_from_dict(
401
401
  ) -> Any:
402
402
  """Prompt for a key from a dict, but return the value."""
403
403
  prompt_session = prompt_session or PromptSession()
404
- console = console or Console(color_system="auto")
404
+ console = console or Console(color_system="truecolor")
405
405
 
406
406
  console.print(table, justify="center")
407
407
 
@@ -0,0 +1 @@
1
+ __version__ = "0.1.50"
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "falyx"
3
- version = "0.1.48"
3
+ version = "0.1.50"
4
4
  description = "Reliable and introspectable async CLI action framework."
5
5
  authors = ["Roland Thomas Jr <roland@rtj.dev>"]
6
6
  license = "MIT"
@@ -1 +0,0 @@
1
- __version__ = "0.1.48"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes