diff --git a/README.md b/README.md
index 81663d0..4e8fc92 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,11 @@
 # proj
 
+## TODO
+
+* Actually spawn the shell
+
+* Figure out why the pre-spawn hook is using the `project_dir` value
+
 ## Usage
 
 Invoke the script and pass the name of the project as its only non-flag argument:
@@ -16,7 +22,7 @@ When invoked as specified in usage (with any number of valid flags plus exactly
 
 * Check for the existence of `project_dir`
 
-  * If `project_dir` is a directory or symbolic link, proceed
+  * If `project_dir` is a directory or symbolic link to one, proceed
 
   * If `project_dir` is any other type of file, panic
 
@@ -26,7 +32,7 @@ When invoked as specified in usage (with any number of valid flags plus exactly
 
 * Check for the existence of `$arg1`
 
-  * If `$arg1` is a directory or symbolic link, proceed
+  * If `$arg1` is a directory or symbolic link to one, proceed
 
   * If `$arg1` is any other type of file, panic
 
@@ -36,9 +42,7 @@ When invoked as specified in usage (with any number of valid flags plus exactly
 
     * Create `$arg1` as a directory
 
-    * Initialize an empty git repository if configured to do so
-
-    * Add and commit a `.gitignore` if configured to do so
+    * Execute the post-create hook if configured to do so
 
 * `cd` to `$arg1`
 
@@ -59,9 +63,8 @@ flag|description|args
 
 value|description|default
 ---|---|---
-git\_auto\_init|If not set to `0`, automatically initialize a git repository on project creation|`0`
-git\_auto\_init\_gitignore|A colon-separated ($PATH-style) list of items to include in the initial `.gitignore`|`*.swp`
 hook\_pre\_create|Path to a script invoked (not sourced) before a project is created. Script will be executed relative to `project_dir`|Empty string
+hook\_post\_create|Path to a script invoked (not sourced) after a project is created. Script will be executed relative to the project's root directory|Empty string
 hook\_pre\_spawn|Path to a script invoked (not sourced) before a shell is spawned in the project directory. Script will be executed relative to the project's root directory|Empty string
 hook\_env|Path to a script to *source in* after entering a project directory but before a shell is spawned. Script will be sourced relative to the project's root directory|`.projenv`
 project\_dir|Path to where projects should be stored.|`$HOME/Projects`
@@ -73,3 +76,5 @@ code|description
 0|Success
 2|Invalid flag or missing flag argument
 50|Non-flag arguments have errors or are nonsensical
+61|`project_dir` exists and is not a directory
+62|`$arg1` exists and is not a directory
diff --git a/proj b/proj
index a84042a..21f705a 100755
--- a/proj
+++ b/proj
@@ -12,9 +12,8 @@ set -e
 declare -r _name="$(basename -- "$0")"
 # Options
 declare -a _config=(
-	[git_auto_init]="0"
-	[git_auto_init_gitignore]="*.swp"
 	[hook_pre_create]=""
+	[hook_post_create]=""
 	[hook_pre_spawn]=""
 	[hook_env]=".projenv"
 	[project_dir]="$HOME/Projects"
@@ -94,9 +93,74 @@ Copyright (c) 2021 rehashedsalt@cock.li
 Licensed under the MIT license
 EOF
 }
+hook() {
+	# Execute a hook, if it exists
+	# If it fails for whatever reason, continue anyway silently
+	# $1 path to executable
+	# $2 name of hook
+	[ -z "$1" ] && return 1
+	[ -z "$2" ] && return 2
+	log "Attempting to execute hook \"$2\": $1"
+	if ! [ -e "$1" ]; then
+		warn "Hook \"$2\" does not exist: $1"
+		return
+	fi
+	if ! [ -x "$1" ]; then
+		warn "Hook \"$2\" cannot be executed: $1"
+		return
+	fi
+	if [ -d "$1" ]; then
+		warn "Hook \"$2\" is a directory: $1"
+		return
+	fi
+	"$1" || warn "Hook \"$2\" failed: $?"
+}
 proj() {
-	# Do the do
+	# Create/find a project directory and get in there
+	[ -z "$1" ] && return 1
+	# Get to project_dir
+	local projdir="${_config[project_dir]}"
+	log "Using project_dir: $projdir" 2
+	if ! [ -d "$projdir" ]; then
+		if [ -e "$projdir" ]; then
+			error "Project directory exists as another file type: $projdir" 61
+		fi
+		mkdir -p "$projdir"
+		log "Created project directory: $projdir" 1
+	fi
+
+	log "Entering directory: $projdir" 2
+	cd "$projdir"
+
+	# Get to THE project directory
+	local dir="$1"
+	local dirnew
+	if ! [ -d "$1" ]; then
+		if [ -e "$1" ]; then
+			error "File is in the way of creating a directory: $dir" 62
+		fi
+		# Execute the pre-create hook
+		[ -n "${_config[hook_pre_create]}" ] && hook "${_config[hook_pre_create]}" "pre-create"
+		mkdir -p "$dir"
+		dirnew=1
+	fi
+
+	log "Entering directory: $dir" 2
+	cd "$dir"
+
+	# Execute a post-create hook if necessary
+	if [ -n "$dirnew" ]; then
+		[ -n "${_config[hook_post_create]}" ] && hook "${_config[hook_post_create]}" "post-create"
+	fi
+
+	# Execute the pre-spawn hook if necesary
+	[ -n "${_config[hook_pre_spawn]}" ] && hook "${_config[hook_pre_spawn]}" "pre-spawn"
+
+	# Get in there
+	(
 	:
+	) || :
+	unset dirnew precreatehook postcreatehook
 }
 
 # Main
@@ -135,7 +199,7 @@ main() {
 	[ -n "$_opthelp" ] && printhelp && exit 0
 	# Parse out a config file if it exists
 	if [ -f "$_optconfigfile" ]; then
-		log "Loading config file" 2
+		log "Loading config file: $_optconfigfile" 2
 		while read line; do
 			# If the line has an equals sign and isn't a comment
 			if [ "$line" != "${line#*=}" ] && validateline "$line"; then
@@ -161,7 +225,7 @@ main() {
 	fi
 
 	# Do the do
-	proj
+	proj "${_args[0]}"
 	exit 0
 }