mirror of
https://github.com/khodges42/nightShift.git
synced 2026-06-14 10:08:37 +00:00
added hotdog animations
I am a professional software engineer
This commit is contained in:
parent
d08e629bce
commit
93a50ddb42
|
|
@ -14,7 +14,7 @@ from .integ_setup import format_setup_result, setup_python_project
|
|||
from .pipeline import PipelineRunner
|
||||
from .runlog import RunLogger
|
||||
from .status import build_status, format_status
|
||||
from .terminal import format_banner, style_text
|
||||
from .terminal import HOTDOG_ANIMATIONS, TerminalAnimation, format_banner, style_text
|
||||
from .tasks import (
|
||||
ensure_dependencies_satisfied,
|
||||
parse_task_file,
|
||||
|
|
@ -49,6 +49,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
run_parser.add_argument("--config", default="nightshift.yaml", help="Config file to use.")
|
||||
run_parser.add_argument("--task", help="Specific task id to run.")
|
||||
run_parser.add_argument("--all", action="store_true", help="Run all runnable incomplete tasks.")
|
||||
run_parser.add_argument(
|
||||
"--animation",
|
||||
default="agent_thinking",
|
||||
choices=tuple(sorted(HOTDOG_ANIMATIONS)),
|
||||
help="Terminal animation to show while the run is active.",
|
||||
)
|
||||
run_parser.add_argument("--no-animation", action="store_true", help="Disable terminal animation.")
|
||||
|
||||
status_parser = subparsers.add_parser("status", help="Inspect NightShift project status.")
|
||||
status_parser.add_argument("--config", default="nightshift.yaml", help="Config file to inspect.")
|
||||
|
|
@ -140,7 +147,12 @@ def main(argv: list[str] | None = None) -> int:
|
|||
runner = PipelineRunner(config, logger=RunLogger(console=print))
|
||||
if args.all:
|
||||
selected = [task for task in tasks if not task.completed]
|
||||
result = runner.run_tasks(selected)
|
||||
with TerminalAnimation(
|
||||
args.animation,
|
||||
message="NightShift running all tasks",
|
||||
enabled=not args.no_animation,
|
||||
):
|
||||
result = runner.run_tasks(selected)
|
||||
print(f"Status: {result.status}")
|
||||
print(f"Tasks run: {len(result.task_results)}")
|
||||
print(f"Completed: {result.completed_count}")
|
||||
|
|
@ -150,7 +162,12 @@ def main(argv: list[str] | None = None) -> int:
|
|||
|
||||
task = select_task_by_id(tasks, args.task) if args.task else select_next_runnable_task(tasks)
|
||||
ensure_dependencies_satisfied(tasks, task)
|
||||
result = runner.run_task(task)
|
||||
with TerminalAnimation(
|
||||
args.animation,
|
||||
message=f"NightShift running {task.id}",
|
||||
enabled=not args.no_animation,
|
||||
):
|
||||
result = runner.run_task(task)
|
||||
print(f"Task: {result.task_id}")
|
||||
print(style_text(f"Status: {result.status}", color=_status_color(result.status), bold=True))
|
||||
print(f"Retries: {result.retry_count}")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import os
|
|||
import sys
|
||||
from typing import TextIO
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
|
||||
from .version import display_version
|
||||
|
||||
|
|
@ -41,6 +43,167 @@ BANNER_MESSAGES = [
|
|||
]
|
||||
quote = random.choice(BANNER_MESSAGES)
|
||||
|
||||
HOTDOG_ANIMATIONS = {
|
||||
"classic_dance": [
|
||||
"🌭",
|
||||
"ヽ(🌭)ノ",
|
||||
"(🌭)",
|
||||
"(🌭)",
|
||||
"(🌭)",
|
||||
],
|
||||
"shuffle_mode": [
|
||||
" (🌭) ",
|
||||
" (🌭) ",
|
||||
"<(🌭<)",
|
||||
"(>🌭)>",
|
||||
"~(🌭)~",
|
||||
],
|
||||
"gremlin_energy": [
|
||||
"(ノ🌭)ノ",
|
||||
"ᕕ(🌭)ᕗ",
|
||||
"^(🌭)^",
|
||||
"(🌭)b",
|
||||
"(🌭)",
|
||||
],
|
||||
"roller_grill": [
|
||||
"🌭",
|
||||
"🌭",
|
||||
"🌭",
|
||||
"🌭",
|
||||
"🌭",
|
||||
],
|
||||
"ascending_glizzy": [
|
||||
"🌭",
|
||||
" 🌭",
|
||||
" 🌭",
|
||||
" ",
|
||||
"🌭",
|
||||
],
|
||||
"agent_thinking": [
|
||||
"🌭 .",
|
||||
"🌭 ..",
|
||||
"🌭 ...",
|
||||
"🌭 ....",
|
||||
"🌭 ???",
|
||||
],
|
||||
"tubular_offering": [
|
||||
" つ 🌭_🌭 つ",
|
||||
" つ🌭 _🌭 つ",
|
||||
" つ 🌭🌭 つ",
|
||||
" つ🌭🌭_ つ",
|
||||
" つ 🌭_🌭 つ",
|
||||
],
|
||||
"tubular_offering_wobble": [
|
||||
" つ 🌭_🌭 つ",
|
||||
" つ 🌭~🌭 つ",
|
||||
" つ ~🌭~ つ",
|
||||
" つ 🌭~🌭 つ",
|
||||
" つ 🌭_🌭 つ",
|
||||
],
|
||||
"chaotic_summoning": [
|
||||
" つ 🌭_🌭 つ",
|
||||
" つ 🌭 つ",
|
||||
" つ 🌭🔥 つ",
|
||||
" つ 🌭 つ",
|
||||
" つ 🌭_🌭 つ",
|
||||
],
|
||||
"hotdog_ritual_dance": [
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
"( ಠ_ಠ) 🌭 (ಠ_ಠ )",
|
||||
"( ಠ_ಠ) 🌭 (ಠ_ಠ )",
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
],
|
||||
"ritual_side_to_side": [
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
"( ಠ_ಠ) 🌭(ಠ_ಠ )",
|
||||
"( ಠ_ಠ) 🌭(ಠ_ಠ )",
|
||||
"( ಠ_ಠ) (ಠ_ಠ )",
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
],
|
||||
"full_rave_mode": [
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
"(ಠ_ಠ )🌭( ಠ_ಠ)",
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
"(ಠ_ಠ )🔥🌭🔥( ಠ_ಠ)",
|
||||
"( ಠ_ಠ)🌭(ಠ_ಠ )",
|
||||
],
|
||||
"terminal_cult_initiation": [
|
||||
"( ಠ_ಠ) (ಠ_ಠ )",
|
||||
"( ಠ_ಠ)🌭 (ಠ_ಠ )",
|
||||
"( ಠ_ಠ) 🌭 (ಠ_ಠ )",
|
||||
"( ಠ_ಠ) 🌭 (ಠ_ಠ )",
|
||||
"( ಠ_ಠ) 🌭 (ಠ_ಠ )",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class TerminalAnimation:
|
||||
"""Transient terminal status animation."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "agent_thinking",
|
||||
*,
|
||||
message: str = "NightShift running",
|
||||
stream: TextIO | None = None,
|
||||
interval_seconds: float = 0.18,
|
||||
enabled: bool = True,
|
||||
) -> None:
|
||||
self.frames = animation_frames(name)
|
||||
self.message = message
|
||||
self.stream = stream or sys.stderr
|
||||
self.interval_seconds = interval_seconds
|
||||
self.enabled = enabled and should_style(self.stream)
|
||||
self._stop = threading.Event()
|
||||
self._thread: threading.Thread | None = None
|
||||
self._width = 0
|
||||
|
||||
def __enter__(self) -> "TerminalAnimation":
|
||||
self.start()
|
||||
return self
|
||||
|
||||
def __exit__(self, *_exc: object) -> None:
|
||||
self.stop()
|
||||
|
||||
def start(self) -> None:
|
||||
if not self.enabled or self._thread is not None:
|
||||
return
|
||||
self._thread = threading.Thread(target=self._run, daemon=True)
|
||||
self._thread.start()
|
||||
|
||||
def stop(self) -> None:
|
||||
if self._thread is None:
|
||||
return
|
||||
self._stop.set()
|
||||
self._thread.join(timeout=1)
|
||||
self._clear()
|
||||
self._thread = None
|
||||
|
||||
def _run(self) -> None:
|
||||
index = 0
|
||||
while not self._stop.is_set():
|
||||
frame = self.frames[index % len(self.frames)]
|
||||
text = f"{frame} {self.message}"
|
||||
self._width = max(self._width, len(text))
|
||||
self.stream.write("\r" + text.ljust(self._width))
|
||||
self.stream.flush()
|
||||
index += 1
|
||||
self._stop.wait(self.interval_seconds)
|
||||
|
||||
def _clear(self) -> None:
|
||||
if not self.enabled:
|
||||
return
|
||||
self.stream.write("\r" + (" " * self._width) + "\r")
|
||||
self.stream.flush()
|
||||
|
||||
|
||||
def animation_frames(name: str) -> tuple[str, ...]:
|
||||
frames = HOTDOG_ANIMATIONS.get(name)
|
||||
if not frames:
|
||||
frames = HOTDOG_ANIMATIONS["agent_thinking"]
|
||||
return tuple(frames)
|
||||
|
||||
def should_style(stream: TextIO | None = None) -> bool:
|
||||
stream = stream or sys.stdout
|
||||
if os.environ.get("NO_COLOR"):
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
|
||||
PACKAGE_VERSION = "0.2.2"
|
||||
PACKAGE_VERSION = "0.2.3"
|
||||
RELEASE_CHANNEL = "alpha"
|
||||
hotdog_version = "footlong"
|
||||
topping_version = "mustard"
|
||||
hotdog_version = "new-york"
|
||||
topping_version = "sport-peppers"
|
||||
|
||||
HOTDOG_VERSIONS = (
|
||||
"bratwurst",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,13 @@ from unittest.mock import patch
|
|||
|
||||
from nightshift.artifacts import ArtifactStore
|
||||
from nightshift.runlog import RunLogger
|
||||
from nightshift.terminal import format_banner, format_console_event_line
|
||||
from nightshift.terminal import (
|
||||
HOTDOG_ANIMATIONS,
|
||||
TerminalAnimation,
|
||||
animation_frames,
|
||||
format_banner,
|
||||
format_console_event_line,
|
||||
)
|
||||
|
||||
|
||||
class FakeTTY(StringIO):
|
||||
|
|
@ -25,6 +31,19 @@ class TerminalStylingTests(unittest.TestCase):
|
|||
self.assertIn("NightShift", banner)
|
||||
self.assertIn("\x1b[", banner)
|
||||
|
||||
def test_animation_frames_fall_back_to_agent_thinking(self) -> None:
|
||||
self.assertEqual(animation_frames("missing"), tuple(HOTDOG_ANIMATIONS["agent_thinking"]))
|
||||
self.assertEqual(animation_frames("classic_dance"), tuple(HOTDOG_ANIMATIONS["classic_dance"]))
|
||||
|
||||
def test_terminal_animation_is_disabled_for_non_tty(self) -> None:
|
||||
stream = StringIO()
|
||||
animation = TerminalAnimation(stream=stream)
|
||||
|
||||
with animation:
|
||||
pass
|
||||
|
||||
self.assertEqual(stream.getvalue(), "")
|
||||
|
||||
def test_console_event_line_colors_success_and_failure(self) -> None:
|
||||
success = format_console_event_line(
|
||||
"2026-05-17T00:00:00Z",
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ from nightshift.version import (
|
|||
|
||||
class VersionTests(unittest.TestCase):
|
||||
def test_display_version_includes_channel_hotdog_and_topping(self) -> None:
|
||||
self.assertEqual(display_version(), "0.2.2-alpha-footlong-mustard")
|
||||
self.assertEqual(PACKAGE_VERSION, "0.2.2")
|
||||
self.assertEqual(display_version(), "0.2.3-alpha-new-york-sport-peppers")
|
||||
self.assertEqual(PACKAGE_VERSION, "0.2.3")
|
||||
self.assertIn(hotdog_version, HOTDOG_VERSIONS)
|
||||
self.assertIn(topping_version, TOPPING_VERSIONS)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user