diff --git a/.config b/.config
index 7fd4b61..476e7bd 100644
--- a/.config
+++ b/.config
@@ -1,14 +1,32 @@
-#
-# scwrypts config
-#
+#####################################################################
 
-# resource paths
 SCWRYPTS_CONFIG_PATH="$HOME/.config/scwrypts"
-SCWRYPTS_ENV_PATH="$SCWRYPTS_CONFIG_PATH/env"
-SCWRYPTS_LOG_PATH="$SCWRYPTS_CONFIG_PATH/logs"
+SCWRYPTS_DATA_PATH="$HOME/.local/share/scwrypts"
 
-SCWRYPTS_OUTPUT_PATH="$HOME/SCWRYPTS"
-
-# ZLE hotkeys
-SCWRYPTS_SHORTCUT=''     # CTRL + W
+SCWRYPTS_SHORTCUT=''     # CTRL + SPACE
 SCWRYPTS_ENV_SHORTCUT='' # CTRL + /
+
+#####################################################################
+
+SCWRYPTS_ENV_PATH="$SCWRYPTS_CONFIG_PATH/env"
+SCWRYPTS_LOG_PATH="$SCWRYPTS_DATA_PATH/logs"
+
+SCWRYPTS_OUTPUT_PATH="$SCWRYPTS_DATA_PATH/output"
+SCWRYPTS_VIRTUALENV_PATH="$SCWRYPTS_DATA_PATH/virtualenv"
+
+[ -f $SCWRYPTS_CONFIG_PATH/config ] && source $SCWRYPTS_CONFIG_PATH/config
+
+#####################################################################
+
+[ ! -d $SCWRYPTS_CONFIG_PATH ] && mkdir -p $SCWRYPTS_CONFIG_PATH
+[ ! -d $SCWRYPTS_DATA_PATH ] && mkdir -p $SCWRYPTS_DATA_PATH
+
+
+[ ! -d $SCWRYPTS_ENV_PATH    ] && mkdir -p $SCWRYPTS_ENV_PATH
+[ ! -d $SCWRYPTS_LOG_PATH    ] && mkdir -p $SCWRYPTS_LOG_PATH
+
+[ ! -d $SCWRYPTS_OUTPUT_PATH ] && mkdir -p $SCWRYPTS_OUTPUT_PATH
+[ ! -d $SCWRYPTS_VIRTUALENV_PATH ] && mkdir -p $SCWRYPTS_VIRTUALENV_PATH
+
+#####################################################################
+true
diff --git a/.template.env b/.env.template
similarity index 100%
rename from .template.env
rename to .env.template
diff --git a/.gitattributes b/.gitattributes
index febccb6..4005fb0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,2 @@
 *.zsh diff
+.config diff
diff --git a/README.md b/README.md
index fd5eb70..99d027a 100644
--- a/README.md
+++ b/README.md
@@ -28,12 +28,9 @@ Check out [Meta Scwrypts](./zsh/scwrypts) to quickly set up environments and adj
 ### No Install / API Usage
 Alternatively, the `scwrypts` API can be used directly:
 ```zsh
-./scwrypts (environment-name) (...script-patterns)
+./scwrypts [--env environment-name] (...script-name-patterns...) [-- ...passthrough arguments... ]
 ```
 
-If not already set with `$SCWRYPTS_ENV`, Scwrypts will try to load `$1` as an environment.
-If no environment with the name `$1` is found, `$1` is assumed to be a script pattern.
-
 Given one or more script patterns, Scwrypts will filter the commands by pattern conjunction.
 If only one command is found which matches the pattern(s), it will immediately begin execution.
 If multiple commands match, the user will be prompted to select from the filtered list.
@@ -44,6 +41,16 @@ Given no script patterns, Scwrypts becomes an interactive CLI, prompting the use
 After determining which script to run, if no environment has been specified, Scwrypts prompts the user to choose one.
 
 
+### Using in CI/CD or Automated Workflows
+Set environment variable `CI=true` (and use the no install method) to run in an automated pipeline.
+There are a few notable changes to this runtime:
+- **The Scwrypts sandbox environment will not load.** All variables will be read from context.
+	- The underscore-prefixed `_AWS_(PROFILE|REGION|ACCOUNT)` variables will be read from the standard `AWS_` variables
+- User yes/no prompts will **always be YES**
+- Other user input will default to an empty string
+- Logs will not be captured
+
+
 ## Contributing
 
 Before contributing an issue, idea, or pull request, check out the [super-brief contributing guide](./docs/CONTRIBUTING.md)
diff --git a/py/.gitignore b/py/.gitignore
index 1b058a1..4534f5c 100644
--- a/py/.gitignore
+++ b/py/.gitignore
@@ -1,4 +1,3 @@
 __pycache__/
 *.py[cod]
 *.so
-.env/
diff --git a/py/redis/interactive.py b/py/redis/interactive.py
index aa41450..51f84cd 100755
--- a/py/redis/interactive.py
+++ b/py/redis/interactive.py
@@ -1,16 +1,15 @@
 #!/usr/bin/env python
-from os import getenv
 
 from py.redis.client import Client
-from py.scwrypts import interactive
+from py.scwrypts import interactive, getenv
 
 
 @interactive
 def main():
     r = Client
 
-    print('''
-    r = StrictRedis("{getenv("REDIS_HOST")}")
+    print(f'''
+>>> r = StrictRedis({getenv("REDIS_HOST")}:{getenv("REDIS_PORT")})
     ''')
 
     return locals()
diff --git a/py/scwrypts/__init__.py b/py/scwrypts/__init__.py
index aa7178d..9f5e6a0 100644
--- a/py/scwrypts/__init__.py
+++ b/py/scwrypts/__init__.py
@@ -1,2 +1,3 @@
 from py.scwrypts.getenv import getenv
 from py.scwrypts.interactive import interactive
+from py.scwrypts.run import run
diff --git a/py/scwrypts/getenv.py b/py/scwrypts/getenv.py
index 1fbbb9a..c82f512 100644
--- a/py/scwrypts/getenv.py
+++ b/py/scwrypts/getenv.py
@@ -1,23 +1,16 @@
 from os import getenv as os_getenv
-from pathlib import Path
-from subprocess import run
 
 from py.scwrypts.exceptions import MissingVariableError
+from py.scwrypts.run import run
 
 
 def getenv(name, required=True):
     value = os_getenv(name, None)
 
     if value == None:
-        ZSH_COMMAND = Path(__file__).parents[2] / 'zsh/scwrypts/environment/stage-variables'
+        run('zsh/scwrypts/environment/stage-variables', name)
 
-        run(
-            f'{ZSH_COMMAND} {name}',
-            shell=True,
-            executable='/bin/zsh',
-            )
-
-        if required:
-            raise MissingVariableError(name)
+    if required and not value:
+        raise MissingVariableError(name)
 
     return value
diff --git a/py/scwrypts/interactive.py b/py/scwrypts/interactive.py
index 259a4da..53d86fc 100644
--- a/py/scwrypts/interactive.py
+++ b/py/scwrypts/interactive.py
@@ -3,7 +3,9 @@ from bpython import embed
 
 def interactive(function):
     def main(*args, **kwargs):
+        print('preparing interactive environment...')
         local_vars = function(*args, **kwargs)
+        print('environment ready; user, GO! :)')
         embed(local_vars)
 
     return main
diff --git a/py/scwrypts/run.py b/py/scwrypts/run.py
new file mode 100644
index 0000000..9bf8dd2
--- /dev/null
+++ b/py/scwrypts/run.py
@@ -0,0 +1,17 @@
+from os import getenv
+from pathlib import Path
+from subprocess import run as subprocess_run
+
+
+def run(scwrypt_name, *args):
+    DEPTH = int(getenv('SUBSCWRYPT', '0'))
+    DEPTH += 1
+
+    print(f'\n {"--"*DEPTH} ({DEPTH}) BEGIN SUBSCWRYPT : {Path(scwrypt_name).name}')
+    subprocess_run(
+        f'SUBSCWRYPT={DEPTH} {Path(__file__).parents[2] / "scwrypts"} {scwrypt_name} -- {" ".join([str(x) for x in args])}',
+        shell=True,
+        executable='/bin/zsh',
+        )
+
+    print(f' {"--"*DEPTH} ({DEPTH}) END SUBSCWRYPT   : {Path(scwrypt_name).name}\n')
diff --git a/scwrypts b/scwrypts
index 4ad5a80..a0c45c8 100755
--- a/scwrypts
+++ b/scwrypts
@@ -1,32 +1,117 @@
 #!/bin/zsh
 SCWRYPTS_ROOT="${0:a:h}"
-source "$SCWRYPTS_ROOT/zsh/common.zsh"
+source "$SCWRYPTS_ROOT/zsh/common.zsh" || exit 42
 #####################################################################
 
 __RUN() {
 	cd "$SCWRYPTS_ROOT"
 
-	##########################################
-	### parse arguments ######################
+	local ENV_NAME="$SCWRYPTS_ENV"
+	local SEARCH_PATTERNS=()
+
+	while [[ $# -gt 0 ]]
+	do
+		case $1 in
+			-e|--env )
+				[ $ENV_NAME ] && __WARNING 'overwriting session environment'
+				ENV_NAME="$2"
+				__STATUS "using CLI environment '$ENV_NAME'"
+				shift 2
+				;;
+			-- )
+				shift 1
+				break # pass arguments after '--' to the scwrypt
+				;;
+			* )
+				SEARCH_PATTERNS+=$1
+				shift 1
+				;;
+		esac
+	done
+
 	##########################################
 
-	local ENV_NAME
-	[ $SCWRYPTS_ENV ] && ENV_NAME="$SCWRYPTS_ENV" || {
-		[ $1 ] && [ -f $(__GET_ENV_FILE $1) ] && {
-			ENV_NAME="$1"
-			shift 1
-		}
+	local SCRIPT=$(__SELECT_SCRIPT $SEARCH_PATTERNS)
+	[ ! $SCRIPT ] && exit 2
+
+	local ENV_REQUIRED=$(__CHECK_ENV_REQUIRED && echo 1 || echo 0)
+
+	[[ $ENV_REQUIRED -eq 1 ]] && {
+		[ ! $ENV_NAME ] && ENV_NAME=$(__SELECT_ENV)
+		local ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
+
+		[ -f "$ENV_FILE" ] && source "$ENV_FILE" \
+			|| __FAIL 5 "missing or invalid environment '$ENV_NAME'"
+
+		export ENV_NAME
 	}
 
+	[ ! $SUBSCWRYPT ] \
+		&& [[ $ENV_NAME =~ prod ]] \
+		&& { __VALIDATE_UPSTREAM_TIMELINE || __ABORT; }
+
+	local RUN_STRING=$(__GET_RUN_STRING $SCRIPT $ENV_NAME)
+	[ ! $RUN_STRING ] && exit 3
+
+	[ -f $_VIRTUALENV ] && source $_VIRTUALENV
+
+	##########################################
+
+	local LOGFILE=$(__GET_LOGFILE $SCRIPT)
+
+	local HEADER=$(
+		[ $SUBSCWRYPT ] && return 0
+		echo '====================================================================='
+		echo "script : $SCRIPT"
+		echo "run at : $(date)"
+		echo "config : $ENV_NAME"
+		[ ! $LOGFILE ] && echo '\033[1;33m------------------------------------------\033[0m'
+	)
+
+	[ ! $LOGFILE ] && {
+		[ $HEADER ] && echo $HEADER
+		eval $RUN_STRING $@ </dev/tty >/dev/tty 2>&1
+		exit $?
+	}
+
+	{
+		[ $HEADER ] && echo $HEADER
+		echo '\033[1;33m--- BEGIN OUTPUT -------------------------\033[0m'
+		eval $RUN_STRING $@
+		EXIT_CODE=$?
+		echo '\033[1;33m--- END OUTPUT ---------------------------\033[0m'
+
+		[[ $EXIT_CODE -eq 0 ]] && EXIT_COLOR='32m' || EXIT_COLOR='31m'
+
+		echo "terminated with\\033[1;$EXIT_COLOR code $EXIT_CODE\\033[0m"
+	} 2>&1 | tee --append "$LOGFILE"
+
+	exit $(\
+		sed -n 's/^terminated with.*code \([0-9]*\).*$/\1/p' $LOGFILE \
+			| tail -n1
+	)
+}
+
+#####################################################################
+
+__SELECT_SCRIPT() {
 	local SCRIPT
 	local SCRIPTS=$(__GET_AVAILABLE_SCRIPTS)
+	local SEARCH=($@)
 
-	[ $1 ] && {
-		for PATTERN in $*
+	[[ ${#SEARCH[@]} -eq 0 ]] && {
+		SCRIPT=$(echo $SCRIPTS | __FZF 'select a script')
+	}
+
+	[[ ${#SEARCH[@]} -eq 1 ]] && [ -f ./$SEARCH ] && {
+		SCRIPT=$SEARCH
+	}
+
+	[ ! $SCRIPT ] && [[ ${#SEARCH[@]} -gt 0 ]] && {
+		SCRIPT=$SCRIPTS
+		for PATTERN in $SEARCH
 		do
-			shift 1
-			[[ $PATTERN =~ ^--$ ]] && break
-			SCRIPT=$(echo $SCRIPTS | grep $PATTERN)
+			SCRIPT=$(echo $SCRIPT | grep $PATTERN)
 		done
 
 		[ ! $SCRIPT ] && __FAIL 2 "no script found by name '$@'"
@@ -35,108 +120,91 @@ __RUN() {
 			__STATUS "more than one script matched '$@'"
 			SCRIPT=$(echo $SCRIPT | __FZF 'select a script')
 		}
-		true
-	} || SCRIPT=$(echo $SCRIPTS | __FZF 'select a script')
+	}
 
-	[ ! $SCRIPT ] && exit 2
+	echo $SCRIPT
+}
 
-	##########################################
-	### check type and min dependencies ######
-	##########################################
-
-	local ENV_REQUIRED=1
-	local RUN_STRING="./$SCRIPT"
+__GET_RUN_STRING() {
+	local SCRIPT="$1"
+	local ENV_NAME="$2"
 	local TYPE=$(echo $SCRIPT | sed 's/\/.*$//')
 
-	local VIRTUALENV="$SCWRYPTS_ROOT/$TYPE/.env/bin/activate"
-	[ -f $VIRTUALENV ] && source $VIRTUALENV
+	local RUN_STRING
+
+	local _VIRTUALENV="$SCWRYPTS_VIRTUALENV_PATH/$TYPE/bin/activate"
+	[ -f $_VIRTUALENV ] && source $_VIRTUALENV
 
 	case $TYPE in
-		py ) __CHECK_DEPENDENCY python || exit 3
+		py ) __CHECK_DEPENDENCY python || return 1
+			RUN_STRING="python -m $(echo $SCRIPT | sed 's/\//./g; s/\.py$//; s/\.\.//')"
 
-			python --version | grep -q '3.[91]' || {
-				__WARNING 'only tested on python>=3.9'
+			CURRENT_PYTHON_VERSION=$(python --version | sed 's/^[^0-9]*\(3\.[^.]*\).*$/\1/')
+
+			echo $__PREFERRED_PYTHON_VERSIONS | grep -q $CURRENT_PYTHON_VERSION || {
+				__WARNING "only tested on the following python versions: $(printf ', %s.x' ${__PREFERRED_PYTHON_VERSIONS[@]} | sed 's/^, //')"
 				__WARNING 'compatibility may vary'
 			}
-
-			RUN_STRING="python -m $(echo $SCRIPT | sed 's/\//./g; s/\.py$//; s/\.\.//')"
 			;;
 
-		zsh ) __CHECK_DEPENDENCY zsh || exit 3
-			echo $SCRIPT | grep -q 'scwrypts' && ENV_REQUIRED=0
-
+		zsh ) __CHECK_DEPENDENCY zsh || return 1
 			RUN_STRING="./$SCRIPT"
 			;;
 
-		zx ) __CHECK_DEPENDENCY zx || exit 3
-
+		zx ) __CHECK_DEPENDENCY zx || return 1
 			RUN_STRING="FORCE_COLOR=3 ./$SCRIPT.mjs"
 			;;
 
-		* ) __FAIL 4 "unsupported script type '$SCRIPT_TYPE'" ;;
+		* ) __ERROR "unsupported script type '$SCRIPT_TYPE'"
+			return 2
+			;;
 	esac
 
-	##########################################
-	### load scwrypts env and virtualenv #####
-	##########################################
+	RUN_STRING="SCWRYPTS_ENV='$ENV_NAME' $RUN_STRING"
+	[ -f $_VIRTUALENV ] && RUN_STRING="source '$_VIRTUALENV'; $RUN_STRING"
 
-	[[ $ENV_REQUIRED -eq 1 ]] && {
-		[ ! $ENV_NAME ] && ENV_NAME=$(__SELECT_ENV)
-		[ ! $ENV_NAME ] && __ABORT
+	echo $RUN_STRING
+}
 
-		local ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
+__CHECK_ENV_REQUIRED() {
+	[ $CI ] && return 1
 
-		[ -f "$ENV_FILE" ] \
-			&& source "$ENV_FILE" \
-			&& export ENV_NAME \
-			|| __FAIL 5 "missing or invalid environment '$ENV_NAME'"
+	echo $SCRIPT | grep -q 'zsh/scwrypts/logs' && return 1
 
-		[[ $ENV_NAME =~ prod ]] && {
-			__STATUS "on '$ENV_NAME'; checking diff against origin/main"
+	return 0
+}
 
-			git fetch --quiet origin main \
-				&& git diff --exit-code origin/main -- . >&2 \
-				&& __SUCCESS 'up-to-date with main!' \
-				|| {
-					__WARNING
-					__WARNING 'your branch differs from origin/main'
-					__WARNING 'in '$ENV_NAME', being out-of-sync with main may have BAD CONSEQUENCES'
-					__WARNING
-					__yN 'continue?' || __ABORT
-			}
-		}
+__VALIDATE_UPSTREAM_TIMELINE() {
+	__STATUS "on '$ENV_NAME'; checking diff against origin/main"
+
+	git fetch --quiet origin main
+	local SYNC_STATUS=$?
+
+	git diff --exit-code origin/main -- . >&2
+	local DIFF_STATUS=$?
+
+	[[ $SYNC_STATUS -eq 0 ]] && [[ $DIFF_STATUS -eq 0 ]] && {
+		__SUCCESS 'up-to-date with origin/main'
+	} || {
+		__WARNING
+		[[ $SYNC_STATUS -ne 0 ]] && __WARNING 'unable to synchronize with origin/main'
+		[[ $DIFF_STATUS -ne 0 ]] && __WARNING 'your branch differs from origin/main (diff listed above)'
+		__WARNING
+
+		__yN 'continue?' || return 1
 	}
+}
 
-	##########################################
-	### run the scwrypt ######################
-	##########################################
+__GET_LOGFILE() {
+	local SCRIPT="$1"
 
-	local HEADER=$(
-		echo '====================================================================='
-		echo "script : $SCRIPT"
-		echo "run at : $(date)"
-		echo "config : $ENV_NAME"
-		echo '------------------------------------------'
-	)
+	[ $CI ] \
+		|| [ $SUBSCWRYPT ] \
+		|| [[ $SCRIPT =~ scwrypts/logs ]] \
+		|| [[ $SCRIPT =~ interactive ]] \
+		&& return 0
 
-	echo $SCRIPT | grep -q 'interactive' && {
-		echo $HEADER
-		eval $RUN_STRING $@ </dev/tty >/dev/tty 2>&1; exit $?
-	}
-
-	local LOGFILE="$SCWRYPTS_LOG_PATH/$(echo $SCRIPT | sed 's/^\.\///; s/\//\%/g').log"
-	[[ $SCRIPT =~ scwrypts/logs ]] && LOGFILE=/dev/null
-	{
-		echo $HEADER
-		echo '--- BEGIN OUTPUT--------------------------'
-		eval $RUN_STRING $@; local EXIT_CODE="$?"
-		echo '--- END OUTPUT ---------------------------'
-
-		local C
-		[[ $EXIT_CODE -eq 0 ]] && C='32m' || C='31m';
-
-		echo "terminated with\\033[1;$C code $EXIT_CODE\\033[0m"
-	} 2>&1 | tee --append "$LOGFILE"
+	echo "$SCWRYPTS_LOG_PATH/$(echo $SCRIPT | sed 's/^\.\///; s/\//\%/g').log"
 }
 
 #####################################################################
diff --git a/scwrypts.plugin.zsh b/scwrypts.plugin.zsh
index 298db43..af4a44c 100644
--- a/scwrypts.plugin.zsh
+++ b/scwrypts.plugin.zsh
@@ -1,11 +1,6 @@
-source ${0:a:h}/zsh/common.zsh
+DONT_EXIT=1 source ${0:a:h}/zsh/common.zsh
 #####################################################################
-[ ! $SCWRYPTS_SHORTCUT ] && {
-	export SCWRYPTS_SHORTCUT='' # CTRL + SPACE
-}
-
 __SCWRYPTS() {
-
 	local SCRIPT=$(__GET_AVAILABLE_SCRIPTS | __FZF 'select a script')
 	zle clear-command-line
 	[ ! $SCRIPT ] && { zle accept-line; return 0; }
@@ -21,10 +16,6 @@ zle -N scwrypts __SCWRYPTS
 bindkey $SCWRYPTS_SHORTCUT scwrypts
 
 #####################################################################
-[ ! $SCWRYPTS_ENV_SHORTCUT ] && {
-	export SCWRYPTS_ENV_SHORTCUT='' # CTRL + /
-}
-
 __SCWRYPTS_ENV() {
 	local RESET='reset'
 	local SELECTED=$(\
diff --git a/zsh/README.md b/zsh/README.md
index 49a4000..7690c07 100644
--- a/zsh/README.md
+++ b/zsh/README.md
@@ -4,6 +4,7 @@
 [![Generic Badge](https://img.shields.io/badge/junegunn-fzf-informational.svg)](https://github.com/junegunn/fzf)
 [![Generic Badge](https://img.shields.io/badge/mikefarah-yq-informational.svg)](https://github.com/mikefarah/yq)
 [![Generic Badge](https://img.shields.io/badge/stedolan-jq-informational.svg)](https://github.com/stedolan/jq)
+[![Generic Badge](https://img.shields.io/badge/dbcli-pgcli-informational.svg)](https://github.com/dbcli/pgcli)
 <br>
 
 Since they emulate direct user interaction, shell scripts are often the straightforward choice for task automation.
diff --git a/zsh/aws/rds/common.zsh b/zsh/aws/rds/common.zsh
index 98e14ea..f4e44c4 100644
--- a/zsh/aws/rds/common.zsh
+++ b/zsh/aws/rds/common.zsh
@@ -1,6 +1,22 @@
-_DEPENDENCIES+=(
-	psql
-)
+_DEPENDENCIES+=()
 _REQUIRED_ENV+=()
 source ${0:a:h}/../common.zsh
 #####################################################################
+
+__SELECT_CONNECTOR() {
+	local DB_TYPE="$1"
+
+	CLIENTS_postgresql=(pgcli psql)
+
+	local C CLIENT=none
+	for C in $(eval 'echo $CLIENTS_'$DB_TYPE)
+	do
+	__CHECK_DEPENDENCY $C >/dev/null 2>&1 && {
+		CLIENT=$C
+		__STATUS "detected '$CLIENT' for $DB_TYPE"
+		break
+	}
+	done
+
+	echo $CLIENT
+}
diff --git a/zsh/aws/rds/interactive-login b/zsh/aws/rds/interactive-login
new file mode 100755
index 0000000..88bbea0
--- /dev/null
+++ b/zsh/aws/rds/interactive-login
@@ -0,0 +1,121 @@
+#!/bin/zsh
+_DEPENDENCIES+=()
+_REQUIRED_ENV+=()
+source ${0:a:h}/common.zsh
+#####################################################################
+
+__CONNECT_TO_RDS() {
+	local DATABASE=$(__SELECT_DATABASE)
+	[ ! $DATABASE ] && __ABORT
+
+	local DB_HOST DB_USER DB_PORT DB_NAME DB_AUTH DB_TYPE
+
+	DB_HOST=$(echo $DATABASE | jq -r '.host')
+	DB_USER=$(echo $DATABASE | jq -r '.user')
+	DB_PORT=$(echo $DATABASE | jq -r '.port')
+	DB_TYPE=$(echo $DATABASE | jq -r '.type')
+
+	[[ $DB_PORT =~ null ]] && DB_PORT=5432
+	DB_NAME=postgres
+
+	local AUTH_METHODS=(iam secretsmanager user-input)
+	local AUTH_METHOD=$(\
+		echo $AUTH_METHODS | sed 's/\s\+/\n/g' \
+			| __FZF 'select an authentication method' \
+	)
+
+	[ ! $AUTH_METHOD ] && __ABORT
+
+	case $AUTH_METHOD in
+		iam )
+			DB_AUTH=$(\
+				_AWS rds generate-db-auth-token \
+					--hostname $DB_HOST \
+					--port $DB_PORT \
+					--username $DB_USER \
+			)
+			;;
+		secretsmanager )
+			CREDENTIALS=$(__GET_SECRETSMANAGER_CREDENTIALS)
+			echo $CREDENTIALS | jq -e '.pass' >/dev/null 2>&1 \
+				&& DB_AUTH=$(echo $CREDENTIALS | jq -r '.pass')
+
+			echo $CREDENTIALS | jq -e '.password' >/dev/null 2>&1 \
+				&& DB_AUTH=$(echo $CREDENTIALS | jq -r '.password')
+
+			echo $CREDENTIALS | jq -e '.user' >/dev/null 2>&1 \
+				&& DB_USER=$(echo $CREDENTIALS | jq -r '.user')
+
+			echo $CREDENTIALS | jq -e '.username' >/dev/null 2>&1 \
+				&& DB_USER=$(echo $CREDENTIALS | jq -r '.username')
+
+			echo $CREDENTIALS | jq -e '.name' >/dev/null 2>&1 \
+				&& DB_NAME=$(echo $CREDENTIALS | jq -r '.name')
+
+			echo $CREDENTIALS | jq -e '.dbname' >/dev/null 2>&1 \
+				&& DB_NAME=$(echo $CREDENTIALS | jq -r '.dbname')
+			;;
+		user-input )
+			;;
+	esac
+
+	__STATUS
+	__STATUS "host     : $DB_HOST"
+	__STATUS "type     : $DB_TYPE"
+	__STATUS "port     : $DB_PORT"
+	__STATUS "database : $DB_NAME"
+	__STATUS "username : $DB_USER"
+	__STATUS
+
+	__RUN_SCWRYPT 'zsh/db/interactive/postgres' -- \
+		--host $DB_HOST \
+		--port $DB_PORT \
+		--name $DB_NAME \
+		--user $DB_USER \
+		--pass $DB_AUTH \
+		;
+}
+
+__SELECT_DATABASE() {
+	local DATABASES=$(__GET_AVAILABLE_DATABASES)
+	[ ! $DATABASES ] && __FAIL 1 'no databases available'
+
+	local ID=$(\
+		echo $DATABASES | jq -r '.instance + " @ " + .cluster' \
+			| __FZF 'select a database (instance@cluster)' \
+	)
+	[ ! $ID ] && __ABORT
+
+	local INSTANCE=$(echo $ID | sed 's/ @ .*$//')
+	local CLUSTER=$(echo $ID  | sed 's/^.* @ //')
+
+	echo $DATABASES | jq "select (.instance == \"$INSTANCE\" and .cluster == \"$CLUSTER\")"
+}
+
+__GET_AVAILABLE_DATABASES() {
+	_AWS rds describe-db-instances \
+		| jq -r '.[] | .[] | {
+			instance: .DBInstanceIdentifier,
+			cluster:  .DBClusterIdentifier,
+			type:     .Engine,
+			host:     .Endpoint.Address,
+			port:     .Endpoint.Port,
+			user:     .MasterUsername,
+			database: .DBName
+		}'
+}
+
+__GET_SECRETSMANAGER_CREDENTIALS() {
+	local ID=$(\
+		_AWS secretsmanager list-secrets \
+			| jq -r '.[] | .[] | .Name' \
+			| __FZF 'select a secret' \
+	)
+	[ ! $ID ] && return 1
+
+	_AWS secretsmanager get-secret-value --secret-id "$ID" \
+		| jq -r '.SecretString' | jq
+}
+
+#####################################################################
+__CONNECT_TO_RDS
diff --git a/zsh/common.zsh b/zsh/common.zsh
index f1c2b85..9fabd01 100644
--- a/zsh/common.zsh
+++ b/zsh/common.zsh
@@ -2,25 +2,20 @@
 
 [ ! $SCWRYPTS_ROOT ] && SCWRYPTS_ROOT="$(dirname ${0:a:h})"
 
-source $SCWRYPTS_ROOT/.config
-[ -f $SCWRYPTS_CONFIG_PATH/config ] && source $SCWRYPTS_CONFIG_PATH/config
-
-[ ! -d $SCWRYPTS_CONFIG_PATH ] && mkdir -p $SCWRYPTS_CONFIG_PATH
-[ ! -d $SCWRYPTS_ENV_PATH    ] && mkdir -p $SCWRYPTS_ENV_PATH
-[ ! -d $SCWRYPTS_LOG_PATH    ] && mkdir -p $SCWRYPTS_LOG_PATH
-
 __PREFERRED_PYTHON_VERSIONS=(3.10 3.9)
 __NODE_VERSION=18.0.0
+__ENV_TEMPLATE=$SCWRYPTS_ROOT/.env.template
+
+__SCWRYPT=1
+
+source $SCWRYPTS_ROOT/.config
+source ${0:a:h}/utils/utils.module.zsh || {
+	[ $DONT_EXIT ] && return 1 || exit 1
+}
 
 #####################################################################
 
-source ${0:a:h}/utils/utils.zsh
-
-#####################################################################
-
-__ENV_TEMPLATE=$SCWRYPTS_ROOT/.template.env
-
-__GET_ENV_FILES() { find $SCWRYPTS_CONFIG_PATH/env -maxdepth 1 -type f; }
+__GET_ENV_FILES() { find $SCWRYPTS_CONFIG_PATH/env -maxdepth 1 -type f | sort -r }
 [ ! "$(__GET_ENV_FILES)" ] && {
 	cp $__ENV_TEMPLATE "$SCWRYPTS_CONFIG_PATH/env/dev"
 	cp $__ENV_TEMPLATE "$SCWRYPTS_CONFIG_PATH/env/local"
@@ -42,3 +37,22 @@ __GET_AVAILABLE_SCRIPTS() {
 		| sed 's/^\.\///; s/\.[^.]*$//' \
 		;
 }
+
+#####################################################################
+
+__RUN_SCWRYPT() {
+	# run a scwrypt inside a scwrypt w/stack-depth indicators
+	((SUBSCWRYPT+=1))
+	printf ' '; printf '--%.0s' {1..$SUBSCWRYPT}; printf " ($SUBSCWRYPT) "
+	echo "  BEGIN SUBSCWRYPT : $(basename $1)"
+
+	SUBSCWRYPT=$SUBSCWRYPT SCWRYPTS_ENV=$ENV_NAME \
+		"$SCWRYPTS_ROOT/scwrypts" $@
+	EXIT_CODE=$?
+
+	printf ' '; printf '--%.0s' {1..$SUBSCWRYPT}; printf " ($SUBSCWRYPT) "
+	echo "  END SUBSCWRYPT   : $(basename $1)"
+	((SUBSCWRYPT-=1))
+
+	return $EXIT_CODE
+}
diff --git a/zsh/db/common.zsh b/zsh/db/common.zsh
new file mode 100644
index 0000000..1191a72
--- /dev/null
+++ b/zsh/db/common.zsh
@@ -0,0 +1,4 @@
+_DEPENDENCIES+=()
+_REQUIRED_ENV+=()
+source ${0:a:h}/../common.zsh
+#####################################################################
diff --git a/zsh/db/interactive/common.zsh b/zsh/db/interactive/common.zsh
new file mode 100644
index 0000000..1191a72
--- /dev/null
+++ b/zsh/db/interactive/common.zsh
@@ -0,0 +1,4 @@
+_DEPENDENCIES+=()
+_REQUIRED_ENV+=()
+source ${0:a:h}/../common.zsh
+#####################################################################
diff --git a/zsh/db/interactive/postgres b/zsh/db/interactive/postgres
new file mode 100755
index 0000000..2333492
--- /dev/null
+++ b/zsh/db/interactive/postgres
@@ -0,0 +1,45 @@
+#!/bin/zsh
+_DEPENDENCIES+=(
+	pgcli
+)
+_REQUIRED_ENV+=()
+source ${0:a:h}/common.zsh
+#####################################################################
+
+_LOGIN_POSTGRES() {
+	local _HOST _NAME _PASS _PORT _USER
+
+	while [[ $# -gt 0 ]]
+	do
+		case $1 in
+			--host | -h ) _HOST="$2"; shift 2 ;;
+			--name | -d ) _NAME="$2"; shift 2 ;;
+			--pass | -w ) _PASS="$2"; shift 2 ;;
+			--port | -p ) _PORT="$2"; shift 2 ;;
+			--user | -U ) _USER="$2"; shift 2 ;;
+			* ) shift 1 ;;
+		esac
+	done
+
+	[ ! $_HOST ] && _HOST=127.0.0.1
+	[ ! $_NAME ] && _NAME=postgres
+	[ ! $_PORT ] && _PORT=5432
+	[ ! $_USER ] && _USER=postgres
+
+	local DATA_DIR="$SCWRYPTS_DATA_PATH/db/$_HOST"
+	[ ! -d $DATA_DIR ] && mkdir -p $DATA_DIR
+	cd $DATA_DIR
+
+	__STATUS "performing login  : $_USER@$_HOST:$_PORT/$_NAME"
+	__STATUS "working directory : $DATA_DIR"
+
+	PGPASSWORD="$_PASS" pgcli \
+		--host $_HOST \
+		--port $_PORT \
+		--user $_USER \
+		--dbname $_NAME \
+		;
+}
+
+#####################################################################
+_LOGIN_POSTGRES $@
diff --git a/zsh/scwrypts/README.md b/zsh/scwrypts/README.md
index edb947c..0879d88 100644
--- a/zsh/scwrypts/README.md
+++ b/zsh/scwrypts/README.md
@@ -15,12 +15,21 @@ This will immediately open your custom configuration file and reload any necessa
 If you use Scwrypts, **you should use these commands all the time**.
 This is your gateway to managing scwrypts sandboxed environments.
 
-Command       | Description
-------------- | ---------------------------------------------------------------------------------------
-`edit`        | edit an existing environment; synchronizes environments if new variables are added
-`copy`        | copy an existing environment to a new one
-`delete`      | permanently delete an environment by name
-`synchronize` | uses [template](../../.template.env) to add missing and remove extemporaneous variables
+Command           | Description
+----------------- | ---------------------------------------------------------------------------------------
+`edit`            | edit an existing environment
+`copy`            | create and edit a new environment from an existing one
+`delete`          | permanently delete an environment by name
+`stage-variables` | stage missing variables; [helpful for non-ZSH scwrypts](../../py/scwrypts/getenv.py)
+`synchronize`     | uses [template](../../.env.template) to add missing and remove extemporaneous variables
+
+### Environment Inheritance
+You can make a child environment by naming an environment `<parent-name>.<child-name>`.
+Children inherit all parent-set values, and **parent-set values overwrite child-set values**.
+Remember that synchronize runs *every time you edit an environment*, so changes propagate to children immediately.
+Inherited values are denoted by `# inherited from <parent-name>` in the environment file.
+
+Nested children will inherit values from all parents.
 
 ## Logs
 Quickly view or clear Scwrypts logs.
diff --git a/zsh/scwrypts/configure b/zsh/scwrypts/configure
index fcd68f1..af30673 100755
--- a/zsh/scwrypts/configure
+++ b/zsh/scwrypts/configure
@@ -6,12 +6,20 @@ source ${0:a:h}/common.zsh
 
 [ ! -f $SCWRYPTS_CONFIG_PATH/config ] && {
 	__STATUS 'first-time setup detected; creating local configuration override...'
-	cp $SCWRYPTS_ROOT/.config $SCWRYPTS_CONFIG_PATH/config \
+	touch $SCWRYPTS_CONFIG_PATH/config \
 		&& __SUCCESS 'created!' \
 		|| __FAIL 1 "unable to create config at '$SCWRYPTS_CONFIG_PATH/config'"
+	{
+		echo '#'
+		echo '# configuration for scwrypts'
+		echo '#'
+		sed -n '1d; /^###/q; p' $SCWRYPTS_ROOT/.config | sed '$d'
+	} > $SCWRYPTS_CONFIG_PATH/config
+
+	__EDIT $SCWRYPTS_CONFIG_PATH/config
 
 	__STATUS 'attempting to build virtual environments'
-	$SCWRYPTS_ROOT/zsh/scwrypts/virutalenv/update-all \
+	__RUN_SCWRYPT zsh/scwrypts/virtualenv/update-all \
 		&& __SUCCESS 'finished updating virtualenvs' \
 		|| __WARNING 'unable to create one or more virtualenv (see above)' \
 		;
@@ -20,16 +28,12 @@ source ${0:a:h}/common.zsh
 	__REMINDER 'use "zsh/scwrypts/virtualenv/update-all" to update environments'
 	__REMINDER '(equivalent to "npm install" or "pip install -r requirements.txt")'
 	__REMINDER
+} || {
+	__STATUS 'opening local config for editing'
+	__EDIT $SCWRYPTS_CONFIG_PATH/config
+	__STATUS 'finished editing!'
 }
 
-__STATUS 'opening local config for editing'
-__EDIT $SCWRYPTS_CONFIG_PATH/config
-__STATUS 'finished editing!'
-
-which __SCWRYPTS >/dev/null 2>&1 && {
-	__STATUS 'reloading configuration for current session'
-	source $SCWRYPTS_ROOT/zsh/common.zsh \
-		|| __FAIL 2 'unable to reload configuration :c'
-}
 
 __SUCCESS 'saved new configuration'
+__REMINDER 'changes which affect the hot-key plugin will require a ZSHRC reload'
diff --git a/zsh/scwrypts/environment/common.zsh b/zsh/scwrypts/environment/common.zsh
index 1191a72..8cf1c5e 100644
--- a/zsh/scwrypts/environment/common.zsh
+++ b/zsh/scwrypts/environment/common.zsh
@@ -2,3 +2,10 @@ _DEPENDENCIES+=()
 _REQUIRED_ENV+=()
 source ${0:a:h}/../common.zsh
 #####################################################################
+
+_SORT_ENV() {
+	local ENV_FILE="$1"
+
+	sed -i "s/^[A-Z]/export &/; s/^[^#=]\\+$/&=/" "$ENV_FILE"
+	LC_COLLATE=C sort -uo "$ENV_FILE" "$ENV_FILE"
+}
diff --git a/zsh/scwrypts/environment/copy b/zsh/scwrypts/environment/copy
index 7587f5e..2b0fb99 100755
--- a/zsh/scwrypts/environment/copy
+++ b/zsh/scwrypts/environment/copy
@@ -11,7 +11,7 @@ TEMPLATE_ENV_NAME=$(__SELECT_ENV)
 __STATUS "selected '$TEMPLATE_ENV_NAME'"
 
 __PROMPT 'enter new environment name'
-ENV_NAME=$(__FZF_HEAD 'new environment')
+ENV_NAME=$(echo '' | __FZF_HEAD 'new environment')
 [ ! $ENV_NAME ] && __ABORT
 
 TEMPLATE_ENV_FILE=$(__GET_ENV_FILE $TEMPLATE_ENV_NAME)
@@ -19,7 +19,19 @@ ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
 
 [ -f "$ENV_FILE" ] && __FAIL 2 "'$ENV_NAME' already exists"
 
-__STATUS "creating environment"
+__STATUS "creating environment '$ENV_NAME'"
 cp "$TEMPLATE_ENV_FILE" "$ENV_FILE" \
 	&& __SUCCESS "created '$ENV_NAME'" \
 	|| __FAIL 3 "unable to create '$ENV_NAME'"
+
+__STATUS 'stripping inherited values'
+sed -i 's/ # inherited from.*$//' "$ENV_FILE" 2>/dev/null
+
+__RUN_SCWRYPT zsh/scwrypts/environment/synchronize -- --no-prompt \
+	|| __FAIL 4 'failed to run environment sync'
+
+__RUN_SCWRYPT zsh/scwrypts/environment/edit -- $ENV_NAME \
+	|| __FAIL 4 'failed to edit new environment'
+	;
+
+__SUCCESS "finished copy environment '$TEMPLATE_ENV_NAME > $ENV_NAME'"
diff --git a/zsh/scwrypts/environment/edit b/zsh/scwrypts/environment/edit
index 3b2d2af..b711710 100755
--- a/zsh/scwrypts/environment/edit
+++ b/zsh/scwrypts/environment/edit
@@ -4,9 +4,13 @@ _REQUIRED_ENV+=()
 source ${0:a:h}/common.zsh
 #####################################################################
 
-[ $SCWRYPTS_ENV ] \
-	&& ENV_NAME=$SCWRYPTS_ENV \
-	|| ENV_NAME=$(__SELECT_OR_CREATE_ENV)
+[ $1 ] && ENV_NAME="$1"
+
+[ ! $1 ] && {
+	[ $SCWRYPTS_ENV ] \
+		&& ENV_NAME=$SCWRYPTS_ENV \
+		|| ENV_NAME=$(__SELECT_OR_CREATE_ENV)
+}
 [ ! $ENV_NAME ] && __ABORT
 
 ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
@@ -33,18 +37,8 @@ do
 	}
 done < $ENV_FILE
 
-[ $NEW_VAR ] && {
-	LC_COLLATE=C sort -uo $__ENV_TEMPLATE $__ENV_TEMPLATE
-	NOPROMPT=1 ${0:a:h}/synchronize
-	git add $__ENV_TEMPLATE \
-		&& __SUCCESS "auto-staged the $(basename $__ENV_TEMPLATE) changes" \
-		|| {
-			__WARNING "unable to stage $(basename $__ENV_TEMPLATE) changes"
-			__REMINDER "don't forget to commit changes to $(basename $__ENV_TEMPLATE)"
-		}
-	true
-} || {
-	__STATUS 'no new environment variables'
-}
+__RUN_SCWRYPT zsh/scwrypts/environment/synchronize -- --no-prompt \
+	|| __FAIL 4 'failed to run environment sync' \
+	;
 
 __SUCCESS "environment '$ENV_NAME' successfully modified"
diff --git a/zsh/scwrypts/environment/stage-variables b/zsh/scwrypts/environment/stage-variables
index 455eba0..afc38e0 100755
--- a/zsh/scwrypts/environment/stage-variables
+++ b/zsh/scwrypts/environment/stage-variables
@@ -4,4 +4,4 @@ _REQUIRED_ENV+=()
 source ${0:a:h}/common.zsh
 #####################################################################
 
-__CHECK_ENV_VARS $@ || NOPROMPT=1 $SCWRYPTS_ROOT/zsh/scwrypts/environment/synchronize
+__CHECK_REQUIRED_ENV $@
diff --git a/zsh/scwrypts/environment/synchronize b/zsh/scwrypts/environment/synchronize
index e0538d5..3f36ae0 100755
--- a/zsh/scwrypts/environment/synchronize
+++ b/zsh/scwrypts/environment/synchronize
@@ -1,47 +1,130 @@
-#!/bin/zsh               
+#!/bin/zsh
 _DEPENDENCIES+=()
 _REQUIRED_ENV+=()
 source ${0:a:h}/common.zsh
 #####################################################################
 
-[ ! $NOPROMPT ] && {
-	__yN 'change the template before sync?' && __EDIT $__ENV_TEMPLATE
-	sed -i "s/^[A-Z]/export &/; s/^[^#=]\\+$/&=/; s/=.*$/=/" $__ENV_TEMPLATE
-	LC_COLLATE=C sort -uo $__ENV_TEMPLATE $__ENV_TEMPLATE
-	git add $__ENV_TEMPLATE >/dev/null 2>&1
+_SYNCHRONIZE() {
+	while [[ $# -gt 0 ]]
+	do
+		case $1 in
+			--no-prompt ) SLIENT=1; shift 1 ;;
+
+			* ) __WARNING "argument '$1' not recognized"
+				shift 1 ;;
+		esac
+	done
+
+	[ ! $SLIENT ] && {
+		__yN 'change the template before sync?' && __EDIT $__ENV_TEMPLATE
+		_SORT_ENV "$__ENV_TEMPLATE"
+		git add $__ENV_TEMPLATE >/dev/null 2>&1
+	}
+
+	ENVIRONMENTS=$(__GET_ENV_NAMES | sort -r)
+
+	_CLEAR_INHERITED_VARIABLES
+	_INSERT_NEW_VARIABLES
+	_REMOVE_OLD_VARIABLES
+	_SORT_AND_CASCADE
+
+	__SUCCESS 'finished sync!'
 }
 
-ENVIRONMENTS=$(__GET_ENV_FILES)
+#####################################################################
 
-__STATUS 'inserting new environment variables...'
-while read line
-do
-	for ENV_FILE in $(echo $ENVIRONMENTS)
-    do
-        grep -q "^$line" $ENV_FILE || {
-            echo $line >> $ENV_FILE && __STATUS "added '$line' to '$ENV_FILE'"
-        }
-    done
-done < <(sed -n '/^./p' $__ENV_TEMPLATE)
+_CLEAR_INHERITED_VARIABLES() {
+	for ENV_NAME in $(echo $ENVIRONMENTS)
+	do
+		ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
+		sed -i 's/ # inherited from.*//' "$ENV_FILE"
+	done
+}
 
-__STATUS 'removing old environment variables...'
-for ENV_FILE in $(echo $ENVIRONMENTS)
-do
-    while read line
-    do
-        ENV_VAR=$(echo "$line" | sed 's/=.*/=/')
-        grep -q "$ENV_VAR" $__ENV_TEMPLATE || {
-            sed -i "\\%$ENV_VAR%d" $ENV_FILE
-            __WARNING "removed unwanted '$ENV_VAR' from '$ENV_FILE'"
-        }
-    done < $ENV_FILE
-done
+_INSERT_NEW_VARIABLES() {
+	__STATUS 'inserting new environment variables...'
 
-for ENV_FILE in $(echo $ENVIRONMENTS)
-do
-	sed -i "s/^[A-Z]/export &/; s/^[^#=]\\+$/&=/" $ENV_FILE
-	LC_COLLATE=C sort -uo $ENV_FILE $ENV_FILE
-done
-LC_COLLATE=C sort -uo $__ENV_TEMPLATE $__ENV_TEMPLATE
+	local ENV_NAME ENV_FILE line
+	while read line
+	do
+		for ENV_NAME in $(echo $ENVIRONMENTS)
+	    do
+			ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
+	        grep -q "$line" $ENV_FILE || {
+	            echo $line >> $ENV_FILE && __STATUS "added '$line' to '$ENV_NAME'"
+	        }
+	    done
+	done < <(sed -n '/^./p' "$__ENV_TEMPLATE")
+}
 
-__SUCCESS 'finished sync!'
+_REMOVE_OLD_VARIABLES() {
+	__STATUS 'removing old environment variables...'
+
+	local ENV_NAME ENV_FILE line
+	for ENV_NAME in $(echo $ENVIRONMENTS)
+	do
+		ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
+	    while read line
+	    do
+	        ENV_VAR=$(echo "$line" | sed 's/=.*/=/')
+	        grep -q "$ENV_VAR" "$__ENV_TEMPLATE" || {
+	            sed -i "\\%$ENV_VAR%d" "$ENV_FILE"
+	            echo "$ENV_VAR" | grep -qv '^#' \
+					&& __WARNING "removed unwanted '$ENV_VAR' from '$ENV_NAME'"
+	        }
+	    done < $ENV_FILE
+	done
+}
+
+_SORT_AND_CASCADE() {
+	local ENV_NAM ENV_FILE
+
+	for ENV_NAME in $(echo $ENVIRONMENTS)
+	do
+		ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
+		_CASCADE_ENVIRONMENT $ENV_NAME
+	done
+
+	for ENV_NAME in $(echo $ENVIRONMENTS)
+	do
+		ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
+		_SORT_ENV "$ENV_FILE"
+	done
+}
+
+_CASCADE_ENVIRONMENT() {
+	local PARENT_NAME="$1"
+	local PARENT_FILE=$(__GET_ENV_FILE $PARENT_NAME)
+
+	local CHILD_NAMES=$(echo $ENVIRONMENTS | grep "^$PARENT_NAME\\.")
+	[ ! $CHILD_NAMES ] && return 0
+
+	__STATUS "cascading '$PARENT_NAME' to children"
+	for CHILD_NAME in $(echo $CHILD_NAMES)
+	do
+		__SUCCESS "detected child '$CHILD_NAME'"
+	done
+
+	local PARENT_VAR VAR_PATTERN CHILD_NAME CHILD_FILE
+
+	while read PARENT_VAR
+	do
+		VAR_PATTERN=$(echo "$PARENT_VAR" | sed 's/=.*/=/; s/\//\/\//g')
+		__STATUS "propagating '$(echo $VAR_PATTERN | sed 's/^export \([^=]*\)=/\1/')' to children"
+
+		PARENT_VAR+=" # inherited from $PARENT_NAME"
+
+		for CHILD_NAME in $(echo $CHILD_NAMES)
+		do
+			CHILD_FILE=$(__GET_ENV_FILE $CHILD_NAME)
+
+			sed -i "/^$VAR_PATTERN/d" "$CHILD_FILE"
+			echo $PARENT_VAR >> "$CHILD_FILE"
+		done
+	done < <(sed -n '/^[^#][^=]*=[^#]\+$/p' "$PARENT_FILE")
+
+	__SUCCESS "finished '$PARENT_NAME' propagation"
+}
+
+#####################################################################
+_SYNCHRONIZE $@
diff --git a/zsh/scwrypts/logs/view b/zsh/scwrypts/logs/view
index e251a31..a39ffcf 100755
--- a/zsh/scwrypts/logs/view
+++ b/zsh/scwrypts/logs/view
@@ -6,7 +6,7 @@ source ${0:a:h}/common.zsh
 
 cd $SCWRYPTS_ROOT
 __PROMPT 'select a script log'
-LOG_FILE=$(ls $SCWRYPTS_LOG_PATH | __FZF 'logfile')
+LOG_FILE=$(ls -t $SCWRYPTS_LOG_PATH | __FZF 'logfile')
 [ ! $LOG_FILE ] && { __ERROR 'user abort'; exit 1; }
 
 __STATUS 'opening logfile'
diff --git a/zsh/scwrypts/virtualenv/common.zsh b/zsh/scwrypts/virtualenv/common.zsh
index cbb459e..55fdcf4 100644
--- a/zsh/scwrypts/virtualenv/common.zsh
+++ b/zsh/scwrypts/virtualenv/common.zsh
@@ -42,11 +42,11 @@ __UPDATE_VIRTUALENV() {
 		return 1
 	}
 
-	cd $VIRTUALENV_PATH/../
+	cd $SCWRYPTS_ROOT
 	local UPDATE_CODE=0
 	case $TYPE in
-		python ) pip install -r requirements.txt; UPDATE_CODE=$? ;;
-		node   ) npm install ;
+		python ) cd py; pip install -r requirements.txt; UPDATE_CODE=$? ;;
+		node   ) cd zx; npm install ;;
 	esac
 	UPDATE_CODE=$?
 	[[ $UPDATE_CODE -eq 0 ]] \
@@ -78,8 +78,8 @@ __DELETE_VIRTUALENV() {
 __GET_VIRTUALENV_PATH() {
 	local TYPE="$1"
 	case $TYPE in
-		python ) echo "$SCWRYPTS_ROOT/py/.env" ;;
-		node   ) echo "$SCWRYPTS_ROOT/zx/.env" ;;
+		python ) echo "$SCWRYPTS_VIRTUALENV_PATH/py" ;;
+		node   ) echo "$SCWRYPTS_VIRTUALENV_PATH/zx" ;;
 	esac
 }
 
diff --git a/zsh/utils/README.md b/zsh/utils/README.md
new file mode 100644
index 0000000..4c73d64
--- /dev/null
+++ b/zsh/utils/README.md
@@ -0,0 +1,71 @@
+# ZSH Utilities
+
+A shell-scripting utilities module made for ZSH.
+This module is definitely a major component of Scwrypts, but is also standalone and can be sourced by any ZSH script to utilize (almost) all of the features.
+
+## Usage
+Import `utils.module.zsh` to activate all of the features.
+Doing so will *also* check for path dependencies and required environment variables (see [Dependencies](#dependencies) and [Environment](#environment) below).
+
+
+```shell
+#!/bin/zsh
+source ./path/to/utils.plugin.zsh
+__SUCCESS 'ZSH utilities online!'
+```
+
+Checkout [io](./io.zsh) and [os](./os.zsh) for available simple functions.
+
+### Dependencies
+Ensures dependent programs are available for execution.
+Specify a simple name to check the current `PATH`, or give a fully-qualified path for arbitrary dependency inclusion.
+
+Include a dependency by adding to the `_DEPENDENCIES` array.
+*Always using `+=` makes your dependencies extensible to other scripts :)*
+
+If any dependencies are missing, `source utils.module.zsh` will return an error code and count the number of missing dependencies in the variable `DEP_ERROR_COUNT`.
+
+```shell
+#!/bin/zsh
+_DEPENDENCIES+=(
+	path-executable-1
+	path-executable-2
+	/path/to/arbitrary/program
+)
+source ./path/to/utils.plugin.zsh
+echo "missing $DEP_ERROR required dependencies"
+```
+
+### Environment
+Similar to [Dependencies](#dependencies), `environment.zsh` ensures a list of environment variables are *set to non-empty values*.
+
+Include an environment variable by adding to the `_REQUIRED_ENV` array.
+*Something something use `+=` here too ;)*
+
+If any environment variables are missing, `source utils.module.zsh` will return an error code and count the number of missing variables in `ENV_ERROR_COUNT`.
+
+Missing environment variables will be added to the environment template (*exclusive to Scwrypts*).
+
+```shell
+#!/bin/zsh
+_REQUIRED_ENV+=(
+	AWS_PROFILE
+	AWS_REGION
+)
+source ./path/to/utils.plugin.zsh
+echo "missing $ENV_ERROR_COUNT required environment variables"
+```
+
+io.zsh
+os.zsh
+
+## Basic Utilities
+
+One of my biggest pet-peeves with scripting is when every line of a *(insert-language-here)* program is escaped to shell.
+This kind of program, which doesn't use language features, should be a shell script.
+While there are definitely unavoidable limitations to shell scripting, we can minimize a variety of problems with a modern shell and shared utilities library.
+
+Loaded by `common.zsh`, the [`utils/` library](./utils) provides:
+- common function wrappers to unify flags and context
+- lazy dependency and environment variable validation
+- consistent (and pretty) user input / output
diff --git a/zsh/utils/credits.zsh b/zsh/utils/credits.zsh
index 8e1a379..afdc9de 100644
--- a/zsh/utils/credits.zsh
+++ b/zsh/utils/credits.zsh
@@ -1,5 +1,5 @@
 __CREDITS() {
-	# only applicable within scwrypts ("credits" pulled from README files)
+	# scwrypts exclusive ("credits" pulled from README files)
 	[ ! $SCWRYPTS_ROOT ] && return 0
 
 	local COMMAND="$1"
diff --git a/zsh/utils/environment.zsh b/zsh/utils/environment.zsh
index 73f67a5..8a7b0e5 100644
--- a/zsh/utils/environment.zsh
+++ b/zsh/utils/environment.zsh
@@ -1,38 +1,37 @@
 __CHECK_REQUIRED_ENV() {
 	local VAR ERROR=0
-	for VAR in $*; do __CHECK_ENV_VAR $VAR_NAME || ((ERROR+=1)); done
+	for VAR in $*; do __CHECK_ENV_VAR $VAR || ((ERROR+=1)); done
 	return $ERROR
 }
 
 __CHECK_ENV_VAR() {
 	local NAME="$1"
+	[ ! $NAME ] && return 1
+
 	local OPTIONAL="$2"
 	local DEFAULT_VALUE="$3"
 
 	local VALUE=$(eval echo '$'$NAME)
 	[ $VALUE ] && return 0
 
-	local LINE="export $NAME="
-	local TEMPLATE="$SCWRYPTS_ROOT/.template.env"
 
-	grep -q -- "^$LINE" "$TEMPLATE" || {
-		__STATUS 'staging new variable in template'
+	[ $__SCWRYPT ] && {
+		# scwrypts exclusive (missing vars staged in env.template)
+		local LINE="export $NAME="
 
-		echo "$LINE" >> "$TEMPLATE" \
-			&& NOPROMPT=1 $SCWRYPTS_ROOT/zsh/scwrypts/environment/synchronize \
-			&& git add $TEMPLATE >/dev/null 2>&1 \
-			&& __SUCCESS "staged '$NAME'" \
-			|| {
-				__WARNING  "failed to stage '$NAME'"
-				__REMINDER "add/commit '$NAME' to template manually"
-			}
+		grep -q -- "^$LINE" "$__ENV_TEMPLATE" || {
+			__STATUS 'staging new variable in template'
+
+			echo "$LINE" >> "$__ENV_TEMPLATE" \
+				&& __RUN_SCWRYPT zsh/scwrypts/environment/synchronize -- --no-prompt
+		}
 	}
 
 	[ $OPTIONAL ] && {
-		 __ERROR "'$NAME' required"
-		return 1
-	} || {
 		[ $DEFAULT_VALUE ] && $NAME="$DEFAULT_VALUE"
 		return 0
+	} || {
+		 __ERROR "'$NAME' required"
+		return 1
 	}
 }
diff --git a/zsh/utils/io.zsh b/zsh/utils/io.zsh
index f29fe62..a14e69f 100644
--- a/zsh/utils/io.zsh
+++ b/zsh/utils/io.zsh
@@ -3,6 +3,7 @@ __SUCCESS()  { echo "\\033[1;32mSUCCESS  ✔ : $@\\033[0m" >&2; }
 __WARNING()  { echo "\\033[1;33mWARNING   : $@\\033[0m" >&2; }
 __STATUS()   { echo "\\033[1;34mSTATUS     : $@\\033[0m" >&2; }
 __REMINDER() { echo "\\033[1;35mREMINDER  : $@\\033[0m" >&2; }
+
 __PROMPT() {
 	echo   "\\033[1;36mPROMPT    : $@\\033[0m" >&2
 	printf "\\033[1;36mUSER      : \\033[0m" >&2
@@ -10,12 +11,16 @@ __PROMPT() {
 
 __Yn() {
 	__PROMPT "$@ [Yn]"
+	[ $CI ] && { echo y; return 0; }
+
 	local Yn; __READ -k Yn; echo
 	[[ $Yn =~ [nN] ]] && return 1 || return 0
 }
 
 __yN() {
 	__PROMPT "$@ [yN]"
+	[ $CI ] && { echo y; return 0; }
+
 	local yN; __READ -k yN; echo
 	[[ $yN =~ [yY] ]] && return 0 || return 1
 }
@@ -35,10 +40,29 @@ __GETSUDO() {
 
 __LESS() { less -R $@ </dev/tty >/dev/tty; }
 
-__FZF()      { fzf -i --height=30% --layout=reverse --prompt "$@ : "; }
-__FZF_HEAD() { fzf -i --height=30% --layout=reverse --print-query --prompt "$@ : " | head -n1; }
-__FZF_TAIL() { fzf -i --height=30% --layout=reverse --print-query --prompt "$@ : " | tail -n1; }
+__FZF()      {
+	[ $CI ] && {
+		__ERROR 'currently in CI, but __FZF requires user input'
+		exit 1
+	}
 
-__READ()  { read $@ </dev/tty; }
+	fzf -i --height=30% --layout=reverse --prompt "$1 : " ${@:2}
+}
+__FZF_HEAD() { __FZF $@ --print-query | sed '/^$/d' | head -n1; } # prefer user input over selected
+__FZF_TAIL() { __FZF $@ --print-query | sed '/^$/d' | tail -n1; } # prefer selected over user input
 
-__EDIT() { $EDITOR $@ </dev/tty >/dev/tty; }
+__READ()  {
+	[ $CI ] && {
+		__ERROR 'currently in CI, but __READ explicitly requires terminal input'
+		return 1
+	}
+	read $@ </dev/tty
+}
+
+__EDIT() {
+	[ $CI ] && {
+		__ERROR 'currently in CI, but __EDIT explicitly requires terminal input'
+		return 1
+	}
+	$EDITOR $@ </dev/tty >/dev/tty
+}
diff --git a/zsh/utils/utils.zsh b/zsh/utils/utils.module.zsh
similarity index 54%
rename from zsh/utils/utils.zsh
rename to zsh/utils/utils.module.zsh
index 4518b93..eabec20 100644
--- a/zsh/utils/utils.zsh
+++ b/zsh/utils/utils.module.zsh
@@ -13,17 +13,33 @@ source ${0:a:h}/credits.zsh
 
 IMPORT_ERROR=0
 
+[ $CI ] && {
+	export _AWS_PROFILE="$AWS_PROFILE"
+	export _AWS_ACCOUNT="$AWS_ACCOUNT"
+	export _AWS_REGION="$AWS_REGION"
+}
+
 source ${0:a:h}/dependencies.zsh
+_DEP_ERROR=0
 _DEPENDENCIES=($(echo $_DEPENDENCIES | sort -u))
-__CHECK_DEPENDENCIES $_DEPENDENCIES || ((IMPORT_ERROR+=$?))
+__CHECK_DEPENDENCIES $_DEPENDENCIES || _DEP_ERROR=$?
 
 source ${0:a:h}/environment.zsh
-_REQUIRED_ENV=($(echo $__CHECK_REQUIRED_ENV | sort -u))
-__CHECK_REQUIRED_ENV $_REQUIRED_ENV || ((IMPORT_ERROR+=$?))
+_ENV_ERROR=0
+_REQUIRED_ENV=($(echo $_REQUIRED_ENV | sort -u))
+__CHECK_REQUIRED_ENV $_REQUIRED_ENV || _ENV_ERROR=$?
 
-[[ $IMPORT_ERROR -eq 0 ]] || { 
+[[ $_ENV_ERROR -ne 0 ]] && {
+	__REMINDER 'to update missing environment variables, run:'
+	__REMINDER "'scwrypts zsh/scwrypts/environment/edit'"
+}
+
+((IMPORT_ERROR+=$_DEP_ERROR))
+((IMPORT_ERROR+=$_ENV_ERROR))
+
+[[ $IMPORT_ERROR -ne 0 ]] && {
 	__ERROR "encountered $IMPORT_ERROR import error(s)"
-	return 1
 }
 
 #####################################################################
+[[ $IMPORT_ERROR -eq 0 ]]
diff --git a/zx/.gitignore b/zx/.gitignore
index c11a089..c2658d7 100644
--- a/zx/.gitignore
+++ b/zx/.gitignore
@@ -1,2 +1 @@
 node_modules/
-.env/
