diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5dff203..3015b793 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,15 @@
         * These allow you to provide feedback to the user in an asychronous fashion, meaning alerts can
         display when the user is still entering text at the prompt. See [async_printing.py](https://github.com/python-cmd2/cmd2/blob/master/examples/async_printing.py)
         for an example.
+    * Cross-platform colored output support 
+        * ``colorama`` gets initialized properly in ``Cmd.__init()``
+        * The ``Cmd.colors`` setting is no longer platform dependent and now has three values:
+            * Terminal (default) - output methods do not strip any ANSI escape sequences when output is a terminal, but 
+            if the output is a pipe or a file the escape sequences are stripped
+            * Always - output methods **never** strip ANSI escape sequences, regardless of the output destination
+            * Never - output methods strip all ANSI escape sequences
+* Deprecations
+    * Deprecated the builtin ``cmd2`` suport for colors including ``Cmd.colorize()`` and ``Cmd._colorcodes``
 * Deletions
     * The ``preparse``, ``postparsing_precmd``, and ``postparsing_postcmd`` methods *deprecated* in the previous release 
     have been deleted
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index ed478b0d..7d8ac7dc 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -32,16 +32,16 @@ Git repository on GitHub at https://github.com/python-cmd2/cmd2
 import argparse
 import cmd
 import collections
+import colorama
 from colorama import Fore
 import glob
 import inspect
 import os
-import platform
 import re
 import shlex
 import sys
 import threading
-from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, Union
+from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, Union, IO
 
 from . import constants
 from . import utils
@@ -318,7 +318,7 @@ class Cmd(cmd.Cmd):
     reserved_words = []
 
     # Attributes which ARE dynamically settable at runtime
-    colors = (platform.system() != 'Windows')
+    colors = constants.COLORS_TERMINAL
     continuation_prompt = '> '
     debug = False
     echo = False
@@ -338,7 +338,7 @@ class Cmd(cmd.Cmd):
 
     # To make an attribute settable with the "do_set" command, add it to this ...
     # This starts out as a dictionary but gets converted to an OrderedDict sorted alphabetically by key
-    settable = {'colors': 'Colorized output (*nix only)',
+    settable = {'colors': 'Allow colorized output (valid values: Terminal, Always, Never)',
                 'continuation_prompt': 'On 2nd+ line of input',
                 'debug': 'Show full error stack on error',
                 'echo': 'Echo command issued into output',
@@ -370,6 +370,9 @@ class Cmd(cmd.Cmd):
             except AttributeError:
                 pass
 
+        # Override whether ansi codes should be stripped from the output since cmd2 has its own logic for doing this
+        colorama.init(strip=False)
+
         # initialize plugin system
         # needs to be done before we call __init__(0)
         self._initialize_plugin_system()
@@ -418,13 +421,13 @@ class Cmd(cmd.Cmd):
         self._STOP_AND_EXIT = True  # cmd convention
 
         self._colorcodes = {'bold': {True: '\x1b[1m', False: '\x1b[22m'},
-                            'cyan': {True: '\x1b[36m', False: '\x1b[39m'},
-                            'blue': {True: '\x1b[34m', False: '\x1b[39m'},
-                            'red': {True: '\x1b[31m', False: '\x1b[39m'},
-                            'magenta': {True: '\x1b[35m', False: '\x1b[39m'},
-                            'green': {True: '\x1b[32m', False: '\x1b[39m'},
-                            'underline': {True: '\x1b[4m', False: '\x1b[24m'},
-                            'yellow': {True: '\x1b[33m', False: '\x1b[39m'}}
+                            'cyan': {True: Fore.CYAN, False: Fore.RESET},
+                            'blue': {True: Fore.BLUE, False: Fore.RESET},
+                            'red': {True: Fore.RED, False: Fore.RESET},
+                            'magenta': {True: Fore.MAGENTA, False: Fore.RESET},
+                            'green': {True: Fore.GREEN, False: Fore.RESET},
+                            'underline': {True: '\x1b[4m', False: Fore.RESET},
+                            'yellow': {True: Fore.YELLOW, False: Fore.RESET}}
 
         # Used load command to store the current script dir as a LIFO queue to support _relative_load command
         self._script_dir = []
@@ -554,34 +557,53 @@ class Cmd(cmd.Cmd):
         # Make sure settable parameters are sorted alphabetically by key
         self.settable = collections.OrderedDict(sorted(self.settable.items(), key=lambda t: t[0]))
 
-    def poutput(self, msg: str, end: str='\n') -> None:
-        """Convenient shortcut for self.stdout.write(); by default adds newline to end if not already present.
+    def decolorized_write(self, fileobj: IO, msg: str) -> None:
+        """Write a string to a fileobject, stripping ANSI escape sequences if necessary
+
+        Honor the current colors setting, which requires us to check whether the
+        fileobject is a tty.
+        """
+        if self.colors.lower() == constants.COLORS_NEVER.lower() or \
+                (self.colors.lower() == constants.COLORS_TERMINAL.lower() and not fileobj.isatty()):
+            msg = utils.strip_ansi(msg)
+        fileobj.write(msg)
 
-        Also handles BrokenPipeError exceptions for when a commands's output has been piped to another process and
-        that process terminates before the cmd2 command is finished executing.
+    def poutput(self, msg: Any, end: str='\n', color: str='') -> None:
+        """Smarter self.stdout.write(); color aware and adds newline of not present.
+
+        Also handles BrokenPipeError exceptions for when a commands's output has
+        been piped to another process and that process terminates before the
+        cmd2 command is finished executing.
 
         :param msg: message to print to current stdout (anything convertible to a str with '{}'.format() is OK)
-        :param end: string appended after the end of the message if not already present, default a newline
+        :param end: (optional) string appended after the end of the message if not already present, default a newline
+        :param color: (optional) color escape to output this message with
         """
         if msg is not None and msg != '':
             try:
                 msg_str = '{}'.format(msg)
-                self.stdout.write(msg_str)
                 if not msg_str.endswith(end):
-                    self.stdout.write(end)
+                    msg_str += end
+                if color:
+                    msg_str = color + msg_str + Fore.RESET
+                self.decolorized_write(self.stdout, msg_str)
             except BrokenPipeError:
-                # This occurs if a command's output is being piped to another process and that process closes before the
-                # command is finished. If you would like your application to print a warning message, then set the
-                # broken_pipe_warning attribute to the message you want printed.
+                # This occurs if a command's output is being piped to another
+                # process and that process closes before the command is
+                # finished. If you would like your application to print a
+                # warning message, then set the broken_pipe_warning attribute
+                # to the message you want printed.
                 if self.broken_pipe_warning:
                     sys.stderr.write(self.broken_pipe_warning)
 
-    def perror(self, err: Union[str, Exception], traceback_war: bool=True) -> None:
+    def perror(self, err: Union[str, Exception], traceback_war: bool=True, err_color: str=Fore.LIGHTRED_EX,
+               war_color: str=Fore.LIGHTYELLOW_EX) -> None:
         """ Print error message to sys.stderr and if debug is true, print an exception Traceback if one exists.
 
         :param err: an Exception or error message to print out
         :param traceback_war: (optional) if True, print a message to let user know they can enable debug
-        :return:
+        :param err_color: (optional) color escape to output error with
+        :param war_color: (optional) color escape to output warning with
         """
         if self.debug:
             import traceback
@@ -589,14 +611,15 @@ class Cmd(cmd.Cmd):
 
         if isinstance(err, Exception):
             err_msg = "EXCEPTION of type '{}' occurred with message: '{}'\n".format(type(err).__name__, err)
-            sys.stderr.write(self.colorize(err_msg, 'red'))
         else:
-            err_msg = self.colorize("ERROR: {}\n".format(err), 'red')
-            sys.stderr.write(err_msg)
+            err_msg = "ERROR: {}\n".format(err)
+        err_msg = err_color + err_msg + Fore.RESET
+        self.decolorized_write(sys.stderr, err_msg)
 
         if traceback_war:
             war = "To enable full traceback, run the following command:  'set debug true'\n"
-            sys.stderr.write(self.colorize(war, 'yellow'))
+            war = war_color + war + Fore.RESET
+            self.decolorized_write(sys.stderr, war)
 
     def pfeedback(self, msg: str) -> None:
         """For printing nonessential feedback.  Can be silenced with `quiet`.
@@ -605,7 +628,7 @@ class Cmd(cmd.Cmd):
             if self.feedback_to_output:
                 self.poutput(msg)
             else:
-                sys.stderr.write("{}\n".format(msg))
+                self.decolorized_write(sys.stderr, "{}\n".format(msg))
 
     def ppaged(self, msg: str, end: str='\n', chop: bool=False) -> None:
         """Print output using a pager if it would go off screen and stdout isn't currently being redirected.
@@ -641,6 +664,9 @@ class Cmd(cmd.Cmd):
                 # Don't attempt to use a pager that can block if redirecting or running a script (either text or Python)
                 # Also only attempt to use a pager if actually running in a real fully functional terminal
                 if functional_terminal and not self.redirecting and not self._in_py and not self._script_dir:
+                    if self.colors.lower() == constants.COLORS_NEVER.lower():
+                        msg_str = utils.strip_ansi(msg_str)
+
                     pager = self.pager
                     if chop:
                         pager = self.pager_chop
@@ -665,7 +691,7 @@ class Cmd(cmd.Cmd):
             except BrokenPipeError:
                 # This occurs if a command's output is being piped to another process and that process closes before the
                 # command is finished. If you would like your application to print a warning message, then set the
-                # broken_pipe_warning attribute to the message you want printed.
+                # broken_pipe_warning attribute to the message you want printed.`
                 if self.broken_pipe_warning:
                     sys.stderr.write(self.broken_pipe_warning)
 
@@ -676,7 +702,7 @@ class Cmd(cmd.Cmd):
            is running on Windows, will return ``val`` unchanged.
            ``color`` should be one of the supported strings (or styles):
            red/blue/green/cyan/magenta, bold, underline"""
-        if self.colors and (self.stdout == self.initial_stdout):
+        if self.colors.lower() != constants.COLORS_NEVER.lower() and (self.stdout == self.initial_stdout):
             return self._colorcodes[color][True] + val + self._colorcodes[color][False]
         return val
 
diff --git a/cmd2/constants.py b/cmd2/constants.py
index d3e8a125..3c133b70 100644
--- a/cmd2/constants.py
+++ b/cmd2/constants.py
@@ -17,3 +17,8 @@ REDIRECTION_TOKENS = [REDIRECTION_PIPE, REDIRECTION_OUTPUT, REDIRECTION_APPEND]
 ANSI_ESCAPE_RE = re.compile(r'\x1b[^m]*m')
 
 LINE_FEED = '\n'
+
+# values for colors setting
+COLORS_NEVER = 'Never'
+COLORS_TERMINAL = 'Terminal'
+COLORS_ALWAYS = 'Always'
diff --git a/docs/settingchanges.rst b/docs/settingchanges.rst
index 02955273..e08b6026 100644
--- a/docs/settingchanges.rst
+++ b/docs/settingchanges.rst
@@ -137,7 +137,7 @@ comments, is viewable from within a running application
 with::
 
     (Cmd) set --long
-    colors: True                   # Colorized output (*nix only)
+    colors: Terminal               # Allow colorized output
     continuation_prompt: >         # On 2nd+ line of input
     debug: False                   # Show full error stack on error
     echo: False                    # Echo command issued into output
@@ -150,5 +150,5 @@ with::
 
 Any of these user-settable parameters can be set while running your app with the ``set`` command like so::
 
-    set colors False
+    set colors Never
 
diff --git a/docs/unfreefeatures.rst b/docs/unfreefeatures.rst
index b5f9415d..364addc6 100644
--- a/docs/unfreefeatures.rst
+++ b/docs/unfreefeatures.rst
@@ -139,23 +139,43 @@ instead.  These methods have these advantages:
 .. automethod:: cmd2.cmd2.Cmd.ppaged
 
 
-color
-=====
+Colored Output
+==============
 
-Text output can be colored by wrapping it in the ``colorize`` method.
+The output methods in the previous section all honor the ``colors`` setting,
+which has three possible values:
+
+Never
+    poutput() and pfeedback() strip all ANSI escape sequences
+    which instruct the terminal to colorize output
+
+Terminal
+    (the default value) poutput() and pfeedback() do not strip any ANSI escape
+    sequences when the output is a terminal, but if the output is a pipe or a
+    file the escape sequences are stripped. If you want colorized output you
+    must add ANSI escape sequences, preferably using some python color library
+    like `plumbum.colors`, `colorama`, `blessings`, or `termcolor`.
+
+Always
+    poutput() and pfeedback() never strip ANSI escape sequences, regardless of
+    the output destination
+
+
+The previously recommended ``colorize`` method is now deprecated.
 
-.. automethod:: cmd2.cmd2.Cmd.colorize
 
 .. _quiet:
 
+Suppressing non-essential output
+================================
 
-quiet
-=====
+The ``quiet`` setting controls whether ``self.pfeedback()`` actually produces
+any output. If ``quiet`` is ``False``, then the output will be produced. If
+``quiet`` is ``True``, no output will be produced.
 
-Controls whether ``self.pfeedback('message')`` output is suppressed;
-useful for non-essential feedback that the user may not always want
-to read.  ``quiet`` is only relevant if
-``app.pfeedback`` is sometimes used.
+This makes ``self.pfeedback()`` useful for non-essential output like status
+messages. Users can control whether they would like to see these messages by changing
+the value of the ``quiet`` setting.
 
 
 select
diff --git a/examples/colors.py b/examples/colors.py
new file mode 100755
index 00000000..8765aee0
--- /dev/null
+++ b/examples/colors.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+# coding=utf-8
+"""
+A sample application for cmd2. Demonstrating colorized output.
+
+Experiment with the command line options on the `speak` command to see how
+different output colors ca
+
+The colors setting has three possible values:
+
+Never
+    poutput() and pfeedback() strip all ANSI escape sequences
+    which instruct the terminal to colorize output
+
+Terminal
+    (the default value) poutput() and pfeedback() do not strip any ANSI escape
+    sequences when the output is a terminal, but if the output is a pipe or a
+    file the escape sequences are stripped. If you want colorized output you
+    must add ANSI escape sequences, preferably using some python color library
+    like `plumbum.colors`, `colorama`, `blessings`, or `termcolor`.
+
+Always
+    poutput() and pfeedback() never strip ANSI escape sequences, regardless of
+    the output destination
+"""
+
+import random
+import argparse
+
+import cmd2
+from colorama import Fore, Back
+
+FG_COLORS = {
+    'black': Fore.BLACK,
+    'red': Fore.RED,
+    'green': Fore.GREEN,
+    'yellow': Fore.YELLOW,
+    'blue': Fore.BLUE,
+    'magenta': Fore.MAGENTA,
+    'cyan': Fore.CYAN,
+    'white': Fore.WHITE,
+}
+BG_COLORS = {
+    'black': Back.BLACK,
+    'red': Back.RED,
+    'green': Back.GREEN,
+    'yellow': Back.YELLOW,
+    'blue': Back.BLUE,
+    'magenta': Back.MAGENTA,
+    'cyan': Back.CYAN,
+    'white': Back.WHITE,
+}
+
+
+class CmdLineApp(cmd2.Cmd):
+    """Example cmd2 application demonstrating colorized output."""
+
+    # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist
+    # default_to_shell = True
+    MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh']
+    MUMBLE_FIRST = ['so', 'like', 'well']
+    MUMBLE_LAST = ['right?']
+
+    def __init__(self):
+        self.multiline_commands = ['orate']
+        self.maxrepeats = 3
+
+        # Add stuff to settable and shortcuts before calling base class initializer
+        self.settable['maxrepeats'] = 'max repetitions for speak command'
+        self.shortcuts.update({'&': 'speak'})
+
+        # Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
+        super().__init__(use_ipython=True)
+
+    speak_parser = argparse.ArgumentParser()
+    speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
+    speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
+    speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
+    speak_parser.add_argument('-f', '--fg', choices=FG_COLORS, help='foreground color to apply to output')
+    speak_parser.add_argument('-b', '--bg', choices=BG_COLORS, help='background color to apply to output')
+    speak_parser.add_argument('words', nargs='+', help='words to say')
+
+    @cmd2.with_argparser(speak_parser)
+    def do_speak(self, args):
+        """Repeats what you tell me to."""
+        words = []
+        for word in args.words:
+            if args.piglatin:
+                word = '%s%say' % (word[1:], word[0])
+            if args.shout:
+                word = word.upper()
+            words.append(word)
+
+        repetitions = args.repeat or 1
+
+        color_on = ''
+        if args.fg:
+            color_on += FG_COLORS[args.fg]
+        if args.bg:
+            color_on += BG_COLORS[args.bg]
+        color_off = Fore.RESET + Back.RESET
+
+        for i in range(min(repetitions, self.maxrepeats)):
+            # .poutput handles newlines, and accommodates output redirection too
+            self.poutput(color_on + ' '.join(words) + color_off)
+
+    do_say = do_speak  # now "say" is a synonym for "speak"
+    do_orate = do_speak  # another synonym, but this one takes multi-line input
+
+    mumble_parser = argparse.ArgumentParser()
+    mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat')
+    mumble_parser.add_argument('-f', '--fg', help='foreground color to apply to output')
+    mumble_parser.add_argument('-b', '--bg', help='background color to apply to output')
+    mumble_parser.add_argument('words', nargs='+', help='words to say')
+
+    @cmd2.with_argparser(mumble_parser)
+    def do_mumble(self, args):
+        """Mumbles what you tell me to."""
+        color_on = ''
+        if args.fg and args.fg in FG_COLORS:
+            color_on += FG_COLORS[args.fg]
+        if args.bg and args.bg in BG_COLORS:
+            color_on += BG_COLORS[args.bg]
+        color_off = Fore.RESET + Back.RESET
+
+        repetitions = args.repeat or 1
+        for i in range(min(repetitions, self.maxrepeats)):
+            output = []
+            if random.random() < .33:
+                output.append(random.choice(self.MUMBLE_FIRST))
+            for word in args.words:
+                if random.random() < .40:
+                    output.append(random.choice(self.MUMBLES))
+                output.append(word)
+            if random.random() < .25:
+                output.append(random.choice(self.MUMBLE_LAST))
+            self.poutput(color_on + ' '.join(output) + color_off)
+
+
+if __name__ == '__main__':
+    c = CmdLineApp()
+    c.cmdloop()
diff --git a/examples/pirate.py b/examples/pirate.py
index 34906a9f..22274dbf 100755
--- a/examples/pirate.py
+++ b/examples/pirate.py
@@ -8,8 +8,21 @@ It demonstrates many features of cmd2.
 """
 import argparse
 
+from colorama import Fore
+
 import cmd2
 
+COLORS = {
+    'black': Fore.BLACK,
+    'red': Fore.RED,
+    'green': Fore.GREEN,
+    'yellow': Fore.YELLOW,
+    'blue': Fore.BLUE,
+    'magenta': Fore.MAGENTA,
+    'cyan': Fore.CYAN,
+    'white': Fore.WHITE,
+}
+
 
 class Pirate(cmd2.Cmd):
     """A piratical example cmd2 application involving looting and drinking."""
@@ -17,10 +30,10 @@ class Pirate(cmd2.Cmd):
         self.default_to_shell = True
         self.multiline_commands = ['sing']
         self.terminators = self.terminators + ['...']
-        self.songcolor = 'blue'
+        self.songcolor = Fore.BLUE
 
         # Add stuff to settable and/or shortcuts before calling base class initializer
-        self.settable['songcolor'] = 'Color to ``sing`` in (red/blue/green/cyan/magenta, bold, underline)'
+        self.settable['songcolor'] = 'Color to ``sing`` in (black/red/green/yellow/blue/magenta/cyan/white)'
         self.shortcuts.update({'~': 'sing'})
 
         """Initialize the base class as well as this one"""
@@ -68,7 +81,8 @@ class Pirate(cmd2.Cmd):
 
     def do_sing(self, arg):
         """Sing a colorful song."""
-        self.poutput(self.colorize(arg, self.songcolor))
+        color_escape = COLORS.get(self.songcolor, default=Fore.RESET)
+        self.poutput(arg, color=color_escape)
 
     yo_parser = argparse.ArgumentParser()
     yo_parser.add_argument('--ho', type=int, default=2, help="How often to chant 'ho'")
diff --git a/examples/plumbum_colors.py b/examples/plumbum_colors.py
new file mode 100755
index 00000000..942eaf80
--- /dev/null
+++ b/examples/plumbum_colors.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+# coding=utf-8
+"""
+A sample application for cmd2. Demonstrating colorized output using the plumbum package.
+
+Experiment with the command line options on the `speak` command to see how
+different output colors ca
+
+The colors setting has three possible values:
+
+Never
+    poutput() and pfeedback() strip all ANSI escape sequences
+    which instruct the terminal to colorize output
+
+Terminal
+    (the default value) poutput() and pfeedback() do not strip any ANSI escape
+    sequences when the output is a terminal, but if the output is a pipe or a
+    file the escape sequences are stripped. If you want colorized output you
+    must add ANSI escape sequences, preferably using some python color library
+    like `plumbum.colors`, `colorama`, `blessings`, or `termcolor`.
+
+Always
+    poutput() and pfeedback() never strip ANSI escape sequences, regardless of
+    the output destination
+
+WARNING: This example requires the plumbum package, which isn't normally required by cmd2.
+"""
+
+import random
+import argparse
+
+import cmd2
+from plumbum.colors import fg, bg, reset
+
+FG_COLORS = {
+    'black': fg.Black,
+    'red': fg.DarkRedA,
+    'green': fg.MediumSpringGreen,
+    'yellow': fg.LightYellow,
+    'blue': fg.RoyalBlue1,
+    'magenta': fg.Purple,
+    'cyan': fg.SkyBlue1,
+    'white': fg.White,
+}
+BG_COLORS = {
+    'black': bg.BLACK,
+    'red': bg.DarkRedA,
+    'green': bg.MediumSpringGreen,
+    'yellow': bg.LightYellow,
+    'blue': bg.RoyalBlue1,
+    'magenta': bg.Purple,
+    'cyan': bg.SkyBlue1,
+    'white': bg.White,
+}
+
+
+class CmdLineApp(cmd2.Cmd):
+    """Example cmd2 application demonstrating colorized output."""
+
+    # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist
+    # default_to_shell = True
+    MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh']
+    MUMBLE_FIRST = ['so', 'like', 'well']
+    MUMBLE_LAST = ['right?']
+
+    def __init__(self):
+        self.multiline_commands = ['orate']
+        self.maxrepeats = 3
+
+        # Add stuff to settable and shortcuts before calling base class initializer
+        self.settable['maxrepeats'] = 'max repetitions for speak command'
+        self.shortcuts.update({'&': 'speak'})
+
+        # Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
+        super().__init__(use_ipython=True)
+
+    speak_parser = argparse.ArgumentParser()
+    speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
+    speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
+    speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
+    speak_parser.add_argument('-f', '--fg', choices=FG_COLORS, help='foreground color to apply to output')
+    speak_parser.add_argument('-b', '--bg', choices=BG_COLORS, help='background color to apply to output')
+    speak_parser.add_argument('words', nargs='+', help='words to say')
+
+    @cmd2.with_argparser(speak_parser)
+    def do_speak(self, args):
+        """Repeats what you tell me to."""
+        words = []
+        for word in args.words:
+            if args.piglatin:
+                word = '%s%say' % (word[1:], word[0])
+            if args.shout:
+                word = word.upper()
+            words.append(word)
+
+        repetitions = args.repeat or 1
+
+        color_on = ''
+        if args.fg:
+            color_on += FG_COLORS[args.fg]
+        if args.bg:
+            color_on += BG_COLORS[args.bg]
+        color_off = reset
+
+        for i in range(min(repetitions, self.maxrepeats)):
+            # .poutput handles newlines, and accommodates output redirection too
+            self.poutput(color_on + ' '.join(words) + color_off)
+
+    do_say = do_speak  # now "say" is a synonym for "speak"
+    do_orate = do_speak  # another synonym, but this one takes multi-line input
+
+    mumble_parser = argparse.ArgumentParser()
+    mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat')
+    mumble_parser.add_argument('-f', '--fg', help='foreground color to apply to output')
+    mumble_parser.add_argument('-b', '--bg', help='background color to apply to output')
+    mumble_parser.add_argument('words', nargs='+', help='words to say')
+
+    @cmd2.with_argparser(mumble_parser)
+    def do_mumble(self, args):
+        """Mumbles what you tell me to."""
+        color_on = ''
+        if args.fg and args.fg in FG_COLORS:
+            color_on += FG_COLORS[args.fg]
+        if args.bg and args.bg in BG_COLORS:
+            color_on += BG_COLORS[args.bg]
+        color_off = Fore.RESET + Back.RESET
+
+        repetitions = args.repeat or 1
+        for i in range(min(repetitions, self.maxrepeats)):
+            output = []
+            if random.random() < .33:
+                output.append(random.choice(self.MUMBLE_FIRST))
+            for word in args.words:
+                if random.random() < .40:
+                    output.append(random.choice(self.MUMBLES))
+                output.append(word)
+            if random.random() < .25:
+                output.append(random.choice(self.MUMBLE_LAST))
+            self.poutput(color_on + ' '.join(output) + color_off)
+
+
+if __name__ == '__main__':
+    c = CmdLineApp()
+    c.cmdloop()
diff --git a/examples/python_scripting.py b/examples/python_scripting.py
index 4c959f58..0b0030a5 100755
--- a/examples/python_scripting.py
+++ b/examples/python_scripting.py
@@ -17,6 +17,8 @@ This application and the "scripts/conditional.py" script serve as an example for
 import argparse
 import os
 
+from colorama import Fore
+
 import cmd2
 
 
@@ -33,7 +35,7 @@ class CmdLineApp(cmd2.Cmd):
     def _set_prompt(self):
         """Set prompt so it displays the current working directory."""
         self.cwd = os.getcwd()
-        self.prompt = self.colorize('{!r} $ '.format(self.cwd), 'cyan')
+        self.prompt = Fore.CYAN + '{!r} $ '.format(self.cwd) + Fore.RESET
 
     def postcmd(self, stop: bool, line: str) -> bool:
         """Hook method executed just after a command dispatch is finished.
diff --git a/examples/transcripts/exampleSession.txt b/examples/transcripts/exampleSession.txt
index 6318776f..38fb0659 100644
--- a/examples/transcripts/exampleSession.txt
+++ b/examples/transcripts/exampleSession.txt
@@ -3,7 +3,7 @@
 # The regex for editor will match whatever program you use.
 # regexes on prompts just make the trailing space obvious
 (Cmd) set
-colors: /(True|False)/
+colors: /(Terminal|Always|Never)/
 continuation_prompt: >/ /
 debug: False
 echo: False
diff --git a/examples/transcripts/transcript_regex.txt b/examples/transcripts/transcript_regex.txt
index 08588ab1..6980fac6 100644
--- a/examples/transcripts/transcript_regex.txt
+++ b/examples/transcripts/transcript_regex.txt
@@ -3,7 +3,7 @@
 # The regex for editor will match whatever program you use.
 # regexes on prompts just make the trailing space obvious
 (Cmd) set
-colors: /(True|False)/
+colors: /(Terminal|Always|Never)/
 continuation_prompt: >/ /
 debug: False
 echo: False

Consider refactoring how colorize and colors work in cmd2
Currently ``cmd2`` has:
* a required dependency on [colorama](https://github.com/tartley/colorama)
    * provides cross-platform colored terminal text on Window, macOS, and Linux
* a built-in ``colorize`` method in ``Cmd`` class that provides colored terminal text on macOS and Linux
    * uses an internal dictionary which maps string color names to ANSI color escape codes

Having both of these feels awkward and redundant.  There are several possible options I could see for refactoring this including the following:
1.  Make ``colorama`` an optional dependency and refactor the **colorize** method to use ``colorama`` if present and to provide no color if it isn't
2. Keep ``colorama`` as a required dependency and refactor the **colorize** method to use it
3. Remove the dependency on ``colorama`` and use **colorize** where it is currently being used

Other possible additions include things such as:
- adding an optional **color** argument to methods such as ``poutput``
- refactoring the underlying implementation of how colors are stored
- creating a separate module which provides string color codes based on a color name lookup and wraps ``colorama`` and/or ``colored``

I would be curious to hear other people's opinions regarding how to best proceed with this.  What are your thoughts?

diff --git a/tests/conftest.py b/tests/conftest.py
index 561f281b..39aa3473 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -82,11 +82,8 @@ SHORTCUTS_TXT = """Shortcuts for other commands:
 @@: _relative_load
 """
 
-expect_colors = True
-if sys.platform.startswith('win'):
-    expect_colors = False
 # Output from the show command with default settings
-SHOW_TXT = """colors: {}
+SHOW_TXT = """colors: Terminal
 continuation_prompt: >
 debug: False
 echo: False
@@ -96,14 +93,10 @@ locals_in_py: False
 prompt: (Cmd)
 quiet: False
 timing: False
-""".format(expect_colors)
+"""
 
-if expect_colors:
-    color_str = 'True '
-else:
-    color_str = 'False'
 SHOW_LONG = """
-colors: {}             # Colorized output (*nix only)
+colors: Terminal          # Allow colorized output (valid values: Terminal, Always, Never)
 continuation_prompt: >    # On 2nd+ line of input
 debug: False              # Show full error stack on error
 echo: False               # Echo command issued into output
@@ -113,8 +106,7 @@ locals_in_py: False       # Allow access to your application in py via self
 prompt: (Cmd)             # The prompt issued to solicit input
 quiet: False              # Don't print nonessential feedback
 timing: False             # Report execution times
-""".format(color_str)
-
+"""
 
 def normalize(block):
     """ Normalize a block of text to perform comparison.
diff --git a/tests/scripts/postcmds.txt b/tests/scripts/postcmds.txt
index 2b478b57..dea8f265 100644
--- a/tests/scripts/postcmds.txt
+++ b/tests/scripts/postcmds.txt
@@ -1,1 +1,1 @@
-set colors off
+set colors Never
diff --git a/tests/scripts/precmds.txt b/tests/scripts/precmds.txt
index d0b27fb6..0ae7eae8 100644
--- a/tests/scripts/precmds.txt
+++ b/tests/scripts/precmds.txt
@@ -1,1 +1,1 @@
-set colors on
+set colors Always
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index c6a90fdf..dece1ab4 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -13,6 +13,7 @@ import os
 import sys
 import tempfile
 
+from colorama import Fore, Back, Style
 import pytest
 
 # Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available
@@ -564,11 +565,11 @@ def test_load_nested_loads(base_app, request):
     expected = """
 %s
 _relative_load precmds.txt
-set colors on
+set colors Always
 help
 shortcuts
 _relative_load postcmds.txt
-set colors off""" % initial_load
+set colors Never""" % initial_load
     assert run_cmd(base_app, 'history -s') == normalize(expected)
 
 
@@ -586,11 +587,11 @@ def test_base_runcmds_plus_hooks(base_app, request):
                                  'load ' + postfilepath])
     expected = """
 load %s
-set colors on
+set colors Always
 help
 shortcuts
 load %s
-set colors off""" % (prefilepath, postfilepath)
+set colors Never""" % (prefilepath, postfilepath)
     assert run_cmd(base_app, 'history -s') == normalize(expected)
 
 
@@ -817,12 +818,7 @@ def test_base_colorize(base_app):
     # But if we create a fresh Cmd() instance, it will
     fresh_app = cmd2.Cmd()
     color_test = fresh_app.colorize('Test', 'red')
-    # Actually, colorization only ANSI escape codes is only applied on non-Windows systems
-    if sys.platform == 'win32':
-        assert color_test == 'Test'
-    else:
-        assert color_test == '\x1b[31mTest\x1b[39m'
-
+    assert color_test == '\x1b[31mTest\x1b[39m'
 
 def _expected_no_editor_error():
     expected_exception = 'OSError'
@@ -1109,22 +1105,22 @@ def test_ansi_prompt_not_esacped(base_app):
 def test_ansi_prompt_escaped():
     from cmd2.rl_utils import rl_make_safe_prompt
     app = cmd2.Cmd()
-    color = 'cyan'
+    color = Fore.CYAN
     prompt = 'InColor'
-    color_prompt = app.colorize(prompt, color)
+    color_prompt = color + prompt + Fore.RESET
 
     readline_hack_start = "\x01"
     readline_hack_end = "\x02"
 
     readline_safe_prompt = rl_make_safe_prompt(color_prompt)
+    assert prompt != color_prompt
     if sys.platform.startswith('win'):
-        # colorize() does nothing on Windows due to lack of ANSI color support
-        assert prompt == color_prompt
-        assert readline_safe_prompt == prompt
+        # PyReadline on Windows doesn't suffer from the GNU readline bug which requires the hack
+        assert readline_safe_prompt.startswith(color)
+        assert readline_safe_prompt.endswith(Fore.RESET)
     else:
-        assert prompt != color_prompt
-        assert readline_safe_prompt.startswith(readline_hack_start + app._colorcodes[color][True] + readline_hack_end)
-        assert readline_safe_prompt.endswith(readline_hack_start + app._colorcodes[color][False] + readline_hack_end)
+        assert readline_safe_prompt.startswith(readline_hack_start + color + readline_hack_end)
+        assert readline_safe_prompt.endswith(readline_hack_start + Fore.RESET + readline_hack_end)
 
 
 class HelpApp(cmd2.Cmd):
@@ -1750,6 +1746,24 @@ def test_poutput_none(base_app):
     expected = ''
     assert out == expected
 
+def test_poutput_color_always(base_app):
+    msg = 'Hello World'
+    color = Fore.CYAN
+    base_app.colors = 'Always'
+    base_app.poutput(msg, color=color)
+    out = base_app.stdout.getvalue()
+    expected = color + msg + '\n' + Fore.RESET
+    assert out == expected
+
+def test_poutput_color_never(base_app):
+    msg = 'Hello World'
+    color = Fore.CYAN
+    base_app.colors = 'Never'
+    base_app.poutput(msg, color=color)
+    out = base_app.stdout.getvalue()
+    expected = msg + '\n'
+    assert out == expected
+
 
 def test_alias(base_app, capsys):
     # Create the alias
@@ -1968,7 +1982,6 @@ def test_bad_history_file_path(capsys, request):
         assert 'readline cannot read' in err
 
 
-
 def test_get_all_commands(base_app):
     # Verify that the base app has the expected commands
     commands = base_app.get_all_commands()
@@ -2055,3 +2068,136 @@ def test_exit_code_nonzero(exit_code_repl):
             app.cmdloop()
     out = app.stdout.getvalue()
     assert out == expected
+
+
+class ColorsApp(cmd2.Cmd):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+    def do_echo(self, args):
+        self.poutput(args)
+        self.perror(args, False)
+
+    def do_echo_error(self, args):
+        color_on = Fore.RED + Back.BLACK
+        color_off = Style.RESET_ALL
+        self.poutput(color_on + args + color_off)
+        # perror uses colors by default
+        self.perror(args, False)
+
+def test_colors_default():
+    app = ColorsApp()
+    assert app.colors == cmd2.constants.COLORS_TERMINAL
+
+def test_colors_pouterr_always_tty(mocker, capsys):
+    app = ColorsApp()
+    app.colors = cmd2.constants.COLORS_ALWAYS
+    mocker.patch.object(app.stdout, 'isatty', return_value=True)
+    mocker.patch.object(sys.stderr, 'isatty', return_value=True)
+
+    app.onecmd_plus_hooks('echo_error oopsie')
+    out, err = capsys.readouterr()
+    # if colors are on, the output should have some escape sequences in it
+    assert len(out) > len('oopsie\n')
+    assert 'oopsie' in out
+    assert len(err) > len('Error: oopsie\n')
+    assert 'ERROR: oopsie' in err
+
+    # but this one shouldn't
+    app.onecmd_plus_hooks('echo oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    # errors always have colors
+    assert len(err) > len('Error: oopsie\n')
+    assert 'ERROR: oopsie' in err
+
+def test_colors_pouterr_always_notty(mocker, capsys):
+    app = ColorsApp()
+    app.colors = cmd2.constants.COLORS_ALWAYS
+    mocker.patch.object(app.stdout, 'isatty', return_value=False)
+    mocker.patch.object(sys.stderr, 'isatty', return_value=False)
+
+    app.onecmd_plus_hooks('echo_error oopsie')
+    out, err = capsys.readouterr()
+    # if colors are on, the output should have some escape sequences in it
+    assert len(out) > len('oopsie\n')
+    assert 'oopsie' in out
+    assert len(err) > len('Error: oopsie\n')
+    assert 'ERROR: oopsie' in err
+
+    # but this one shouldn't
+    app.onecmd_plus_hooks('echo oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    # errors always have colors
+    assert len(err) > len('Error: oopsie\n')
+    assert 'ERROR: oopsie' in err
+
+def test_colors_terminal_tty(mocker, capsys):
+    app = ColorsApp()
+    app.colors = cmd2.constants.COLORS_TERMINAL
+    mocker.patch.object(app.stdout, 'isatty', return_value=True)
+    mocker.patch.object(sys.stderr, 'isatty', return_value=True)
+
+    app.onecmd_plus_hooks('echo_error oopsie')
+    # if colors are on, the output should have some escape sequences in it
+    out, err = capsys.readouterr()
+    assert len(out) > len('oopsie\n')
+    assert 'oopsie' in out
+    assert len(err) > len('Error: oopsie\n')
+    assert 'ERROR: oopsie' in err
+
+    # but this one shouldn't
+    app.onecmd_plus_hooks('echo oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    assert len(err) > len('Error: oopsie\n')
+    assert 'ERROR: oopsie' in err
+
+def test_colors_terminal_notty(mocker, capsys):
+    app = ColorsApp()
+    app.colors = cmd2.constants.COLORS_TERMINAL
+    mocker.patch.object(app.stdout, 'isatty', return_value=False)
+    mocker.patch.object(sys.stderr, 'isatty', return_value=False)
+
+    app.onecmd_plus_hooks('echo_error oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    assert err == 'ERROR: oopsie\n'
+
+    app.onecmd_plus_hooks('echo oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    assert err == 'ERROR: oopsie\n'
+
+def test_colors_never_tty(mocker, capsys):
+    app = ColorsApp()
+    app.colors = cmd2.constants.COLORS_NEVER
+    mocker.patch.object(app.stdout, 'isatty', return_value=True)
+    mocker.patch.object(sys.stderr, 'isatty', return_value=True)
+
+    app.onecmd_plus_hooks('echo_error oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    assert err == 'ERROR: oopsie\n'
+
+    app.onecmd_plus_hooks('echo oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    assert err == 'ERROR: oopsie\n'
+
+def test_colors_never_notty(mocker, capsys):
+    app = ColorsApp()
+    app.colors = cmd2.constants.COLORS_NEVER
+    mocker.patch.object(app.stdout, 'isatty', return_value=False)
+    mocker.patch.object(sys.stderr, 'isatty', return_value=False)
+
+    app.onecmd_plus_hooks('echo_error oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    assert err == 'ERROR: oopsie\n'
+
+    app.onecmd_plus_hooks('echo oopsie')
+    out, err = capsys.readouterr()
+    assert out == 'oopsie\n'
+    assert err == 'ERROR: oopsie\n'
diff --git a/tests/transcripts/regex_set.txt b/tests/transcripts/regex_set.txt
index b818c464..d45672a7 100644
--- a/tests/transcripts/regex_set.txt
+++ b/tests/transcripts/regex_set.txt
@@ -1,10 +1,10 @@
 # Run this transcript with "python example.py -t transcript_regex.txt"
-# The regex for colors is because no color on Windows.
+# The regex for colors shows all possible settings for colors
 # The regex for editor will match whatever program you use.
 # Regexes on prompts just make the trailing space obvious
 
 (Cmd) set
-colors: /(True|False)/
+colors: /(Terminal|Always|Never)/
 continuation_prompt: >/ /
 debug: False
 echo: False

pip install -e .[dev]
parse_log_pytest
pytest
pytest pytest-mock pytest-cov codecov
apt-get update apt-get install -y gcc
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
anyio==4.9.0
astroid==3.3.9
attrs==25.3.0
babel==2.17.0
backports.tarfile==1.2.0
cachetools==5.5.2
certifi==2025.1.31
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
-e git+https://github.com/python-cmd2/cmd2.git@982d2f2c2b91c04ecd2ba45dc2f6e1d26d77e4c1#egg=cmd2
codecov==2.1.13
colorama==0.4.6
coverage==7.8.0
cryptography==44.0.2
dill==0.3.9
distlib==0.3.9
docutils==0.21.2
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
filelock==3.18.0
h11==0.14.0
id==1.5.0
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
invoke==2.2.0
isort==6.0.1
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
Jinja2==3.1.6
keyring==25.6.0
markdown-it-py==3.0.0
MarkupSafe==3.0.2
mccabe==0.7.0
mdurl==0.1.2
more-itertools==10.6.0
nh3==0.2.21
packaging @ file:///croot/packaging_1734472117206/work
platformdirs==4.3.7
pluggy @ file:///croot/pluggy_1733169602837/work
pycparser==2.22
Pygments==2.19.1
pylint==3.3.6
pyperclip==1.9.0
pyproject-api==1.9.0
pytest @ file:///croot/pytest_1738938843180/work
pytest-cov==6.0.0
pytest-mock==3.14.0
readme_renderer==44.0
requests==2.32.3
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
sniffio==1.3.1
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinx-autobuild==2024.10.3
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
starlette==0.46.1
tomli==2.2.1
tomlkit==0.13.2
tox==4.25.0
twine==6.1.0
typing_extensions==4.13.0
urllib3==2.3.0
uvicorn==0.34.0
virtualenv==20.29.3
watchfiles==1.0.4
wcwidth==0.2.13
websockets==15.0.1
zipp==3.21.0

name: cmd2
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - anyio==4.9.0
      - astroid==3.3.9
      - attrs==25.3.0
      - babel==2.17.0
      - backports-tarfile==1.2.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - click==8.1.8
      - codecov==2.1.13
      - colorama==0.4.6
      - coverage==7.8.0
      - cryptography==44.0.2
      - dill==0.3.9
      - distlib==0.3.9
      - docutils==0.21.2
      - filelock==3.18.0
      - h11==0.14.0
      - id==1.5.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - invoke==2.2.0
      - isort==6.0.1
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==25.6.0
      - markdown-it-py==3.0.0
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - mdurl==0.1.2
      - more-itertools==10.6.0
      - nh3==0.2.21
      - platformdirs==4.3.7
      - pycparser==2.22
      - pygments==2.19.1
      - pylint==3.3.6
      - pyperclip==1.9.0
      - pyproject-api==1.9.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - readme-renderer==44.0
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - sniffio==1.3.1
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinx-autobuild==2024.10.3
      - sphinx-rtd-theme==3.0.2
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - starlette==0.46.1
      - tomli==2.2.1
      - tomlkit==0.13.2
      - tox==4.25.0
      - twine==6.1.0
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - uvicorn==0.34.0
      - virtualenv==20.29.3
      - watchfiles==1.0.4
      - wcwidth==0.2.13
      - websockets==15.0.1
      - zipp==3.21.0
prefix: /opt/conda/envs/cmd2

tests/test_cmd2.py::test_base_show tests/test_cmd2.py::test_base_show_long tests/test_cmd2.py::test_base_show_readonly tests/test_cmd2.py::test_poutput_color_always tests/test_cmd2.py::test_poutput_color_never tests/test_cmd2.py::test_colors_default tests/test_cmd2.py::test_colors_pouterr_always_tty tests/test_cmd2.py::test_colors_pouterr_always_notty tests/test_cmd2.py::test_colors_terminal_tty tests/test_cmd2.py::test_colors_terminal_notty tests/test_cmd2.py::test_colors_never_tty tests/test_cmd2.py::test_colors_never_notty
tests/test_cmd2.py::test_which_editor_good
tests/test_cmd2.py::test_version tests/test_cmd2.py::test_empty_statement tests/test_cmd2.py::test_base_help tests/test_cmd2.py::test_base_help_verbose tests/test_cmd2.py::test_base_help_history tests/test_cmd2.py::test_base_argparse_help tests/test_cmd2.py::test_base_invalid_option tests/test_cmd2.py::test_base_shortcuts tests/test_cmd2.py::test_cast tests/test_cmd2.py::test_cast_problems tests/test_cmd2.py::test_base_set tests/test_cmd2.py::test_set_not_supported tests/test_cmd2.py::test_set_quiet tests/test_cmd2.py::test_base_shell tests/test_cmd2.py::test_base_py tests/test_cmd2.py::test_base_run_python_script tests/test_cmd2.py::test_base_run_pyscript tests/test_cmd2.py::test_recursive_pyscript_not_allowed tests/test_cmd2.py::test_pyscript_with_nonexist_file tests/test_cmd2.py::test_pyscript_with_exception tests/test_cmd2.py::test_pyscript_requires_an_argument tests/test_cmd2.py::test_base_error tests/test_cmd2.py::test_history_span tests/test_cmd2.py::test_history_get tests/test_cmd2.py::test_base_history tests/test_cmd2.py::test_history_script_format tests/test_cmd2.py::test_history_with_string_argument tests/test_cmd2.py::test_history_with_integer_argument tests/test_cmd2.py::test_history_with_integer_span tests/test_cmd2.py::test_history_with_span_start tests/test_cmd2.py::test_history_with_span_end tests/test_cmd2.py::test_history_with_span_index_error tests/test_cmd2.py::test_history_output_file tests/test_cmd2.py::test_history_edit tests/test_cmd2.py::test_history_run_all_commands tests/test_cmd2.py::test_history_run_one_command tests/test_cmd2.py::test_history_clear tests/test_cmd2.py::test_base_load tests/test_cmd2.py::test_load_with_empty_args tests/test_cmd2.py::test_load_with_nonexistent_file tests/test_cmd2.py::test_load_with_directory tests/test_cmd2.py::test_load_with_empty_file tests/test_cmd2.py::test_load_with_binary_file tests/test_cmd2.py::test_load_with_utf8_file tests/test_cmd2.py::test_load_nested_loads tests/test_cmd2.py::test_base_runcmds_plus_hooks tests/test_cmd2.py::test_base_relative_load tests/test_cmd2.py::test_relative_load_requires_an_argument tests/test_cmd2.py::test_output_redirection tests/test_cmd2.py::test_output_redirection_to_nonexistent_directory tests/test_cmd2.py::test_output_redirection_to_too_long_filename tests/test_cmd2.py::test_feedback_to_output_true tests/test_cmd2.py::test_feedback_to_output_false tests/test_cmd2.py::test_allow_redirection tests/test_cmd2.py::test_pipe_to_shell tests/test_cmd2.py::test_pipe_to_shell_error tests/test_cmd2.py::test_base_timing tests/test_cmd2.py::test_base_debug tests/test_cmd2.py::test_base_colorize tests/test_cmd2.py::test_edit_no_editor tests/test_cmd2.py::test_edit_file tests/test_cmd2.py::test_edit_file_with_spaces tests/test_cmd2.py::test_edit_blank tests/test_cmd2.py::test_base_py_interactive tests/test_cmd2.py::test_exclude_from_history tests/test_cmd2.py::test_base_cmdloop_with_queue tests/test_cmd2.py::test_base_cmdloop_without_queue tests/test_cmd2.py::test_cmdloop_without_rawinput tests/test_cmd2.py::test_precmd_hook_success tests/test_cmd2.py::test_precmd_hook_failure tests/test_cmd2.py::test_interrupt_quit tests/test_cmd2.py::test_interrupt_noquit tests/test_cmd2.py::test_default_to_shell_unknown tests/test_cmd2.py::test_default_to_shell_good tests/test_cmd2.py::test_default_to_shell_failure tests/test_cmd2.py::test_ansi_prompt_not_esacped tests/test_cmd2.py::test_ansi_prompt_escaped tests/test_cmd2.py::test_custom_command_help tests/test_cmd2.py::test_custom_help_menu tests/test_cmd2.py::test_help_undocumented tests/test_cmd2.py::test_help_overridden_method tests/test_cmd2.py::test_help_cat_base tests/test_cmd2.py::test_help_cat_verbose tests/test_cmd2.py::test_select_options tests/test_cmd2.py::test_select_invalid_option tests/test_cmd2.py::test_select_list_of_strings tests/test_cmd2.py::test_select_list_of_tuples tests/test_cmd2.py::test_select_uneven_list_of_tuples tests/test_cmd2.py::test_help_with_no_docstring tests/test_cmd2.py::test_which_editor_bad tests/test_cmd2.py::test_multiline_complete_empty_statement_raises_exception tests/test_cmd2.py::test_multiline_complete_statement_without_terminator tests/test_cmd2.py::test_multiline_complete_statement_with_unclosed_quotes tests/test_cmd2.py::test_clipboard_failure tests/test_cmd2.py::test_commandresult_truthy tests/test_cmd2.py::test_commandresult_falsy tests/test_cmd2.py::test_is_text_file_bad_input tests/test_cmd2.py::test_eof tests/test_cmd2.py::test_eos tests/test_cmd2.py::test_echo tests/test_cmd2.py::test_pseudo_raw_input_tty_rawinput_true tests/test_cmd2.py::test_pseudo_raw_input_tty_rawinput_false tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_true_echo_true tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_true_echo_false tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_false_echo_true tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_false_echo_false tests/test_cmd2.py::test_raw_input tests/test_cmd2.py::test_stdin_input tests/test_cmd2.py::test_empty_stdin_input tests/test_cmd2.py::test_poutput_string tests/test_cmd2.py::test_poutput_zero tests/test_cmd2.py::test_poutput_empty_string tests/test_cmd2.py::test_poutput_none tests/test_cmd2.py::test_alias tests/test_cmd2.py::test_alias_with_quotes tests/test_cmd2.py::test_alias_lookup_invalid_alias tests/test_cmd2.py::test_unalias tests/test_cmd2.py::test_unalias_all tests/test_cmd2.py::test_unalias_non_existing tests/test_cmd2.py::test_create_invalid_alias[">"] tests/test_cmd2.py::test_create_invalid_alias["no>pe"] tests/test_cmd2.py::test_create_invalid_alias["no tests/test_cmd2.py::test_create_invalid_alias["nopipe|"] tests/test_cmd2.py::test_create_invalid_alias["noterm;"] tests/test_cmd2.py::test_create_invalid_alias[noembedded"quotes] tests/test_cmd2.py::test_complete_unalias tests/test_cmd2.py::test_multiple_aliases tests/test_cmd2.py::test_ppaged tests/test_cmd2.py::test_parseline_empty tests/test_cmd2.py::test_parseline tests/test_cmd2.py::test_readline_remove_history_item tests/test_cmd2.py::test_onecmd_raw_str_continue tests/test_cmd2.py::test_onecmd_raw_str_quit tests/test_cmd2.py::test_existing_history_file tests/test_cmd2.py::test_new_history_file tests/test_cmd2.py::test_bad_history_file_path tests/test_cmd2.py::test_get_all_commands tests/test_cmd2.py::test_get_help_topics tests/test_cmd2.py::test_exit_code_default tests/test_cmd2.py::test_exit_code_nonzero
cmd2/cmd2.py examples/plumbum_colors.py CHANGELOG.md examples/python_scripting.py examples/transcripts/transcript_regex.txt cmd2/constants.py examples/transcripts/exampleSession.txt examples/pirate.py examples/colors.py docs/settingchanges.rst docs/unfreefeatures.rst
cmd2/cmd2.py examples/plumbum_colors.py CHANGELOG.md examples/python_scripting.py examples/transcripts/transcript_regex.txt cmd2/constants.py examples/transcripts/exampleSession.txt examples/pirate.py examples/colors.py docs/settingchanges.rst docs/unfreefeatures.rst