affirmation.py
python · 134 lines
1#!/usr/bin/env python32"""A quiet CLI that reveals a poetic affirmation word by word."""34from __future__ import annotations56import argparse7import random8import signal9import sys10import time11from typing import Sequence1213# Curated affirmations: short, poetic, calming.14AFFIRMATIONS: tuple[str, ...] = (15 "You are exactly where you need to be right now.",16 "Breathe in. You are held by this moment.",17 "Softness is not weakness. It is your deepest strength.",18 "The stillness inside you is always there, waiting.",19 "You do not need to earn rest.",20 "Let the weight fall. You have carried enough today.",21 "Even roots grow in the dark.",22 "You are allowed to begin again, gently.",23 "This breath is yours. Nothing else is required.",24 "You belong here, just as you are.",25 "The sky does not rush its colors at dusk.",26 "There is grace in simply continuing.",27 "Your pace is not a flaw. It is a rhythm.",28 "Quiet things still grow.",29 "You are more than what you produce.",30 "The world is better with you in it, even now.",31 "You are not behind. You are unfolding in your own time.",32 "Nothing is missing. This moment is already whole.",33 "You can loosen your shoulders now.",34 "You are allowed to take up space in this world.",35 "Not every day must be loud to be meaningful.",36 "You are doing better than you think.",37 "The heart knows how to keep going, even quietly.",38 "Rest is part of the path, not a detour.",39 "You do not have to prove your worth to exist.",40 "There is no rush. The sun rises without effort.",41 "You are safe to soften here.",42 "Small steps still count as moving forward.",43 "You have survived every day that tried to stop you.",44 "Gentle is still powerful.",45 "You are allowed to be both tired and hopeful.",46 "Your presence alone is enough today.",47 "Even slow mornings lead somewhere beautiful.",48 "You are not broken. You are becoming.",49 "The quiet inside you is a place you can return to.",50 "You are worthy of the same kindness you give others.",51 "It is okay to simply be, without fixing anything.",52 "Some days, breathing is the bravest thing you can do.",53 "You are not late to your own life.",54 "Let today be simple. Let that be enough.",55 "You are held by more than you can see.",56 "Peace does not need to be earned.",57 "You are allowed to outgrow old versions of yourself.",58 "The future does not need you to hurry.",59 "You are learning, even when it feels still.",60 "There is beauty in your ordinary days.",61 "You are not alone in this breath.",62 "You are permitted to start over as many times as you need.",63 "Your heart has carried you this far. Trust it.",64 "You are more than your thoughts about yourself.",65 "The light finds you, even through small cracks.",66 "Everything you need is already quietly inside you.",67)6869DEFAULT_DELAY_MS: int = 160707172def select_affirmation(messages: Sequence[str]) -> str:73 """Return a single randomly chosen affirmation."""74 return random.choice(messages)757677def reveal_words(text: str, delay_seconds: float) -> None:78 """Print text word by word with a pause between each word.7980 Uses stdout directly with flush to ensure each word appears81 immediately, regardless of terminal buffering mode.82 """83 words = text.split()84 for i, word in enumerate(words):85 if i > 0:86 sys.stdout.write(" ")87 sys.stdout.write(word)88 sys.stdout.flush()89 time.sleep(delay_seconds)90 sys.stdout.write("\n")91 sys.stdout.flush()929394def build_parser() -> argparse.ArgumentParser:95 """Construct the argument parser for CLI options."""96 parser = argparse.ArgumentParser(97 description="A quiet affirmation, revealed slowly.",98 )99 parser.add_argument(100 "--delay",101 type=int,102 default=DEFAULT_DELAY_MS,103 metavar="MS",104 help=f"milliseconds between words (default: {DEFAULT_DELAY_MS})",105 )106 return parser107108109def _suppress_interrupt() -> None:110 """Configure signal handling so Ctrl+C exits silently."""111 signal.signal(signal.SIGINT, lambda *_: sys.exit(0))112113114def main(argv: Sequence[str] | None = None) -> None:115 """Entry point: parse args, select a message, reveal it."""116 _suppress_interrupt()117 parser = build_parser()118 args = parser.parse_args(argv)119 delay_seconds: float = args.delay / 1000.0120 message = select_affirmation(AFFIRMATIONS)121122 # A small pause before speaking — intentional silence.123 sys.stdout.write("\n")124 sys.stdout.flush()125 time.sleep(delay_seconds)126127 reveal_words(message, delay_seconds)128 sys.stdout.write("\n")129 sys.stdout.flush()130131132if __name__ == "__main__":133 main()134