--- a/sshoot/main.py
+++ b/sshoot/main.py
@@ -8,10 +8,98 @@
 from typing import Set
 
 from argcomplete import autocomplete
-from toolrack.script import (
-    ErrorExitMessage,
-    Script,
-)
+
+#from toolrack.script import (
+#    ErrorExitMessage,
+#    Script,
+#)
+#https://raw.githubusercontent.com/albertodonato/toolrack/main/toolrack/script.py
+
+import sys
+from typing import IO
+
+
+class ErrorExitMessage(Exception):
+    """Raised to exit the process with the specified message and exit code.
+
+    :param message: the error message.
+    :param code: the script exit code.
+
+    """
+
+    def __init__(self, message: str, code: int = 1) -> None:
+        self.message = message
+        self.code = code
+
+
+class Script:
+    """Wraps a python script handling argument parsing.
+
+    Subclasses must implement :func:`get_parser` and :func:`main` methods.
+
+    Inside :func:`main`, :exc:`ErrorExitMessage` can be raised with the
+    appropriate ``message`` and ``code`` to cause the script termination, with
+    the message outputted to standard error.
+
+    Script instances are callable, and can be passed the argument list (which
+    defaults to :data:`sys.argv` if not provided).
+
+    """
+
+    def __init__(
+        self, stdout: IO | None = None, stderr: IO | None = None
+    ) -> None:
+        self._stdout = stdout or sys.stdout
+        self._stderr = stderr or sys.stderr
+
+    def get_parser(self) -> ArgumentParser:
+        """Return a configured :class:`argparse.ArgumentParser` instance.
+
+        .. note::
+            Subclasses must implement this method.
+
+        """
+        raise NotImplementedError()
+
+    def main(self, args: Namespace) -> int | None:
+        """The body of the script.
+
+        It gets called with the :class:`argparse.Namespace` instance returned
+        by :func:`get_parser`.
+
+        :param args: command line arguments.
+
+        .. note::
+            Subclasses must implement this method.
+
+        """
+        raise NotImplementedError()
+
+    def exit(self, code: int = 0) -> None:
+        """Exit with the specified return code."""
+        sys.exit(code)
+
+    def handle_keyboard_interrupt(self, interrupt: KeyboardInterrupt):
+        """Called when a :class:`KeyboardInterrupt` is raised.
+
+        By default it just traps the exception and exits with success.
+        It can be overridden to perform additional cleanups.
+
+        """
+        self.exit()
+
+    def __call__(self, args: list[str] | None = None) -> int:
+        """Call the script, passing :data:`sys.argv` by default."""
+        parser = self.get_parser()
+        parsed_args = parser.parse_args(args=args)
+        try:
+            return self.main(parsed_args) or 0
+        except KeyboardInterrupt as interrupt:
+            self.handle_keyboard_interrupt(interrupt)
+        except ErrorExitMessage as error:
+            self._stderr.write(f"{error.message}\n")
+            self.exit(error.code)
+        return 0
 
 from . import __version__
 from .autocomplete import (
