diff --git a/.envrc b/.envrc
index 4857c1e..1d953f4 100644
--- a/.envrc
+++ b/.envrc
@@ -1,3 +1 @@
 use nix
-
-export BASHLY_SOURCE_DIR=$(cat .target)
diff --git a/.forgejo/workflows/release.yaml b/.forgejo/workflows/release.yaml
index a574329..a5297ea 100644
--- a/.forgejo/workflows/release.yaml
+++ b/.forgejo/workflows/release.yaml
@@ -3,45 +3,30 @@ on:
     tags:
       - v**
 jobs:
-  build-artifacts:
+  release:
     runs-on: docker
-    strategy:
-      matrix:
-        tool:
-          - autoinstall
-          - autostart-manage
     steps:
       - uses: https://code.forgejo.org/actions/checkout@v3
       - id: install-dependencies
         run: |
           apt update
+          # TODO add pandoc for docs
           apt install -y make ruby
           gem install bashly
         shell: bash
       - id: run-bashly
         run: |
-          echo "${{ matrix.tool }}" > .target
           make generate
+          #make docs
         shell: bash
-      - uses: https://code.forgejo.org/actions/upload-artifact@v3
-        with:
-          name: ${{ matrix.tool }}
-          path: ${{ github.workspace }}/output/${{ matrix.tool }}
-  upload-release:
-    runs-on: docker
-    steps:
-      - uses: https://code.forgejo.org/actions/checkout@v3
-      - uses: https://code.forgejo.org/actions/download-artifact@v3
-        with:
-          path: /tmp/artifacts-in
-          merge-multiple: true # broken "collect-artifacts" used as workaround
-      - id: collect-artifacts
+      - id: prepare-release
         run: |
-          mkdir /tmp/artifacts-out
-          mv $(find /tmp/artifacts-in -type f) /tmp/artifacts-out
+          mkdir /tmp/release
+          cp ./autostart-manage /tmp/release
+          #cp -r ./man1 /tmp/release
         shell: bash
       - uses: https://code.forgejo.org/actions/forgejo-release@v1
         with:
           direction: upload
-          release-dir: /tmp/artifacts-out
+          release-dir: /tmp/release
           token: ${{ github.token }}
diff --git a/.gitignore b/.gitignore
index d45b622..d0fb366 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
-.target
-output/
+src/lib/send_completions.sh
 
-autoinstall/lib/send_completions.sh
-autostart-manage/lib/send_completions.sh
+autostart-manage
+man1
diff --git a/Makefile b/Makefile
index b2a84de..1fc809f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,12 @@
-target:
-	@echo "Select target directory"
-	@echo $$(find . -name bashly.yml | sed -e 's/.\///' -e 's/\/.*//' | $$DMENU) > .target
+completions:
+	bashly add completions
 
-get-target:
-	$(eval TARGET := $(shell cat .target))
-
-output-dir:
-	mkdir -p ./output
-
-completions: get-target
-	BASHLY_SOURCE_DIR=$(TARGET) bashly add completions
-
-generate: get-target output-dir completions
-	BASHLY_SOURCE_DIR=$(TARGET) bashly generate --upgrade
+generate: completions
+	bashly generate --upgrade
 
 docs: generate
-	BASHLY_SOURCE_DIR=$(TARGET) bashly render :mandoc ./output/man1
+	bashly render :mandoc ./man1
 
-deploy: generate docs
-	cp -f ./output/$(TARGET) ~/.local/bin/
-	cp -f ./output/man1/$(TARGET)*.1 ~/.local/share/man/man1/
+install: generate docs
+	cp -f ./autostart-manage ~/.local/bin/
+	cp -f ./man1/autostart-manage*.1 ~/.local/share/man/man1/
diff --git a/autoinstall/archive_command.sh b/autoinstall/archive_command.sh
deleted file mode 100644
index 6019708..0000000
--- a/autoinstall/archive_command.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env bash
-
-target=${args[target]:?}
-url=${args[url]:?}
-
-if [ ! -f "$target" ]
-then
-	echo "Installing archive $url to $target"
-
-	target_dir=$(dirname "$target")
-	mkdir -p "$target_dir"
-	cd "$target_dir" || exit 1
-
-	_http_client "$url" > "$target"
-	tar xaf "$target"
-
-	_run_hook
-fi
diff --git a/autoinstall/bashly.yml b/autoinstall/bashly.yml
deleted file mode 100644
index 3a382ae..0000000
--- a/autoinstall/bashly.yml
+++ /dev/null
@@ -1,111 +0,0 @@
-name: autoinstall
-help: Install files, repository and else
-version: 0.1.1
-
-flags:
-  - long: --clean
-    short: -c
-    help: Clean exisiting targets
-  - long: --hook
-    short: -h
-    help: Hook to run if something changed
-    arg: hook
-
-environment_variables:
-  - name: AUTOINSTALL_CLEAN
-    help: Clean exisiting targets
-
-dependencies:
-  http_client:
-    command: [curl, wget]
-    help: Please install either curl or wget
-
-commands:
-  - name: completions
-    help: Generate bash completions
-
-  - name: run
-    help: Autoinstall from file
-    args:
-      - name: group
-        required: true
-        help: The group to install
-
-  - name: git
-    help: Install a git repository
-    args:
-      - name: repo
-        required: true
-        help: The url of the git repository
-      - name: target
-        required: true
-        help: The path to clone the repository into
-
-  - name: file
-    help: Install a file
-    args:
-      - name: url
-        required: true
-        help: The url of the file
-      - name: target
-        required: true
-        help: The file-path to download the file into
-    flags:
-      - long: --pipe
-        short: -p
-        help: Pipe file through command (e.g. "tar xzO")
-        arg: pipe
-        default: cat
-
-  - name: exe
-    help: Install an executable
-    args:
-      - name: url
-        required: true
-        help: The url of the file
-      - name: target
-        required: true
-        help: The file-path to download the file into
-    filename: file_command.sh
-    flags:
-      - long: --pipe
-        short: -f
-        help: Pipe file through command (e.g. "tar xzO")
-        arg: pipe
-        default: cat
-      - long: --completions
-        short: -c
-        help: Run a command on the new exe to install completions
-        arg: completions
-
-  - name: archive
-    help: Install an archive
-    args:
-      - name: url
-        required: true
-        help: The url of the arhive
-      - name: target
-        required: true
-        help: The path to clone the repository into
-
-  - name: env
-    help: Create a file with envsubst
-    args:
-      - name: template
-        required: true
-        help: The path to the template
-      - name: target
-        required: true
-        help: The path to write the result to
-    dependencies:
-      envsubst: This tool is usually part of the gettext package
-
-  - name: text
-    help: Create a file from text
-    args:
-      - name: text
-        required: true
-        help: The text to write to the file
-      - name: target
-        required: true
-        help: The path to write the result to
diff --git a/autoinstall/before.sh b/autoinstall/before.sh
deleted file mode 100644
index f88899b..0000000
--- a/autoinstall/before.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env bash
-
-action=${action:?}
-
-# if action is run or completions, do nothing
-if [ "$action" = "run" ] || [ "$action" = "completions" ]
-then
-	return
-fi
-
-clean=${args[--clean]:-}
-target=${args[target]:?}
-
-if [ "$action" = "exe" ]
-then
-	target="$HOME/.local/bin/$target"
-fi
-
-if [ -n "$clean" ] || [ -n "${AUTOINSTALL_CLEAN:-}" ]
-then
-	echo "Cleaning $target"
-	rm -rf "$target"
-fi
diff --git a/autoinstall/env_command.sh b/autoinstall/env_command.sh
deleted file mode 100644
index 74d65d7..0000000
--- a/autoinstall/env_command.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-target=${args[target]:?}
-template=${args[template]:?}
-
-if [ ! -f "$target" ]
-then
-	echo "Installing env-template $template to $target"
-
-	target_dir=$(dirname "$target")
-	mkdir -p "$target_dir"
-	cd "$target_dir" || exit 1
-
-	envsubst < "$template" > "$target"
-
-	_run_hook
-fi
diff --git a/autoinstall/file_command.sh b/autoinstall/file_command.sh
deleted file mode 100644
index 09ff198..0000000
--- a/autoinstall/file_command.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-
-pipe=${args[--pipe]:?}
-completions=${args[--completions]:-}
-target=${args[target]:?}
-url=${args[url]:?}
-
-if [ "${action:-file}" = "exe" ]
-then
-	if [ -x "$(command -v "$target")" ]
-	then
-		return
-	fi
-	exe=$target
-	target="$HOME/.local/bin/$target"
-fi
-
-
-if [ ! -f "$target" ]
-then
-	echo "Installing file $url to $target"
-
-	target_dir=$(dirname "$target")
-	mkdir -p "$target_dir"
-	cd "$target_dir" || exit 1
-
-	_http_client "$url" | eval "$pipe" > "$target"
-
-	if [ "${action:-file}" = "exe" ]
-	then
-		chmod +x "$target"
-
-		if [ -n "$completions" ]
-		then
-			echo "Installing completions for $exe"
-			# shellcheck disable=SC2086
-			"$target" $completions > "$XDG_CONFIG_HOME/completionsrc.d/_$(basename "$exe")"
-		fi
-	fi
-
-	_run_hook
-fi
diff --git a/autoinstall/git_command.sh b/autoinstall/git_command.sh
deleted file mode 100644
index 33faf65..0000000
--- a/autoinstall/git_command.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-
-target=${args[target]:?}
-repo=${args[repo]:?}
-
-if [ ! -d "$target" ]
-then
-	echo "Installing repo $repo to $target"
-
-	mkdir -p "$target"
-
-	git clone --depth=1 "$repo" "$target" >/dev/null 2>&1
-
-	_run_hook
-fi
diff --git a/autoinstall/lib/common.sh b/autoinstall/lib/common.sh
deleted file mode 100644
index 584603a..0000000
--- a/autoinstall/lib/common.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-
-_http_client() {
-	if [ -x "$(command -v curl)" ]
-	then
-		curl -fsSL "$1"
-	elif [ -x "$(command -v wget)" ]
-	then
-		wget -qO - "$1"
-	fi
-}
-
-_run_hook() {
-	hook=${args[--hook]:-}
-	if [ -n "$hook" ]
-	then
-		yellow "Running hook: $hook"
-		bash -c "$hook"
-	fi
-}
diff --git a/autoinstall/run_command.sh b/autoinstall/run_command.sh
deleted file mode 100644
index 7703daf..0000000
--- a/autoinstall/run_command.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env bash
-
-group=${args[group]:?}
-clean=${args[--clean]:-}
-
-_config_query() {
-	tomlq -c --arg group "$group" \
-		'.autoinstall | map(select(.group == $group)) | '"$1" \
-		"$XDG_CONFIG_HOME/autoinstall.toml"
-}
-
-#length=$(_config_query "length")
-_config_query ".[]" | while read -r entry; do
-	install_args=()
-
-	type=$(echo "$entry" | jq -r '.type')
-	source=$(echo "$entry" | jq -r '.source')
-	target=$(echo "$entry" | jq -r '.target')
-	hook=$(echo "$entry" | jq -r '.hook // ""')
-	pipe=$(echo "$entry" | jq -r '.pipe // ""')
-	completions=$(echo "$entry" | jq -r '.completions // ""')
-
-	if [[ -n "$hook" ]]; then
-		install_args+=("--hook=$hook")
-	fi
-	if [[ -n "$clean" ]]; then
-		install_args+=("--clean")
-	fi
-
-	install_args+=("$type")
-
-	if [[ -n "$pipe" ]]; then
-		install_args+=("--pipe=$pipe")
-	fi
-	if [[ -n "$completions" ]]; then
-		install_args+=("--completions=$completions")
-	fi
-
-	if [[ $source = \$* ]]
-	then
-		source=$(eval "echo $source")
-	fi
-	install_args+=("$source")
-
-	install_args+=("$(eval "echo $target")")
-
-	autoinstall "${install_args[@]}"
-done
diff --git a/autoinstall/text_command.sh b/autoinstall/text_command.sh
deleted file mode 100644
index 4fcd486..0000000
--- a/autoinstall/text_command.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-
-target=${args[target]:?}
-text=${args[text]:?}
-
-if [ ! -f "$target" ]
-then
-	echo "Installing text '$text' to $target"
-
-	target_dir=$(dirname "$target")
-	mkdir -p "$target_dir"
-
-	echo "$text" > "$target"
-
-	_run_hook
-fi
diff --git a/autostart-manage/completions_command.sh b/autostart-manage/completions_command.sh
deleted file mode 100644
index 0d10993..0000000
--- a/autostart-manage/completions_command.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-send_completions
diff --git a/autostart-manage/lib/colors.sh b/autostart-manage/lib/colors.sh
deleted file mode 100644
index cbdc015..0000000
--- a/autostart-manage/lib/colors.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-## Color functions [@bashly-upgrade colors]
-## This file is a part of Bashly standard library
-##
-## Usage:
-## Use any of the functions below to color or format a portion of a string.
-##
-##   echo "before $(red this is red) after"
-##   echo "before $(green_bold this is green_bold) after"
-##
-## Color output will be disabled if `NO_COLOR` environment variable is set
-## in compliance with https://no-color.org/
-##
-print_in_color() {
-  local color="$1"
-  shift
-  if [[ -z ${NO_COLOR+x} ]]; then
-    printf "$color%b\e[0m\n" "$*"
-  else
-    printf "%b\n" "$*"
-  fi
-}
-
-red() { print_in_color "\e[31m" "$*"; }
-green() { print_in_color "\e[32m" "$*"; }
-yellow() { print_in_color "\e[33m" "$*"; }
-blue() { print_in_color "\e[34m" "$*"; }
-magenta() { print_in_color "\e[35m" "$*"; }
-cyan() { print_in_color "\e[36m" "$*"; }
-bold() { print_in_color "\e[1m" "$*"; }
-underlined() { print_in_color "\e[4m" "$*"; }
-red_bold() { print_in_color "\e[1;31m" "$*"; }
-green_bold() { print_in_color "\e[1;32m" "$*"; }
-yellow_bold() { print_in_color "\e[1;33m" "$*"; }
-blue_bold() { print_in_color "\e[1;34m" "$*"; }
-magenta_bold() { print_in_color "\e[1;35m" "$*"; }
-cyan_bold() { print_in_color "\e[1;36m" "$*"; }
-red_underlined() { print_in_color "\e[4;31m" "$*"; }
-green_underlined() { print_in_color "\e[4;32m" "$*"; }
-yellow_underlined() { print_in_color "\e[4;33m" "$*"; }
-blue_underlined() { print_in_color "\e[4;34m" "$*"; }
-magenta_underlined() { print_in_color "\e[4;35m" "$*"; }
-cyan_underlined() { print_in_color "\e[4;36m" "$*"; }
diff --git a/autostart-manage/lib/common.sh b/autostart-manage/lib/common.sh
deleted file mode 100644
index dfbbb1c..0000000
--- a/autostart-manage/lib/common.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-
-_systemctl () {
-	systemctl --user "${@:2}" "autostart@$1.service"
-}
-
-_query_autostart_toml() {
-	tomlq -r --arg host "$HOSTNAME" \
-    '.hosts[$host].groups as $groups | .apps | to_entries[] | select(
-        (.value.hosts | contains([$host])) or
-        ([.value.group] | inside($groups))
-    ) | '"$1" \
-    "$XDG_CONFIG_HOME/autostart.toml"
-}
-
-_list () {
-	_query_autostart_toml '.value.alias // .key'
-}
-
-_autostart_run_graphical () {
-	set +e
-	pass x # Try to unlock yubikey asap
-
-	start-audio pipewire
-	wait-for-service "network-online.target"
-
-	# Execute only if not already running
-	# Don't match keepassxc-proxy
-	if ! (pgrep -l keepassxc | grep -v prox) >/dev/null
-	then
-		if pass x
-		then
-			(pass keepass | head -n 1 | keepassxc --pw-stdin ~/sync/passwords.kdbx) &
-		fi
-	fi
-
-	autoinstall run graphical
-	autostart-manage run
-}
-
-_echo_table () {
-	for unit in "$@"
-	do
-		printf "%s\t%s\t%s\t%s\n" \
-			"$unit" \
-			"$(_systemctl "$unit" is-enabled)" \
-			"$(_systemctl "$unit" is-active)" \
-			"$(_get_autostart_cmd "$unit")"
-	done
-}
-
-_get_autostart_cmd () {
-	_query_autostart_toml 'select((.key == "'"$1"'") or (.value.alias == "'"$1"'")) | .value.command'
-}
-
-_get_autostart_delay () {
-	_query_autostart_toml 'select((.key == "'"$1"'") or (.value.alias == "'"$1"'")) | .value.delay // 0'
-}
diff --git a/autostart-manage/log_command.sh b/autostart-manage/log_command.sh
deleted file mode 100644
index 7d9657c..0000000
--- a/autostart-manage/log_command.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-journalctl --user -fu "autostart@${args[program]:?}.service"
diff --git a/autostart-manage/sync_command.sh b/autostart-manage/sync_command.sh
deleted file mode 100644
index d08ac71..0000000
--- a/autostart-manage/sync_command.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-rm "$HOME/.config/systemd/user/autostart.target.wants/"*
-
-autostart_units=()
-while IFS='' read -r line
-do
-	autostart_units+=("$line")
-done < <(_list)
-
-for unit in "${autostart_units[@]}"
-do
-	_systemctl "$unit" add-wants autostart.target
-done
diff --git a/run.sh b/run.sh
index 67177b3..8125377 100755
--- a/run.sh
+++ b/run.sh
@@ -1,9 +1,7 @@
 #!/usr/bin/env sh
 
-target=$(cat .target)
-
-[ -n "$target" ] || exit 1
-
 make generate
 
-"./output/$target" "$@"
+printf "\nBuild complete. Running...\n==========================\n\n"
+
+time ./autostart-manage "$@"
diff --git a/settings.yml b/settings.yml
deleted file mode 100644
index 63e7150..0000000
--- a/settings.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-# The path to bashly.yml
-config_path: "%{source_dir}/bashly.yml"
-
-# The path to use for creating the bash script
-target_dir: output
-
-# The path to use for common library files, relative to source_dir
-lib_dir: lib
-
-# The path to use for command files, relative to source_dir
-# When set to nil (~), command files will be placed directly under source_dir
-# When set to any other string, command files will be placed under this
-# directory, and each command will get its own subdirectory
-commands_dir: ~
-
-# Configure the bash options that will be added to the initialize function:
-# strict: true       Bash strict mode (set -euo pipefail)
-# strict: false      Only exit on errors (set -e)
-# strict: ''         Do not add any 'set' directive
-# strict: <string>   Add any other custom 'set' directive
-strict: true
-
-# When true, the generated script will use tab indentation instead of spaces
-# (every 2 leading spaces will be converted to a tab character)
-tab_indent: true
-
-# When true, the generated script will consider any argument in the form of
-# `-abc` as if it is `-a -b -c`.
-compact_short_flags: true
-
-# Set to 'production' or 'development':
-# env: production    Generate a smaller script, without file markers
-# env: development   Generate with file markers
-env: development
-
-# The extension to use when reading/writing partial script snippets
-partials_extension: sh
-
-# Display various usage elements in color by providing the name of the color
-# function. The value for each property is a name of a function that is
-# available in your script, for example: `green` or `bold`.
-# You can run `bashly add colors` to add a standard colors library.
-# This option cannot be set via environment variables.
-usage_colors:
-  caption: blue
-  command: green
-  arg: red
-  flag: yellow
-  environment_variable: cyan
diff --git a/autostart-manage/bashly.yml b/src/bashly.yml
similarity index 88%
rename from autostart-manage/bashly.yml
rename to src/bashly.yml
index 4c292bd..a578c1b 100644
--- a/autostart-manage/bashly.yml
+++ b/src/bashly.yml
@@ -1,6 +1,6 @@
 name: autostart-manage
 help: Manage autostart
-version: 0.1.1
+version: 0.1.4
 
 dependencies:
   tomlq: please install yq (https://github.com/kislyuk/yq)
@@ -19,6 +19,10 @@ commands:
     help: Get the current status of all programs
   - name: sync
     help: Remove all programs from autostart and the re-enable all
+    args:
+      - name: groups
+        required: false
+        help: Extra groups to sync
   - name: enable
     help: Add a single program to autostart
     args:
@@ -73,6 +77,15 @@ commands:
     completions:
       - $(autostart-manage list)
     filename: systemctl.sh
+  - name: kill
+    help: Kill the program from autostart
+    args:
+      - name: program
+        required: true
+        help: Program to kill
+    completions:
+      - $(autostart-manage list)
+    filename: systemctl.sh
   - name: log
     alias: logs
     help: Show the log for a single program from autostart
diff --git a/src/before.sh b/src/before.sh
new file mode 100644
index 0000000..b2e3c84
--- /dev/null
+++ b/src/before.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+service_file="$HOME/.config/systemd/user/autostart@.service"
+if [ ! -f "$service_file" ]; then
+  _deploy_service_file "$service_file"
+fi
+
+target_file="$HOME/.config/systemd/user/autostart.target"
+if [ ! -f "$target_file" ]; then
+  _deploy_target_file "$target_file"
+fi
diff --git a/autoinstall/completions_command.sh b/src/completions_command.sh
similarity index 100%
rename from autoinstall/completions_command.sh
rename to src/completions_command.sh
diff --git a/autostart-manage/exec_command.sh b/src/exec_command.sh
similarity index 63%
rename from autostart-manage/exec_command.sh
rename to src/exec_command.sh
index 2842069..2e2c816 100644
--- a/autostart-manage/exec_command.sh
+++ b/src/exec_command.sh
@@ -2,6 +2,5 @@
 
 program=${args[program]:?}
 
-cmd=$(_get_autostart_cmd "$program")
 sleep "$(_get_autostart_delay "$program")"
-bash -c "$cmd"
+bash -c "$(_get_autostart_cmd "$program")"
diff --git a/autostart-manage/info_command.sh b/src/info_command.sh
similarity index 90%
rename from autostart-manage/info_command.sh
rename to src/info_command.sh
index 1f895e3..566cdce 100644
--- a/autostart-manage/info_command.sh
+++ b/src/info_command.sh
@@ -4,6 +4,6 @@ autostart_units=()
 while IFS='' read -r line
 do
 	autostart_units+=("$line")
-done < <(_list)
+done < <(_list "*")
 
 _echo_table "${autostart_units[@]}" | column -t -s$'\t' --table-columns 'Unit,Enabled?,Active?,Command'
diff --git a/autoinstall/lib/colors.sh b/src/lib/colors.sh
similarity index 83%
rename from autoinstall/lib/colors.sh
rename to src/lib/colors.sh
index cbdc015..7458e2d 100644
--- a/autoinstall/lib/colors.sh
+++ b/src/lib/colors.sh
@@ -26,17 +26,26 @@ yellow() { print_in_color "\e[33m" "$*"; }
 blue() { print_in_color "\e[34m" "$*"; }
 magenta() { print_in_color "\e[35m" "$*"; }
 cyan() { print_in_color "\e[36m" "$*"; }
+black() { print_in_color "\e[30m" "$*"; }
+white() { print_in_color "\e[37m" "$*"; }
+
 bold() { print_in_color "\e[1m" "$*"; }
 underlined() { print_in_color "\e[4m" "$*"; }
+
 red_bold() { print_in_color "\e[1;31m" "$*"; }
 green_bold() { print_in_color "\e[1;32m" "$*"; }
 yellow_bold() { print_in_color "\e[1;33m" "$*"; }
 blue_bold() { print_in_color "\e[1;34m" "$*"; }
 magenta_bold() { print_in_color "\e[1;35m" "$*"; }
 cyan_bold() { print_in_color "\e[1;36m" "$*"; }
+black_bold() { print_in_color "\e[1;30m" "$*"; }
+white_bold() { print_in_color "\e[1;37m" "$*"; }
+
 red_underlined() { print_in_color "\e[4;31m" "$*"; }
 green_underlined() { print_in_color "\e[4;32m" "$*"; }
 yellow_underlined() { print_in_color "\e[4;33m" "$*"; }
 blue_underlined() { print_in_color "\e[4;34m" "$*"; }
 magenta_underlined() { print_in_color "\e[4;35m" "$*"; }
 cyan_underlined() { print_in_color "\e[4;36m" "$*"; }
+black_underlined() { print_in_color "\e[4;30m" "$*"; }
+white_underlined() { print_in_color "\e[4;37m" "$*"; }
diff --git a/src/lib/common.sh b/src/lib/common.sh
new file mode 100644
index 0000000..2f99562
--- /dev/null
+++ b/src/lib/common.sh
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+
+_systemctl () {
+	systemctl --user "${@:2}" "autostart@$1.service"
+}
+
+_query_autostart_toml() {
+	groups_json=$(echo -n "$2" | tr "," "\n" | jq -R . | jq -s .)
+	tomlq -r \
+		--arg host "$HOSTNAME" \
+		--argjson extra_groups "$groups_json" \
+		'.hosts[$host].groups as $groups | .apps | to_entries[] | select(
+			(.value.hosts | contains([$host])) or
+			(
+				(
+					([.value.group] | inside($groups)) or
+					([.value.group] | inside($extra_groups)) or
+					$extra_groups == ["*"]
+				) and (.value.group != "")
+			)
+		) | '"$1" \
+		"$XDG_CONFIG_HOME/autostart.toml"
+}
+
+_list () {
+	_query_autostart_toml '.value.alias // .key' "$1"
+}
+
+_autostart_run_graphical () {
+	set +e
+	pass x # Try to unlock yubikey asap
+
+	start-audio pipewire
+	wait-for-service "network-online.target"
+
+	# Execute only if not already running
+	# Don't match keepassxc-proxy
+	if ! (pgrep -l keepassxc | grep -v prox) >/dev/null
+	then
+		if pass x
+		then
+			(pass keepass | head -n 1 | keepassxc --pw-stdin ~/sync/passwords.kdbx) &
+		fi
+	fi
+
+	autoinstall run graphical
+	autostart-manage run
+}
+
+_echo_table () {
+	for unit in "$@"
+	do
+		if [ "$(_systemctl "$unit" is-enabled)" = "enabled" ]
+		then
+			_enabled=$(green "enabled")
+		else
+			_enabled=$(red "disabled")
+		fi
+
+		if _systemctl "$unit" is-active --quiet
+		then
+			_active=$(green "active")
+		else
+			_active=$(red "inactive")
+		fi
+
+		printf "%s\t%s\t%s\t%s\n" \
+			"$unit" \
+			"$_enabled" \
+			"$_active" \
+			"$(_get_autostart_cmd "$unit")"
+	done
+}
+
+_get_autostart_cmd () {
+	_query_autostart_toml 'select((.key == "'"$1"'") or (.value.alias == "'"$1"'")) | .value.command' "*"
+}
+
+_get_autostart_delay () {
+	_query_autostart_toml 'select((.key == "'"$1"'") or (.value.alias == "'"$1"'")) | .value.delay // 0' "*"
+}
diff --git a/src/lib/systemd_files.sh b/src/lib/systemd_files.sh
new file mode 100644
index 0000000..d5eb682
--- /dev/null
+++ b/src/lib/systemd_files.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+_deploy_service_file() {
+  cat <<EOF > "$1"
+[Unit]
+Description=Autostart several tools and services
+StartLimitIntervalSec=120
+StartLimitBurst=10
+
+[Service]
+KillMode=process
+ExecStart=/bin/sh -c ". \$HOME/.profile && autostart-manage exec '%i'"
+Restart=on-failure
+RestartSec=5s
+EOF
+}
+
+_deploy_target_file() {
+  cat <<EOF > "$1"
+[Unit]
+Description=Current graphical user session
+Documentation=man:systemd.special(7)
+RefuseManualStart=no
+StopWhenUnneeded=no
+EOF
+}
diff --git a/autostart-manage/list_command.sh b/src/list_command.sh
similarity index 67%
rename from autostart-manage/list_command.sh
rename to src/list_command.sh
index 0986740..c732452 100644
--- a/autostart-manage/list_command.sh
+++ b/src/list_command.sh
@@ -1,3 +1,3 @@
 #!/usr/bin/env bash
 
-_list
+_list "*"
diff --git a/src/log_command.sh b/src/log_command.sh
new file mode 100644
index 0000000..8f8f075
--- /dev/null
+++ b/src/log_command.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+journalctl --user -b0 -fu "autostart@${args[program]:?}.service"
diff --git a/autostart-manage/run_command.sh b/src/run_command.sh
similarity index 100%
rename from autostart-manage/run_command.sh
rename to src/run_command.sh
diff --git a/autostart-manage/run_wayland_command.sh b/src/run_wayland_command.sh
similarity index 100%
rename from autostart-manage/run_wayland_command.sh
rename to src/run_wayland_command.sh
diff --git a/autostart-manage/run_xorg_command.sh b/src/run_xorg_command.sh
similarity index 100%
rename from autostart-manage/run_xorg_command.sh
rename to src/run_xorg_command.sh
diff --git a/src/sync_command.sh b/src/sync_command.sh
new file mode 100644
index 0000000..49dfd4e
--- /dev/null
+++ b/src/sync_command.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+rm -f "$HOME/.config/systemd/user/autostart.target.wants/"*
+
+autostart_units=()
+while IFS='' read -r line
+do
+	autostart_units+=("autostart@$line.service")
+done < <(_list "${args[groups]:-}")
+
+systemctl --user add-wants autostart.target "${autostart_units[@]}"
diff --git a/autostart-manage/systemctl.sh b/src/systemctl.sh
similarity index 100%
rename from autostart-manage/systemctl.sh
rename to src/systemctl.sh