Reliable and introspectable async CLI action framework.
Project description
โ๏ธ Falyx
Falyx is a battle-ready, introspectable CLI framework for building resilient, asynchronous workflows with:
- โ Modular action chaining and rollback
- ๐ Built-in retry handling
- โ๏ธ Full lifecycle hooks (before, after, success, error, teardown)
- ๐ Execution tracing, logging, and introspection
- ๐งโโ๏ธ Async-first design with Process support
- ๐งฉ Extensible CLI menus, customizable bottom bars, and keyboard shortcuts
Built for developers who value clarity, resilience, and visibility in their terminal workflows.
โจ Why Falyx?
Modern CLI tools deserve the same resilience as production systems. Falyx makes it easy to:
- Compose workflows using
Action,ChainedAction, orActionGroup - Inject the result of one step into the next (
last_result/auto_inject) - Handle flaky operations with retries, backoff, and jitter
- Roll back safely on failure with structured undo logic
- Add observability with timing, tracebacks, and lifecycle hooks
- Run in both interactive and headless (scriptable) modes
- Support config-driven workflows with YAML or TOML
- Visualize tagged command groups and menu state via Rich tables
๐ง Installation
pip install falyx
Or install from source:
git clone https://github.com/rolandtjr/falyx.git
cd falyx
poetry install
โก Quick Example
import asyncio
import random
from falyx import Falyx
from falyx.action import Action, ChainedAction
# A flaky async step that fails randomly
async def flaky_step():
await asyncio.sleep(0.2)
if random.random() < 0.5:
raise RuntimeError("Random failure!")
print("ok")
return "ok"
# Create the actions
step1 = Action(name="step_1", action=flaky_step)
step2 = Action(name="step_2", action=flaky_step)
# Chain the actions
chain = ChainedAction(name="my_pipeline", actions=[step1, step2])
# Create the CLI menu
falyx = Falyx("๐ Falyx Demo")
falyx.add_command(
key="R",
description="Run My Pipeline",
action=chain,
preview_before_confirm=True,
confirm=True,
retry_all=True,
spinner=True,
style="cyan",
)
# Entry point
if __name__ == "__main__":
asyncio.run(falyx.run())
$ python simple.py
๐ Falyx Demo
[R] Run My Pipeline
[H] Help [Y] History [X] Exit
>
$ python simple.py run r
Command: 'R' โ Run My Pipeline
โโโ โ ChainedAction 'my_pipeline'
โโโ โ Action 'step_1'
โ โป Retries: 3x, delay 1.0s, backoff 2.0x
โโโ โ Action 'step_2'
โป Retries: 3x, delay 1.0s, backoff 2.0x
โ Confirm execution of R โ Run My Pipeline (calls `my_pipeline`) [Y/n] > y
[2025-07-20 09:29:35] WARNING Retry attempt 1/3 failed due to 'Random failure!'.
ok
[2025-07-20 09:29:38] WARNING Retry attempt 1/3 failed due to 'Random failure!'.
ok
๐ฆ Core Features
- โ
Async-native
Action,ChainedAction,ActionGroup,ProcessAction - ๐ Retry policies with delay, backoff, jitter โ opt-in per action or globally
- โ Rollbacks and lifecycle hooks for chained execution
- ๐๏ธ Headless or interactive CLI powered by
argparse+prompt_toolkit - ๐ In-memory
ExecutionRegistrywith result tracking, timing, and tracebacks - ๐ CLI menu construction via config files or Python
- โก Bottom bar toggle switches and counters with
Ctrl+<key>shortcuts - ๐ Structured confirmation prompts and help rendering
- ๐ชต Flexible logging: Rich console for devs, JSON logs for ops
๐งฐ Building Blocks
Action: A single unit of async (or sync) logicChainedAction: Execute a sequence of actions, with rollback and injectionActionGroup: Run actions concurrently and collect resultsProcessAction: Usemultiprocessingfor CPU-bound workflowsFalyx: Interactive or headless CLI controller with history, menus, and themingExecutionContext: Metadata store per invocation (name, args, result, timing)HookManager: Attachbefore,after,on_success,on_error,on_teardown
๐ Logging
2025-07-20 09:29:32 [falyx] [INFO] Command 'R' selected.
2025-07-20 09:29:32 [falyx] [INFO] [run_key] Executing: R โ Run My Pipeline
2025-07-20 09:29:33 [falyx] [INFO] [my_pipeline] Starting -> ChainedAction(name=my_pipeline, actions=['step_1', 'step_2'], args=(), kwargs={}, auto_inject=False, return_list=False)()
2025-07-20 09:29:33 [falyx] [INFO] [step_1] Retrying (1/3) in 1.0s due to 'Random failure!'...
2025-07-20 09:29:35 [falyx] [WARNING] [step_1] Retry attempt 1/3 failed due to 'Random failure!'.
2025-07-20 09:29:35 [falyx] [INFO] [step_1] Retrying (2/3) in 2.0s due to 'Random failure!'...
2025-07-20 09:29:37 [falyx] [INFO] [step_1] Retry succeeded on attempt 2.
2025-07-20 09:29:37 [falyx] [INFO] [step_1] Recovered: step_1
2025-07-20 09:29:37 [falyx] [DEBUG] [step_1] status=OK duration=3.627s result='ok' exception=None
2025-07-20 09:29:37 [falyx] [INFO] [step_2] Retrying (1/3) in 1.0s due to 'Random failure!'...
2025-07-20 09:29:38 [falyx] [WARNING] [step_2] Retry attempt 1/3 failed due to 'Random failure!'.
2025-07-20 09:29:38 [falyx] [INFO] [step_2] Retrying (2/3) in 2.0s due to 'Random failure!'...
2025-07-20 09:29:40 [falyx] [INFO] [step_2] Retry succeeded on attempt 2.
2025-07-20 09:29:40 [falyx] [INFO] [step_2] Recovered: step_2
2025-07-20 09:29:40 [falyx] [DEBUG] [step_2] status=OK duration=3.609s result='ok' exception=None
2025-07-20 09:29:40 [falyx] [DEBUG] [my_pipeline] Success -> Result: 'ok'
2025-07-20 09:29:40 [falyx] [DEBUG] [my_pipeline] Finished in 7.237s
2025-07-20 09:29:40 [falyx] [DEBUG] [my_pipeline] status=OK duration=7.237s result='ok' exception=None
2025-07-20 09:29:40 [falyx] [DEBUG] [Run My Pipeline] status=OK duration=7.238s result='ok' exception=None
๐ History Tracking
View full execution history:
> history
๐ Execution History
Index Name Start End Duration Status Result / Exception
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
0 step_1 09:23:55 09:23:55 0.201s โ
Success 'ok'
1 step_2 09:23:55 09:24:03 7.829s โ Error RuntimeError('Random failure!')
2 my_pipeline 09:23:55 09:24:03 8.080s โ Error RuntimeError('Random failure!')
3 Run My Pipeline 09:23:55 09:24:03 8.082s โ Error RuntimeError('Random failure!')
Inspect result by index:
> history --result-index 0
Action(name='step_1', action=flaky_step, args=(), kwargs={}, retry=True, rollback=False) ():
ok
Print last result includes tracebacks:
> history --last-result
Command(key='R', description='Run My Pipeline' action='ChainedAction(name=my_pipeline, actions=['step_1', 'step_2'],
args=(), kwargs={}, auto_inject=False, return_list=False)') ():
Traceback (most recent call last):
File ".../falyx/command.py", line 291, in __call__
result = await self.action(*combined_args, **combined_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../falyx/action/base_action.py", line 91, in __call__
return await self._run(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../falyx/action/chained_action.py", line 212, in _run
result = await prepared(*combined_args, **updated_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../falyx/action/base_action.py", line 91, in __call__
return await self._run(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../falyx/action/action.py", line 157, in _run
result = await self.action(*combined_args, **combined_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../falyx/examples/simple.py", line 15, in flaky_step
raise RuntimeError("Random failure!")
RuntimeError: Random failure!
๐ง Design Philosophy
โLike a phalanx: organized, resilient, and reliable.โ
Falyx is designed for developers who donโt just want CLI tools to run โ they want them to fail meaningfully, recover intentionally, and log clearly.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file falyx-0.2.0.tar.gz.
File metadata
- Download URL: falyx-0.2.0.tar.gz
- Upload date:
- Size: 170.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.13.5 Linux/6.19.14-arch1-1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c349e74b7d40884f0f1f4474b3c27970362382731fc3a3bf2472b9236da188c1
|
|
| MD5 |
4ea02b5d89112742a2db9827bbe1e0cf
|
|
| BLAKE2b-256 |
5895d4e5bd10f57b4ac4f6888e3d6efcb2138bde0e9f3b634252e111bc1ea626
|
File details
Details for the file falyx-0.2.0-py3-none-any.whl.
File metadata
- Download URL: falyx-0.2.0-py3-none-any.whl
- Upload date:
- Size: 220.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.13.5 Linux/6.19.14-arch1-1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed5d24ab1739234d8e33addd4a292de00e50d11f2ba203adc3f21232a656a277
|
|
| MD5 |
fdf9c21ab332a1883dfd61fe57b656c2
|
|
| BLAKE2b-256 |
1de39dee37b9c2e4391c302de14ef03440093e8db32b7e12bc5fcb1d4a49aa19
|