diff --git a/.gitignore b/.gitignore
index 0b6d13d94e1ea082686bbc137a1f5c1bf112360c..3a1ec4c01c7de14864c8405f0cf60cef6a8116ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,7 @@
 **/*.pyc
 logs
 dev-23a/
-*.log
\ No newline at end of file
+*.log
+dist/
+build/
+*.egg-info/
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 05f003eca71090f8574e469f448bb8d838fd62c0..b70ecaf92cd7962f8a0386c1669495facadf6b0e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -80,10 +80,10 @@ style:
     - pydocstyle --version
     - ruff .
     - black --check --diff .
-    - pydocstyle mpsd-software.py
-    - pydocstyle test_mpsd-software.py
+    - pydocstyle src/mpsd_software_manager/mpsd_software.py
+    - pydocstyle tests/test_mpsd_software.py
     # we could also use `ruff --select D` for pycodestyle. But the behaviour is not exactly the same.
-    - ./mpsd-software.py --version
+    - src/mpsd_software_manager/mpsd_software.py --version
 
 
 test-bullseye:
@@ -91,12 +91,11 @@ test-bullseye:
   image: debian:bullseye-slim
   script:
     - *prepare_debian
-    - pytest -v -l test_mpsd-software.py
-
+    - pytest -v -l 
 test-bookworm:
   stage: test
   image: debian:bookworm-slim
   script:
     - *prepare_debian
-    - pytest -v -l test_mpsd-software.py
+    - pytest -v -l 
 
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..688828ac52250885608483777fdc5343f4430699
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2023 SSU-Computational Science Group (Fangohr et al)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.rst b/README.rst
index c29a590af31b643b3ae901c564f33e9789b51a94..8008ca8a35ef39d2cd55a6f663680805b9f522a6 100644
--- a/README.rst
+++ b/README.rst
@@ -34,22 +34,23 @@ To install, for example, the ``foss2022a-serial`` toolchain:
      $ cd /home/user/mpsd-software
 
 
-3. Initiate the installation at this location using::
-
-     $ mpsd-software init
-
-   (This creates a hidden file ``.mpsd-software-root`` to tag the location for
-   as the root of the installation. All compiled files, logs etc are written in
-   or below this subdirectory.)
-
-4. From the same directory, run the command to install the ``foss2022a-serial``
+.. comment:
+    3. Initiate the installation at this location using::
+    
+         $ mpsd-software init
+    
+       (This creates a hidden file ``.mpsd-software-root`` to tag the location for
+       as the root of the installation. All compiled files, logs etc are written in
+       or below this subdirectory.)
+    
+3. From the same directory, run the command to install the ``foss2022a-serial``
    toolchain::
 
      $ mpsd-software install dev-23a foss2022a-serial
 
    This will take some time (up to several hours depending on hardware).
 
-5. To see the installation status, and the required ``module use`` command line
+4. To see the installation status, and the required ``module use`` command line
    to activate the created modules, try the ``status`` command::
 
      $ mpsd-software status dev-23a
@@ -166,8 +167,10 @@ The compilation of the ``*-mpi`` toolchains needs linux headers installed. (TODO
 Working example
 ~~~~~~~~~~~~~~~
 
-We have a continuous integration example at XXX, that shows the complete
-compilation cycle (including compilation of Octopus). (TODO)
+There is an
+[example](https://github.com/mpsd-computational-science/octopus-with-mpsd-software)
+compilation that shows the complete compilation cycle (including compilation of
+Octopus) using the ``foss2022a-serial`` toolchain. 
 
 
 
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..43fabe2af26474f3f89816fcbb6aec5ceb31dd79
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,35 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "mpsd_software_manager"
+authors = [{name = "SSU-Computational Science (Fangohr et al)", email = "ssu-cs@mpsd.mpg.de"}]
+license = {file = "LICENSE"}
+classifiers = ["License :: OSI Approved :: MIT License"]
+version = "2023.6.14"
+readme = "README.rst"
+dependencies = [
+    "archspec",
+    "rich",
+]
+[project.scripts]
+mpsd-software = "mpsd_software_manager.mpsd_software:main"
+
+[project.urls]
+Home = "https://gitlab.gwdg.de/mpsd-cs/mpsd-software-manager/"
+
+[project.optional-dependencies]
+dev = [
+    "black",
+    "pytest",
+    "pytest-mock",
+    "pytest-cov",
+    "ruff",
+]
+
+[tool.pytest.ini_options]
+addopts = [
+    "--import-mode=importlib",
+]
+pythonpath = "src"
diff --git a/src/mpsd_software_manager/__init__.py b/src/mpsd_software_manager/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..58a386508c18735f483fcd5ccebe9ebbf9867c2e
--- /dev/null
+++ b/src/mpsd_software_manager/__init__.py
@@ -0,0 +1 @@
+"""mpsd-software: tool for installation of software as on MPSD HPC."""
diff --git a/mpsd-software.py b/src/mpsd_software_manager/mpsd_software.py
similarity index 97%
rename from mpsd-software.py
rename to src/mpsd_software_manager/mpsd_software.py
index 438dccc4f1ec39abc6c1163ac6468d3dd9f7d8e6..1d9cb42a19e818d1edc51ad54cab374dcd90b49b 100755
--- a/mpsd-software.py
+++ b/src/mpsd_software_manager/mpsd_software.py
@@ -599,24 +599,28 @@ def record_script_execution_summary(
                 cmd_line = " ".join(sys.argv)
                 # script branch and commit hash
                 with os_chdir(root_dir):
-                    script_branch = (
-                        run(
-                            ["git", "rev-parse", "--abbrev-ref", "HEAD"],
-                            stdout=subprocess.PIPE,
-                            check=True,
+                    try:
+                        script_branch = (
+                            run(
+                                ["git", "rev-parse", "--abbrev-ref", "HEAD"],
+                                stdout=subprocess.PIPE,
+                                check=True,
+                            )
+                            .stdout.decode()
+                            .strip()
                         )
-                        .stdout.decode()
-                        .strip()
-                    )
-                    script_commit_hash = (
-                        run(
-                            ["git", "rev-parse", "--short", "HEAD"],
-                            stdout=subprocess.PIPE,
-                            check=True,
+                        script_commit_hash = (
+                            run(
+                                ["git", "rev-parse", "--short", "HEAD"],
+                                stdout=subprocess.PIPE,
+                                check=True,
+                            )
+                            .stdout.decode()
+                            .strip()
                         )
-                        .stdout.decode()
-                        .strip()
-                    )
+                    except subprocess.CalledProcessError:
+                        script_branch = "unknown"
+                        script_commit_hash = "unknown"
                 # spack-environments branch and commit hash from kwargs
                 spe_branch = kwargs.get("spe_branch", None)
                 spe_commit_hash = kwargs.get("spe_commit_hash", None)
@@ -1189,8 +1193,8 @@ def main():
     # Carry out the action
     args = parser.parse_args()
 
-    # target dir is the place where this script exists. the
-    root_dir = Path(os.path.dirname(os.path.realpath(__file__)))
+    # root dir is the place where this script is called from
+    root_dir = Path(os.getcwd())
 
     set_up_logging(
         args.loglevel,
diff --git a/test_mpsd-software.py b/tests/test_mpsd_software.py
similarity index 98%
rename from test_mpsd-software.py
rename to tests/test_mpsd_software.py
index ce31308efe3dfd5ed377aeb8c7837bc5122ffc5c..6c6c7134cb5aaec6197ff0e846ab7808ded00886 100644
--- a/test_mpsd-software.py
+++ b/tests/test_mpsd_software.py
@@ -11,7 +11,7 @@ import sys
 
 import pytest
 
-mod = importlib.import_module("mpsd-software")
+mod = importlib.import_module("mpsd_software_manager.mpsd_software")
 
 # set loglevel to debug - useful for understanding problems.
 # (if the tests pass, pytest doesn't show any output)
@@ -190,13 +190,15 @@ def test_record_script_execution_summary(tmp_path):
 
 def test_install_environment_wrong_package_set(tmp_path):
     """Test exception is raised for non-existing package_set."""
-    # Expect an Exception when wrong package_sets are provided
-    with pytest.raises(Exception):
+    # exits with exit code 1 when wrong package_sets are provided
+    with pytest.raises(SystemExit) as e:
         mod.install_environment(
             mpsd_release="dev-23a",
             package_sets=["wrong-package_set"],
             root_dir=(tmp_path),
         )
+    assert e.type == SystemExit
+    assert e.value.code == 1
 
 
 def test_install_environment_wrong_mpsd_release(tmp_path):