From 4890d170d29e50db322b48c092ab7e14d25da111 Mon Sep 17 00:00:00 2001 From: lesh Date: Mon, 27 Oct 2025 10:06:42 +0200 Subject: [PATCH 1/5] loader experiment --- dimos/utils/cli/human/humanclianim.py | 171 ++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 dimos/utils/cli/human/humanclianim.py diff --git a/dimos/utils/cli/human/humanclianim.py b/dimos/utils/cli/human/humanclianim.py new file mode 100644 index 0000000000..f098c183df --- /dev/null +++ b/dimos/utils/cli/human/humanclianim.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# Copyright 2025 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import threading +import time + +from terminaltexteffects import Color + +from dimos.utils.cli import theme + +# Global to store the imported main function +_humancli_main = None +_import_complete = threading.Event() + +print(theme.ACCENT) + + +def import_cli_in_background(): + """Import the heavy CLI modules in the background""" + global _humancli_main + try: + from dimos.utils.cli.human.humancli import main as humancli_main + + _humancli_main = humancli_main + except Exception as e: + print(f"Failed to import CLI: {e}") + finally: + _import_complete.set() + + +def get_effect_config(effect_name): + """Get hardcoded configuration for a specific effect""" + # Hardcoded configs for each effect + configs = { + "randomsequence": { + "speed": 0.075, + "final_gradient_stops": [Color(theme.ACCENT)], + }, + "slide": {"direction": "left", "final_gradient_stops": [Color(theme.ACCENT)]}, + "sweep": {"direction": "left"}, + "print": { + "print_speed": 10, + "print_head_return_speed": 10, + "final_gradient_stops": [Color(theme.ACCENT)], + }, + "pour": {"pour_speed": 9}, + "matrix": {"rain_symbols": "01", "rain_fall_speed_range": (4, 7)}, + "decrypt": {"typing_speed": 5, "decryption_speed": 3}, + "burn": {"fire_chars": "█", "flame_color": "ffffff"}, + "expand": {"expand_direction": "center"}, + "scattered": {"movement_speed": 0.5}, + "rain": {"rain_symbols": "░▒▓█", "rain_fall_speed_range": (5, 10)}, + } + + return configs.get(effect_name, {}) + + +def run_banner_animation(): + """Run the ASCII banner animation before launching Textual""" + + # Check if we should animate + animation_style = os.environ.get("DIMOS_BANNER_ANIMATION", "print").lower() + + if animation_style == "none": + return # Skip animation + + from terminaltexteffects.effects.effect_burn import Burn + from terminaltexteffects.effects.effect_decrypt import Decrypt + from terminaltexteffects.effects.effect_expand import Expand + from terminaltexteffects.effects.effect_matrix import Matrix + from terminaltexteffects.effects.effect_overflow import Overflow + from terminaltexteffects.effects.effect_pour import Pour + from terminaltexteffects.effects.effect_print import Print + from terminaltexteffects.effects.effect_rain import Rain + from terminaltexteffects.effects.effect_random_sequence import RandomSequence + from terminaltexteffects.effects.effect_scattered import Scattered + from terminaltexteffects.effects.effect_slide import Slide + from terminaltexteffects.effects.effect_sweep import Sweep + + # The DIMENSIONAL ASCII art + ascii_art = """ + + ██████╗ ██╗███╗ ███╗███████╗███╗ ██╗███████╗██╗ ██████╗ ███╗ ██╗ █████╗ ██╗ + ██╔══██╗██║████╗ ████║██╔════╝████╗ ██║██╔════╝██║██╔═══██╗████╗ ██║██╔══██╗██║ + ██║ ██║██║██╔████╔██║█████╗ ██╔██╗ ██║███████╗██║██║ ██║██╔██╗ ██║███████║██║ + ██║ ██║██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║╚════██║██║██║ ██║██║╚██╗██║██╔══██║██║ + ██████╔╝██║██║ ╚═╝ ██║███████╗██║ ╚████║███████║██║╚██████╔╝██║ ╚████║██║ ██║███████╗ + ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ +""" + + # Choose effect based on style + effect_map = { + "slide": Slide, + "sweep": Sweep, + "print": Print, + "pour": Pour, + "burn": Burn, + "matrix": Matrix, + "rain": Rain, + "scattered": Scattered, + "expand": Expand, + "decrypt": Decrypt, + "overflow": Overflow, + "randomsequence": RandomSequence, + } + + EffectClass = effect_map.get(animation_style, Slide) + + # Clear screen before starting animation + print("\033[2J\033[H", end="", flush=True) + + # Get effect configuration + effect_config = get_effect_config(animation_style) + + # Create and run the effect with config + effect = EffectClass(ascii_art) + for key, value in effect_config.items(): + setattr(effect.effect_config, key, value) + + # Run the animation - terminal.print() handles all screen management + with effect.terminal_output() as terminal: + for frame in effect: + terminal.print(frame) + + # Brief pause to see the final frame + time.sleep(0.5) + + # Clear screen for Textual to take over + print("\033[2J\033[H", end="") + + +def main(): + """Main entry point - run animation then launch the real CLI""" + + # Start importing CLI in background (this is slow) + import_thread = threading.Thread(target=import_cli_in_background, daemon=True) + import_thread.start() + + # Run the animation while imports happen (if not in web mode) + if not (len(sys.argv) > 1 and sys.argv[1] == "web"): + run_banner_animation() + + # Wait for import to complete + _import_complete.wait(timeout=10) # Max 10 seconds wait + + # Launch the real CLI + if _humancli_main: + _humancli_main() + else: + # Fallback if threaded import failed + from dimos.utils.cli.human.humancli import main as humancli_main + + humancli_main() + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 2d3804c1fc..b028f8397b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,7 +116,7 @@ lcmspy = "dimos.utils.cli.lcmspy.run_lcmspy:main" foxglove-bridge = "dimos.utils.cli.foxglove_bridge.run_foxglove_bridge:main" skillspy = "dimos.utils.cli.skillspy.skillspy:main" agentspy = "dimos.utils.cli.agentspy.agentspy:main" -human-cli = "dimos.utils.cli.human.humancli:main" +human-cli = "dimos.utils.cli.human.humanclianim:main" dimos-robot = "dimos.robot.cli.dimos_robot:main" [project.optional-dependencies] From 23941f128af5883a0fa66288752556301f7459e3 Mon Sep 17 00:00:00 2001 From: lesh Date: Mon, 27 Oct 2025 13:01:44 +0200 Subject: [PATCH 2/5] random anim --- dimos/utils/cli/human/humanclianim.py | 32 ++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/dimos/utils/cli/human/humanclianim.py b/dimos/utils/cli/human/humanclianim.py index f098c183df..fd21fe5d39 100644 --- a/dimos/utils/cli/human/humanclianim.py +++ b/dimos/utils/cli/human/humanclianim.py @@ -14,6 +14,7 @@ # limitations under the License. import os +import random import sys import threading import time @@ -45,12 +46,15 @@ def import_cli_in_background(): def get_effect_config(effect_name): """Get hardcoded configuration for a specific effect""" # Hardcoded configs for each effect + global_config = { + "final_gradient_stops": [Color(theme.ACCENT)], + } + configs = { "randomsequence": { "speed": 0.075, - "final_gradient_stops": [Color(theme.ACCENT)], }, - "slide": {"direction": "left", "final_gradient_stops": [Color(theme.ACCENT)]}, + "slide": {"direction": "left", "movement_speed": 1.5}, "sweep": {"direction": "left"}, "print": { "print_speed": 10, @@ -63,25 +67,35 @@ def get_effect_config(effect_name): "burn": {"fire_chars": "█", "flame_color": "ffffff"}, "expand": {"expand_direction": "center"}, "scattered": {"movement_speed": 0.5}, - "rain": {"rain_symbols": "░▒▓█", "rain_fall_speed_range": (5, 10)}, + "beams": {"movement_speed": 0.5, "beam_delay": 0}, + "middleout": {"center_movement_speed": 3, "full_movement_speed": 0.5}, + "rain": { + "rain_symbols": "░▒▓█", + "rain_fall_speed_range": (5, 10), + }, + "highlight": {"highlight_brightness": 3}, } - return configs.get(effect_name, {}) + return {**configs.get(effect_name, {}), **global_config} def run_banner_animation(): """Run the ASCII banner animation before launching Textual""" # Check if we should animate - animation_style = os.environ.get("DIMOS_BANNER_ANIMATION", "print").lower() + random_anim = ["scattered", "print", "expand", "slide", "rain"] + animation_style = os.environ.get("DIMOS_BANNER_ANIMATION", random.choice(random_anim)).lower() if animation_style == "none": return # Skip animation - + from terminaltexteffects.effects.effect_beams import Beams from terminaltexteffects.effects.effect_burn import Burn + from terminaltexteffects.effects.effect_colorshift import ColorShift from terminaltexteffects.effects.effect_decrypt import Decrypt from terminaltexteffects.effects.effect_expand import Expand + from terminaltexteffects.effects.effect_highlight import Highlight from terminaltexteffects.effects.effect_matrix import Matrix + from terminaltexteffects.effects.effect_middleout import MiddleOut from terminaltexteffects.effects.effect_overflow import Overflow from terminaltexteffects.effects.effect_pour import Pour from terminaltexteffects.effects.effect_print import Print @@ -99,8 +113,7 @@ def run_banner_animation(): ██║ ██║██║██╔████╔██║█████╗ ██╔██╗ ██║███████╗██║██║ ██║██╔██╗ ██║███████║██║ ██║ ██║██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║╚════██║██║██║ ██║██║╚██╗██║██╔══██║██║ ██████╔╝██║██║ ╚═╝ ██║███████╗██║ ╚████║███████║██║╚██████╔╝██║ ╚████║██║ ██║███████╗ - ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ -""" + ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝""" # Choose effect based on style effect_map = { @@ -116,6 +129,9 @@ def run_banner_animation(): "decrypt": Decrypt, "overflow": Overflow, "randomsequence": RandomSequence, + "beams": Beams, + "middleout": MiddleOut, + "highlight": Highlight, } EffectClass = effect_map.get(animation_style, Slide) From 31c4916ebda2e7e454ed3539fee88458f461c842 Mon Sep 17 00:00:00 2001 From: lesh Date: Mon, 27 Oct 2025 23:24:15 +0200 Subject: [PATCH 3/5] dep --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index b028f8397b..91878865e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -186,6 +186,7 @@ dev = [ "pytest-timeout==2.4.0", "textual==3.7.1", "requests-mock==1.12.1", + "terminaltexteffects==0.12.2", ] sim = [ From d699571e85845e8c277c7651dfcf41a9b0a281ec Mon Sep 17 00:00:00 2001 From: lesh Date: Tue, 28 Oct 2025 08:29:20 +0200 Subject: [PATCH 4/5] better logo --- dimos/utils/cli/human/humancli.py | 12 +----------- dimos/utils/cli/human/humanclianim.py | 10 +--------- dimos/utils/cli/theme.py | 9 +++++++++ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/dimos/utils/cli/human/humancli.py b/dimos/utils/cli/human/humancli.py index fb0ebc5fe2..57192334b5 100644 --- a/dimos/utils/cli/human/humancli.py +++ b/dimos/utils/cli/human/humancli.py @@ -32,7 +32,6 @@ from dimos.utils.cli import theme from dimos.utils.generic import truncate_display_string - # Custom theme for JSON highlighting JSON_THEME = Theme( { @@ -111,16 +110,7 @@ def on_mount(self) -> None: # Focus on input self.input_widget.focus() - # Display ASCII art banner - ascii_art = """ - ██████╗ ██╗███╗ ███╗███████╗███╗ ██╗███████╗██╗ ██████╗ ███╗ ██╗ █████╗ ██╗ - ██╔══██╗██║████╗ ████║██╔════╝████╗ ██║██╔════╝██║██╔═══██╗████╗ ██║██╔══██╗██║ - ██║ ██║██║██╔████╔██║█████╗ ██╔██╗ ██║███████╗██║██║ ██║██╔██╗ ██║███████║██║ - ██║ ██║██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║╚════██║██║██║ ██║██║╚██╗██║██╔══██║██║ - ██████╔╝██║██║ ╚═╝ ██║███████╗██║ ╚████║███████║██║╚██████╔╝██║ ╚████║██║ ██║███████╗ - ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ -""" - self.chat_log.write(f"[{theme.ACCENT}]{ascii_art}[/{theme.ACCENT}]") + self.chat_log.write(f"[{theme.ACCENT}]{theme.ascii_logo}[/{theme.ACCENT}]") # Welcome message self._add_system_message("Connected to DimOS Agent Interface") diff --git a/dimos/utils/cli/human/humanclianim.py b/dimos/utils/cli/human/humanclianim.py index fd21fe5d39..a0349eedf8 100644 --- a/dimos/utils/cli/human/humanclianim.py +++ b/dimos/utils/cli/human/humanclianim.py @@ -106,15 +106,7 @@ def run_banner_animation(): from terminaltexteffects.effects.effect_sweep import Sweep # The DIMENSIONAL ASCII art - ascii_art = """ - - ██████╗ ██╗███╗ ███╗███████╗███╗ ██╗███████╗██╗ ██████╗ ███╗ ██╗ █████╗ ██╗ - ██╔══██╗██║████╗ ████║██╔════╝████╗ ██║██╔════╝██║██╔═══██╗████╗ ██║██╔══██╗██║ - ██║ ██║██║██╔████╔██║█████╗ ██╔██╗ ██║███████╗██║██║ ██║██╔██╗ ██║███████║██║ - ██║ ██║██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║╚════██║██║██║ ██║██║╚██╗██║██╔══██║██║ - ██████╔╝██║██║ ╚═╝ ██║███████╗██║ ╚████║███████║██║╚██████╔╝██║ ╚████║██║ ██║███████╗ - ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝""" - + ascii_art = "\n" + theme.ascii_logo.replace("\n", "\n ") # Choose effect based on style effect_map = { "slide": Slide, diff --git a/dimos/utils/cli/theme.py b/dimos/utils/cli/theme.py index aa061bc43a..ddadc3ba6d 100644 --- a/dimos/utils/cli/theme.py +++ b/dimos/utils/cli/theme.py @@ -97,3 +97,12 @@ def get(name: str, default: str = "#ffffff") -> str: ERROR = COLORS.get("error", "#ff0000") WARNING = COLORS.get("warning", "#ffcc00") INFO = COLORS.get("info", "#00eeee") + +ascii_logo = """ + ▇▇▇▇▇▇╗ ▇▇╗▇▇▇╗ ▇▇▇╗▇▇▇▇▇▇▇╗▇▇▇╗ ▇▇╗▇▇▇▇▇▇▇╗▇▇╗ ▇▇▇▇▇▇╗ ▇▇▇╗ ▇▇╗ ▇▇▇▇▇╗ ▇▇╗ + ▇▇╔══▇▇╗▇▇║▇▇▇▇╗ ▇▇▇▇║▇▇╔════╝▇▇▇▇╗ ▇▇║▇▇╔════╝▇▇║▇▇╔═══▇▇╗▇▇▇▇╗ ▇▇║▇▇╔══▇▇╗▇▇║ + ▇▇║ ▇▇║▇▇║▇▇╔▇▇▇▇╔▇▇║▇▇▇▇▇╗ ▇▇╔▇▇╗ ▇▇║▇▇▇▇▇▇▇╗▇▇║▇▇║ ▇▇║▇▇╔▇▇╗ ▇▇║▇▇▇▇▇▇▇║▇▇║ + ▇▇║ ▇▇║▇▇║▇▇║╚▇▇╔╝▇▇║▇▇╔══╝ ▇▇║╚▇▇╗▇▇║╚════▇▇║▇▇║▇▇║ ▇▇║▇▇║╚▇▇╗▇▇║▇▇╔══▇▇║▇▇║ + ▇▇▇▇▇▇╔╝▇▇║▇▇║ ╚═╝ ▇▇║▇▇▇▇▇▇▇╗▇▇║ ╚▇▇▇▇║▇▇▇▇▇▇▇║▇▇║╚▇▇▇▇▇▇╔╝▇▇║ ╚▇▇▇▇║▇▇║ ▇▇║▇▇▇▇▇▇▇╗ + ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ +""" From db81f65894f1a533667d8246f6f22bf114f31857 Mon Sep 17 00:00:00 2001 From: lesh Date: Tue, 28 Oct 2025 09:00:35 +0200 Subject: [PATCH 5/5] renamed human-cli to humancli (faster to type) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 91878865e7..b50f8e5e0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,7 +116,7 @@ lcmspy = "dimos.utils.cli.lcmspy.run_lcmspy:main" foxglove-bridge = "dimos.utils.cli.foxglove_bridge.run_foxglove_bridge:main" skillspy = "dimos.utils.cli.skillspy.skillspy:main" agentspy = "dimos.utils.cli.agentspy.agentspy:main" -human-cli = "dimos.utils.cli.human.humanclianim:main" +humancli = "dimos.utils.cli.human.humanclianim:main" dimos-robot = "dimos.robot.cli.dimos_robot:main" [project.optional-dependencies]