Rename falyx.action.types.FileReturnType -> falyx.action.action_types.FileType, falyx.action.base -> falyx.action.base_action, argparse tweaks for custom cli programs

This commit is contained in:
2025-06-10 23:03:09 -04:00
parent 2d1177e820
commit 38f5f1e934
29 changed files with 91 additions and 65 deletions

View File

@ -2,19 +2,24 @@ import asyncio
from falyx import Falyx from falyx import Falyx
from falyx.action import SelectFileAction from falyx.action import SelectFileAction
from falyx.action.types import FileReturnType from falyx.action.action_types import FileType
sf = SelectFileAction( sf = SelectFileAction(
name="select_file", name="select_file",
suffix_filter=".yaml", suffix_filter=".yaml",
title="Select a YAML file", title="Select a YAML file",
prompt_message="Choose 2 > ", prompt_message="Choose 2 > ",
return_type=FileReturnType.TEXT, return_type=FileType.TEXT,
columns=3, columns=3,
number_selections=2, number_selections=2,
) )
flx = Falyx() flx = Falyx(
title="File Selection Example",
description="This example demonstrates how to select files using Falyx.",
version="1.0.0",
program="file_select.py",
)
flx.add_command( flx.add_command(
key="S", key="S",

View File

@ -8,7 +8,7 @@ Licensed under the MIT License. See LICENSE file for details.
from .action import Action from .action import Action
from .action_factory import ActionFactoryAction from .action_factory import ActionFactoryAction
from .action_group import ActionGroup from .action_group import ActionGroup
from .base import BaseAction from .base_action import BaseAction
from .chained_action import ChainedAction from .chained_action import ChainedAction
from .fallback_action import FallbackAction from .fallback_action import FallbackAction
from .http_action import HTTPAction from .http_action import HTTPAction

View File

@ -6,7 +6,7 @@ from typing import Any, Callable
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookManager, HookType from falyx.hook_manager import HookManager, HookType

View File

@ -4,7 +4,7 @@ from typing import Any, Callable
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookType from falyx.hook_manager import HookType

View File

@ -2,12 +2,12 @@
"""action_group.py""" """action_group.py"""
import asyncio import asyncio
import random import random
from typing import Any, Callable from typing import Any, Callable, Sequence
from rich.tree import Tree from rich.tree import Tree
from falyx.action.action import Action from falyx.action.action import Action
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.action.mixins import ActionListMixin from falyx.action.mixins import ActionListMixin
from falyx.context import ExecutionContext, SharedContext from falyx.context import ExecutionContext, SharedContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
@ -54,7 +54,7 @@ class ActionGroup(BaseAction, ActionListMixin):
def __init__( def __init__(
self, self,
name: str, name: str,
actions: list[BaseAction] | None = None, actions: Sequence[BaseAction | Callable[..., Any]] | None = None,
*, *,
hooks: HookManager | None = None, hooks: HookManager | None = None,
inject_last_result: bool = False, inject_last_result: bool = False,
@ -70,7 +70,7 @@ class ActionGroup(BaseAction, ActionListMixin):
if actions: if actions:
self.set_actions(actions) self.set_actions(actions)
def _wrap_if_needed(self, action: BaseAction | Any) -> BaseAction: def _wrap_if_needed(self, action: BaseAction | Callable[..., Any]) -> BaseAction:
if isinstance(action, BaseAction): if isinstance(action, BaseAction):
return action return action
elif callable(action): elif callable(action):
@ -81,12 +81,18 @@ class ActionGroup(BaseAction, ActionListMixin):
f"{type(action).__name__}" f"{type(action).__name__}"
) )
def add_action(self, action: BaseAction | Any) -> None: def add_action(self, action: BaseAction | Callable[..., Any]) -> None:
action = self._wrap_if_needed(action) action = self._wrap_if_needed(action)
super().add_action(action) super().add_action(action)
if hasattr(action, "register_teardown") and callable(action.register_teardown): if hasattr(action, "register_teardown") and callable(action.register_teardown):
action.register_teardown(self.hooks) action.register_teardown(self.hooks)
def set_actions(self, actions: Sequence[BaseAction | Callable[..., Any]]) -> None:
"""Replaces the current action list with a new one."""
self.actions.clear()
for action in actions:
self.add_action(action)
def get_infer_target(self) -> tuple[Callable[..., Any] | None, dict[str, Any] | None]: def get_infer_target(self) -> tuple[Callable[..., Any] | None, dict[str, Any] | None]:
arg_defs = same_argument_definitions(self.actions) arg_defs = same_argument_definitions(self.actions)
if arg_defs: if arg_defs:

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from enum import Enum from enum import Enum
class FileReturnType(Enum): class FileType(Enum):
"""Enum for file return types.""" """Enum for file return types."""
TEXT = "text" TEXT = "text"
@ -28,7 +28,7 @@ class FileReturnType(Enum):
return aliases.get(value, value) return aliases.get(value, value)
@classmethod @classmethod
def _missing_(cls, value: object) -> FileReturnType: def _missing_(cls, value: object) -> FileType:
if isinstance(value, str): if isinstance(value, str):
normalized = value.lower() normalized = value.lower()
alias = cls._get_alias(normalized) alias = cls._get_alias(normalized)
@ -36,7 +36,7 @@ class FileReturnType(Enum):
if member.value == alias: if member.value == alias:
return member return member
valid = ", ".join(member.value for member in cls) valid = ", ".join(member.value for member in cls)
raise ValueError(f"Invalid FileReturnType: '{value}'. Must be one of: {valid}") raise ValueError(f"Invalid FileType: '{value}'. Must be one of: {valid}")
class SelectionReturnType(Enum): class SelectionReturnType(Enum):

View File

@ -1,5 +1,5 @@
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed # Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
"""base.py """base_action.py
Core action system for Falyx. Core action system for Falyx.

View File

@ -2,12 +2,12 @@
"""chained_action.py""" """chained_action.py"""
from __future__ import annotations from __future__ import annotations
from typing import Any, Callable from typing import Any, Callable, Sequence
from rich.tree import Tree from rich.tree import Tree
from falyx.action.action import Action from falyx.action.action import Action
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.action.fallback_action import FallbackAction from falyx.action.fallback_action import FallbackAction
from falyx.action.literal_input_action import LiteralInputAction from falyx.action.literal_input_action import LiteralInputAction
from falyx.action.mixins import ActionListMixin from falyx.action.mixins import ActionListMixin
@ -47,7 +47,7 @@ class ChainedAction(BaseAction, ActionListMixin):
def __init__( def __init__(
self, self,
name: str, name: str,
actions: list[BaseAction | Any] | None = None, actions: Sequence[BaseAction | Callable[..., Any]] | None = None,
*, *,
hooks: HookManager | None = None, hooks: HookManager | None = None,
inject_last_result: bool = False, inject_last_result: bool = False,
@ -67,7 +67,7 @@ class ChainedAction(BaseAction, ActionListMixin):
if actions: if actions:
self.set_actions(actions) self.set_actions(actions)
def _wrap_if_needed(self, action: BaseAction | Any) -> BaseAction: def _wrap_if_needed(self, action: BaseAction | Callable[..., Any]) -> BaseAction:
if isinstance(action, BaseAction): if isinstance(action, BaseAction):
return action return action
elif callable(action): elif callable(action):
@ -75,7 +75,7 @@ class ChainedAction(BaseAction, ActionListMixin):
else: else:
return LiteralInputAction(action) return LiteralInputAction(action)
def add_action(self, action: BaseAction | Any) -> None: def add_action(self, action: BaseAction | Callable[..., Any]) -> None:
action = self._wrap_if_needed(action) action = self._wrap_if_needed(action)
if self.actions and self.auto_inject and not action.inject_last_result: if self.actions and self.auto_inject and not action.inject_last_result:
action.inject_last_result = True action.inject_last_result = True
@ -83,6 +83,12 @@ class ChainedAction(BaseAction, ActionListMixin):
if hasattr(action, "register_teardown") and callable(action.register_teardown): if hasattr(action, "register_teardown") and callable(action.register_teardown):
action.register_teardown(self.hooks) action.register_teardown(self.hooks)
def set_actions(self, actions: Sequence[BaseAction | Callable[..., Any]]) -> None:
"""Replaces the current action list with a new one."""
self.actions.clear()
for action in actions:
self.add_action(action)
def get_infer_target(self) -> tuple[Callable[..., Any] | None, dict[str, Any] | None]: def get_infer_target(self) -> tuple[Callable[..., Any] | None, dict[str, Any] | None]:
if self.actions: if self.actions:
return self.actions[0].get_infer_target() return self.actions[0].get_infer_target()

View File

@ -21,7 +21,7 @@ from typing import Any, Callable
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookManager, HookType from falyx.hook_manager import HookManager, HookType

View File

@ -7,7 +7,7 @@ from rich.console import Console
from rich.table import Table from rich.table import Table
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookType from falyx.hook_manager import HookType

View File

@ -1,6 +1,8 @@
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed # Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
"""mixins.py""" """mixins.py"""
from falyx.action.base import BaseAction from typing import Sequence
from falyx.action.base_action import BaseAction
class ActionListMixin: class ActionListMixin:
@ -9,7 +11,7 @@ class ActionListMixin:
def __init__(self) -> None: def __init__(self) -> None:
self.actions: list[BaseAction] = [] self.actions: list[BaseAction] = []
def set_actions(self, actions: list[BaseAction]) -> None: def set_actions(self, actions: Sequence[BaseAction]) -> None:
"""Replaces the current action list with a new one.""" """Replaces the current action list with a new one."""
self.actions.clear() self.actions.clear()
for action in actions: for action in actions:

View File

@ -9,7 +9,7 @@ from typing import Any, Callable
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookManager, HookType from falyx.hook_manager import HookManager, HookType

View File

@ -11,7 +11,7 @@ from typing import Any, Callable
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext, SharedContext from falyx.context import ExecutionContext, SharedContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookManager, HookType from falyx.hook_manager import HookManager, HookType

View File

@ -7,7 +7,7 @@ from prompt_toolkit.formatted_text import FormattedText, merge_formatted_text
from rich.console import Console from rich.console import Console
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookType from falyx.hook_manager import HookType

View File

@ -14,8 +14,8 @@ from prompt_toolkit import PromptSession
from rich.console import Console from rich.console import Console
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.action_types import FileType
from falyx.action.types import FileReturnType from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookType from falyx.hook_manager import HookType
@ -50,7 +50,7 @@ class SelectFileAction(BaseAction):
prompt_message (str): Message to display when prompting for selection. prompt_message (str): Message to display when prompting for selection.
style (str): Style for the selection options. style (str): Style for the selection options.
suffix_filter (str | None): Restrict to certain file types. suffix_filter (str | None): Restrict to certain file types.
return_type (FileReturnType): What to return (path, content, parsed). return_type (FileType): What to return (path, content, parsed).
console (Console | None): Console instance for output. console (Console | None): Console instance for output.
prompt_session (PromptSession | None): Prompt session for user input. prompt_session (PromptSession | None): Prompt session for user input.
""" """
@ -65,7 +65,7 @@ class SelectFileAction(BaseAction):
prompt_message: str = "Choose > ", prompt_message: str = "Choose > ",
style: str = OneColors.WHITE, style: str = OneColors.WHITE,
suffix_filter: str | None = None, suffix_filter: str | None = None,
return_type: FileReturnType | str = FileReturnType.PATH, return_type: FileType | str = FileType.PATH,
number_selections: int | str = 1, number_selections: int | str = 1,
separator: str = ",", separator: str = ",",
allow_duplicates: bool = False, allow_duplicates: bool = False,
@ -104,35 +104,35 @@ class SelectFileAction(BaseAction):
else: else:
raise ValueError("number_selections must be a positive integer or one of '*'") raise ValueError("number_selections must be a positive integer or one of '*'")
def _coerce_return_type(self, return_type: FileReturnType | str) -> FileReturnType: def _coerce_return_type(self, return_type: FileType | str) -> FileType:
if isinstance(return_type, FileReturnType): if isinstance(return_type, FileType):
return return_type return return_type
return FileReturnType(return_type) return FileType(return_type)
def get_options(self, files: list[Path]) -> dict[str, SelectionOption]: def get_options(self, files: list[Path]) -> dict[str, SelectionOption]:
value: Any value: Any
options = {} options = {}
for index, file in enumerate(files): for index, file in enumerate(files):
try: try:
if self.return_type == FileReturnType.TEXT: if self.return_type == FileType.TEXT:
value = file.read_text(encoding="UTF-8") value = file.read_text(encoding="UTF-8")
elif self.return_type == FileReturnType.PATH: elif self.return_type == FileType.PATH:
value = file value = file
elif self.return_type == FileReturnType.JSON: elif self.return_type == FileType.JSON:
value = json.loads(file.read_text(encoding="UTF-8")) value = json.loads(file.read_text(encoding="UTF-8"))
elif self.return_type == FileReturnType.TOML: elif self.return_type == FileType.TOML:
value = toml.loads(file.read_text(encoding="UTF-8")) value = toml.loads(file.read_text(encoding="UTF-8"))
elif self.return_type == FileReturnType.YAML: elif self.return_type == FileType.YAML:
value = yaml.safe_load(file.read_text(encoding="UTF-8")) value = yaml.safe_load(file.read_text(encoding="UTF-8"))
elif self.return_type == FileReturnType.CSV: elif self.return_type == FileType.CSV:
with open(file, newline="", encoding="UTF-8") as csvfile: with open(file, newline="", encoding="UTF-8") as csvfile:
reader = csv.reader(csvfile) reader = csv.reader(csvfile)
value = list(reader) value = list(reader)
elif self.return_type == FileReturnType.TSV: elif self.return_type == FileType.TSV:
with open(file, newline="", encoding="UTF-8") as tsvfile: with open(file, newline="", encoding="UTF-8") as tsvfile:
reader = csv.reader(tsvfile, delimiter="\t") reader = csv.reader(tsvfile, delimiter="\t")
value = list(reader) value = list(reader)
elif self.return_type == FileReturnType.XML: elif self.return_type == FileType.XML:
tree = ET.parse(file, parser=ET.XMLParser(encoding="UTF-8")) tree = ET.parse(file, parser=ET.XMLParser(encoding="UTF-8"))
root = tree.getroot() root = tree.getroot()
value = ET.tostring(root, encoding="unicode") value = ET.tostring(root, encoding="unicode")

View File

@ -6,8 +6,8 @@ from prompt_toolkit import PromptSession
from rich.console import Console from rich.console import Console
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.action_types import SelectionReturnType
from falyx.action.types import SelectionReturnType from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookType from falyx.hook_manager import HookType

View File

@ -5,7 +5,7 @@ from prompt_toolkit.validation import Validator
from rich.console import Console from rich.console import Console
from rich.tree import Tree from rich.tree import Tree
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er
from falyx.hook_manager import HookType from falyx.hook_manager import HookType

View File

@ -27,7 +27,7 @@ from rich.console import Console
from rich.tree import Tree from rich.tree import Tree
from falyx.action.action import Action from falyx.action.action import Action
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
from falyx.debug import register_debug_hooks from falyx.debug import register_debug_hooks
from falyx.execution_registry import ExecutionRegistry as er from falyx.execution_registry import ExecutionRegistry as er

View File

@ -14,7 +14,7 @@ from pydantic import BaseModel, Field, field_validator, model_validator
from rich.console import Console from rich.console import Console
from falyx.action.action import Action from falyx.action.action import Action
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.command import Command from falyx.command import Command
from falyx.falyx import Falyx from falyx.falyx import Falyx
from falyx.logger import logger from falyx.logger import logger

View File

@ -43,7 +43,7 @@ from rich.markdown import Markdown
from rich.table import Table from rich.table import Table
from falyx.action.action import Action from falyx.action.action import Action
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.bottom_bar import BottomBar from falyx.bottom_bar import BottomBar
from falyx.command import Command from falyx.command import Command
from falyx.context import ExecutionContext from falyx.context import ExecutionContext
@ -346,7 +346,6 @@ class Falyx:
aliases=["HISTORY"], aliases=["HISTORY"],
action=Action(name="View Execution History", action=er.summary), action=Action(name="View Execution History", action=er.summary),
style=OneColors.DARK_YELLOW, style=OneColors.DARK_YELLOW,
simple_help_signature=True,
arg_parser=parser, arg_parser=parser,
help_text="View the execution history of commands.", help_text="View the execution history of commands.",
) )
@ -1152,7 +1151,7 @@ class Falyx:
sys.exit(0) sys.exit(0)
if self.cli_args.command == "version" or self.cli_args.version: if self.cli_args.command == "version" or self.cli_args.version:
self.console.print(f"[{self.version_style}]{self.program} v{__version__}[/]") self.console.print(f"[{self.version_style}]{self.program} v{self.version}[/]")
sys.exit(0) sys.exit(0)
if self.cli_args.command == "preview": if self.cli_args.command == "preview":

View File

@ -4,7 +4,7 @@ from dataclasses import dataclass
from prompt_toolkit.formatted_text import FormattedText from prompt_toolkit.formatted_text import FormattedText
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.signals import BackSignal, QuitSignal from falyx.signals import BackSignal, QuitSignal
from falyx.themes import OneColors from falyx.themes import OneColors
from falyx.utils import CaseInsensitiveDict from falyx.utils import CaseInsensitiveDict

View File

@ -3,7 +3,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.parser.argument_action import ArgumentAction from falyx.parser.argument_action import ArgumentAction

View File

@ -9,7 +9,7 @@ from rich.console import Console
from rich.markup import escape from rich.markup import escape
from rich.text import Text from rich.text import Text
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.exceptions import CommandArgumentError from falyx.exceptions import CommandArgumentError
from falyx.parser.argument import Argument from falyx.parser.argument import Argument
from falyx.parser.argument_action import ArgumentAction from falyx.parser.argument_action import ArgumentAction

View File

@ -76,14 +76,14 @@ def get_root_parser(
help="Run in non-interactive mode with all prompts bypassed.", help="Run in non-interactive mode with all prompts bypassed.",
) )
parser.add_argument( parser.add_argument(
"-v", "--verbose", action="store_true", help="Enable debug logging for Falyx." "-v", "--verbose", action="store_true", help=f"Enable debug logging for {prog}."
) )
parser.add_argument( parser.add_argument(
"--debug-hooks", "--debug-hooks",
action="store_true", action="store_true",
help="Enable default lifecycle debug logging", help="Enable default lifecycle debug logging",
) )
parser.add_argument("--version", action="store_true", help="Show Falyx version") parser.add_argument("--version", action="store_true", help=f"Show {prog} version")
return parser return parser
@ -98,7 +98,6 @@ def get_subparsers(
subparsers = parser.add_subparsers( subparsers = parser.add_subparsers(
title=title, title=title,
description=description, description=description,
metavar="COMMAND",
dest="command", dest="command",
) )
return subparsers return subparsers
@ -124,6 +123,8 @@ def get_arg_parsers(
subparsers: _SubParsersAction | None = None, subparsers: _SubParsersAction | None = None,
) -> FalyxParsers: ) -> FalyxParsers:
"""Returns the argument parser for the CLI.""" """Returns the argument parser for the CLI."""
if epilog is None:
epilog = f"Tip: Use '{prog} run ?[COMMAND]' to preview any command from the CLI."
if root_parser is None: if root_parser is None:
parser = get_root_parser( parser = get_root_parser(
prog=prog, prog=prog,
@ -145,7 +146,14 @@ def get_arg_parsers(
parser = root_parser parser = root_parser
if subparsers is None: if subparsers is None:
subparsers = get_subparsers(parser) if prog == "falyx":
subparsers = get_subparsers(
parser,
title="Falyx Commands",
description="Available commands for the Falyx CLI.",
)
else:
subparsers = get_subparsers(parser, title="subcommands", description=None)
if not isinstance(subparsers, _SubParsersAction): if not isinstance(subparsers, _SubParsersAction):
raise TypeError("subparsers must be an instance of _SubParsersAction") raise TypeError("subparsers must be an instance of _SubParsersAction")
@ -154,10 +162,10 @@ def get_arg_parsers(
if isinstance(commands, dict): if isinstance(commands, dict):
for command in commands.values(): for command in commands.values():
run_description.append(command.usage) run_description.append(command.usage)
command_description = command.description or command.help_text command_description = command.help_text or command.description
run_description.append(f"{' '*24}{command_description}") run_description.append(f"{' '*24}{command_description}")
run_epilog = ( run_epilog = (
"Tip: Use 'falyx run ?[COMMAND]' to preview commands by their key or alias." f"Tip: Use '{prog} run ?[COMMAND]' to preview commands by their key or alias."
) )
run_parser = subparsers.add_parser( run_parser = subparsers.add_parser(
"run", "run",
@ -259,7 +267,7 @@ def get_arg_parsers(
"-t", "--tag", help="Filter commands by tag (case-insensitive)", default=None "-t", "--tag", help="Filter commands by tag (case-insensitive)", default=None
) )
version_parser = subparsers.add_parser("version", help="Show the Falyx version") version_parser = subparsers.add_parser("version", help=f"Show {prog} version")
return FalyxParsers( return FalyxParsers(
root=parser, root=parser,

View File

@ -6,7 +6,7 @@ from typing import Any, Literal, Union, get_args, get_origin
from dateutil import parser as date_parser from dateutil import parser as date_parser
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.logger import logger from falyx.logger import logger
from falyx.parser.signature import infer_args_from_func from falyx.parser.signature import infer_args_from_func

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from typing import Any, Awaitable, Protocol, runtime_checkable from typing import Any, Awaitable, Protocol, runtime_checkable
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
@runtime_checkable @runtime_checkable

View File

@ -1,7 +1,7 @@
# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed # Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
"""retry_utils.py""" """retry_utils.py"""
from falyx.action.action import Action from falyx.action.action import Action
from falyx.action.base import BaseAction from falyx.action.base_action import BaseAction
from falyx.hook_manager import HookType from falyx.hook_manager import HookType
from falyx.retry import RetryHandler, RetryPolicy from falyx.retry import RetryHandler, RetryPolicy

View File

@ -1 +1 @@
__version__ = "0.1.52" __version__ = "0.1.53"

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "falyx" name = "falyx"
version = "0.1.52" version = "0.1.53"
description = "Reliable and introspectable async CLI action framework." description = "Reliable and introspectable async CLI action framework."
authors = ["Roland Thomas Jr <roland@rtj.dev>"] authors = ["Roland Thomas Jr <roland@rtj.dev>"]
license = "MIT" license = "MIT"