#!/bin/bash
# ==============================================================
# HTB Writeup Scaffolder (htb)
# --------------------------------------------------------------
# Author : Kris Yotam (aka. khr1st)
# Contact : krisyotam@protonmail.com
# License : GNU GPLv3
# Date : 2025-09-29
# --------------------------------------------------------------
# Description:
# - Interactive scaffold for HackTheBox writeups.
# - Creates directory under:
# $HOME/Notes/Writeups/HackTheBox (or HTB_WRITEUPS_DIR)
# with subdirs: Machines, Challenges, Starting Point.
# - For Machines/Challenges asks Active|Retired and difficulty
# (Machines: Easy, Medium, Hard, Insane).
# - For Starting Point asks Tier (0, I, II).
# - Produces a writeup file named HTB_<CamelCaseName>
# inside the box directory and prints an ASCII banner.
# - Minimal, functionized, suckless-minded.
# ==============================================================
set -eu
# ============================================================
# CONFIGURATION - Modify these paths and settings as needed
# ============================================================
# Base directory for all HTB writeups
BASE="${HTB_WRITEUPS_DIR:-$HOME/Notes/Writeups/HackTheBox}"
# Script binary name
BIN_NAME="htb"
# Directory structure
CHALLENGES_DIR="Challenges"
MACHINES_DIR="Machines"
STARTING_POINT_DIR="Starting Point"
# Status subdirectories
ACTIVE_SUBDIR="Active"
RETIRED_SUBDIR="Retired"
# Starting Point tiers
TIER_0_DIR="Tier 0"
TIER_I_DIR="Tier I"
TIER_II_DIR="Tier II"
# File naming
WRITEUP_PREFIX="HTB_"
# Difficulty levels
MACHINE_DIFFICULTIES="Very Easy Easy Medium Hard Insane"
CHALLENGE_DIFFICULTIES="Very Easy Easy Medium Hard"
STARTING_POINT_DIFFICULTIES="Very Easy Easy Medium Hard"
# Type options
TYPE_OPTIONS="Machine Challenge Starting Point"
# Status options
STATUS_OPTIONS="Active Retired"
# Tier options
TIER_OPTIONS="0 I II"
# File handling options
OVERWRITE_OPTIONS="Overwrite Append Abort"
# Contact and metadata
AUTHOR_NAME="Kris Yotam (aka. khr1st)"
AUTHOR_EMAIL="krisyotam@protonmail.com"
LICENSE="GNU GPLv3"
SCRIPT_DATE="2025-09-29"
# ============================================================
# ASCII banner printed to terminal (visible TUI header)
ascii_banner() {
cat <<'BANNER'
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⢀⣠⣤⣤⠴⠶⠾⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀⣰⡿⠥⠤⠤⠶⠶⠶⠶⠖⣒⣒⣿⣷⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⣩⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⢈⣉⡛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⡥⠚⠁⠀⠀⠀⠀⠀⠀⠀⠀⠒⠒⠒⠉⠉⠉⠁⠀⠀⠀⠀⢈⣉⡒⠯⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⢀⣀⡠⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⣺⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢋⣠⣤⣶⣿⠝⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠒⠀⠀⠀⠀⠀⠀⠀⢀⡤⠒⠉⢩⠄⠀⠙⢿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⣴⠎⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠑⢻⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⠀⠀⡠⠀⠀⣠⣀⣤⠒⠋⠁⡴⠁⠀⠀⠀⠀⠀⢀⢄⡜⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢯⠻⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠋⠀⢀⠄⡠⠊⢀⡤⠖⠉⠈⢻⡳⡀⢠⠃⠀⠀⠀⠀⢀⣴⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡆⠹⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⢀⡔⣡⣞⣡⣶⣡⣤⣄⠀⠀⠀⠹⣧⡎⠀⠄⡔⠀⡴⡻⠋⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡀⢹
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢁⣔⣿⣾⣿⣿⣿⣿⠟⣛⣿⣧⠐⡄⠀⢿⠀⡘⠞⢠⢎⠞⠁⠀⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⢰⢸⡏⡇⠀
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣱⣾⠟⣽⣫⣍⣾⡿⢁⡼⢋⣌⢽⡇⠸⠀⡼⠀⠉⡰⣳⠋⠀⠀⠀⢠⠀⠀⠀⠀⠠⠀⠀⠀⠀⠀⠀⠀⠀⠈⡜⣼⡇⡇⠀
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠴⠋⠀⣼⢋⣽⡟⠀⠁⢸⠀⠘⠤⢿⡇⠀⠀⡇⠀⢐⡿⠁⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⢓⣿⡇⣱⠀
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⢀⣞⣵⣿⢿⠁⠀⠀⠈⠢⠤⠤⠚⠁⠀⢨⠁⢠⠞⠀⠀⠀⠀⠀⠀⠘⠀⠀⠀⠀⠀⠀⢀⠀⢀⡎⠀⠀⢠⢡⣿⡟⠇⡇⠀
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⢀⡼⠋⢸⡞⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡰⠃⠀⠀⠀⠐⠲⢦⣄⡀⡆⠀⠀⠀⢠⠀⣮⢇⡞⠀⡴⣠⢣⢳⡿⡸⢰⠃⠀
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⠀⠁⠀⠀⣿⠃⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠁⠀⠀⠀⠀⠀⠀⠀⠈⠻⡅⠀⠀⠀⡆⡜⢈⣾⡁⡐⡱⢣⢣⣿⡵⢁⡿⠤⢔
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⣿⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣀⣀⠂⠄⠀⠀⠀⡇⠀⠀⠀⢠⢁⢎⣜⡞⡴⣱⣷⣿⡟⢡⣾⠶⣶⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⡿⣖⣇⠀⠀⢠⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠻⣶⣄⠀⠀⡇⠀⠀⠀⣎⡞⢎⢎⡜⣼⣿⡿⢋⣴⠋⣁⣼⣿⣿
⠟⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠿⡏⠀⠀⠀⠀⠀⠀⠀⣿⣾⣾⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣧⡀⡇⠀⠀⠀⠜⠃⢈⠞⣾⣿⢋⣴⢟⣻⣩⠿⢿⣿⣿
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠁⠀⠀⠀⠀⠀⠀⢀⡼⠛⣿⣇⠀⠀⠀⠀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣇⡇⠀⠀⠀⠀⠠⣋⣾⢟⣥⣿⣋⣴⠛⠃⠀⠀⢹⣿
⠀⠀⠀⠉⠉⠁⠈⠉⠉⠐⠲⠤⣀⠇⠀⡀⣠⠄⠐⠐⠒⠉⠀⠈⠉⠙⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠔⠋⣇⡀⠀⠀⢠⣾⢟⡵⠟⠻⣆⣽⣿⣶⣶⣶⣦⣼⣿
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⢿⢲⠁⠀⠀⠀⠀⠀⠀⠀⠀⢀⢼⢿⣍⣒⢂⡠⠀⠀⠀⢄⣀⠀⠀⠀⣀⣤⠞⢁⡴⠊⠀⠉⠓⠶⣿⡿⢟⣶⣲⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣘⡇⡇⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⣼⣿⠏⠛⠉⠀⠀⠀⠀⠀⠈⢙⢻⣫⠟⢁⠞⠁⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⣍⡀⢀⣿⣿⣿⣿⣿⣏⣹⡟
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣬⣿⣳⠀⠀⠀⠀⠀⠀⢀⡴⣟⠁⡜⠀⠀⠀⠀⠀⠀⠀⠀⠠⣤⡀⠈⣆⢯⣀⣿⡴⠀⣀⡠⠔⠚⠋⠀⠀⠀⠀⠀⠀⠻⣾⣿⣿⣿⣿⣿⣿⡟⠀
⠀⠀⠀⠀⠀⠀⠀⣠⠏⠀⣐⣿⠋⢸⡇⠀⠀⠀⣀⣴⠞⢻⠃⢀⡟⠀⠀⠀⢀⣠⡖⠉⣻⡿⠶⣬⠿⢦⣈⣯⣿⣯⣤⠾⠳⠒⠒⢚⡍⢙⠋⠲⢄⣀⠀⠀⠹⣿⣿⣿⣿⣿⣿⠁⠀
⣀⠀⠀⠀⠀⠀⣰⠇⣠⣽⠞⠁⠀⢯⡇⠤⠒⠛⣃⣀⣀⡜⢠⠞⠀⠀⠀⢰⠋⢸⣦⡉⠁⣀⣼⣧⡀⠀⠸⣽⠀⠀⠀⠀⠀⠀⠰⠟⠁⢘⠀⠀⢰⡌⠀⠀⠀⠙⢿⣛⣛⣛⠃⠀⠀
⢾⣳⡀⠀⠀⠚⢡⢒⠞⠁⠀⠀⠀⣀⣉⣀⣀⣠⠏⠀⣾⡿⠃⠀⠀⠀⠀⠋⠀⠀⢿⣷⣠⣾⣿⢻⣅⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠀⠀⠀⠹⣆⠀⠀⠀⠀⠛⣿⣿⣷⡄⠀
⠀⢹⣷⣀⣠⣶⣏⠤⠒⣲⠾⢫⢽⢳⣖⣶⣿⣿⠀⠐⡟⠁⠀⠀⠀⠀⠀⠀⠀⢀⡈⣿⡟⣽⣿⠃⠀⠉⠲⠤⣄⣀⠀⠀⠀⠀⠀⢠⠏⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀⠀⠘⢿⣿⣿⠀
⠀⠀⣤⣻⠿⠥⡞⡗⢺⣿⣽⣿⣽⡿⣿⣿⣿⣿⣇⠀⢃⠀⠀⠀⠀⣸⠁⠀⠀⠈⠁⠈⠑⠻⠆⠀⠀⠀⠀⠀⠀⠈⠉⠒⠤⠤⠒⠉⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠈⢻⣿⠀
⠀⠀⢦⣟⣻⣾⣭⣭⡽⠶⠶⠶⠶⣤⣦⠖⠿⣯⡿⡄⠘⡆⠀⠀⡼⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠢⡄⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⣀⣀⣤⡝⢆⠀⠀⠀⠀⠈⡿⠀
⠂⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⡾⡭⣗⢣⢹⡀⠘⣄⠞⣡⣤⣤⣤⣴⣶⣶⣿⣽⠿⠉⠉⠁⠒⠒⠤⣧⠤⠴⢲⢶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⢦⡀⠀⢀⠇⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠃⣿⢨⠇⢳⡀⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠓⠀⠀⠀⠀⠀⠈⠁⠀⠀⡞⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡍⠁⡸⠀⢠
⣤⣤⣤⢤⣤⣤⣄⡀⣠⣀⣀⣾⣤⠀⠄⠈⡀⣸⡏⠀⢘⣷⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⠿⡗⠦⣀⣀⡀⠀⠀⠀⠀⠀⡇⢾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣤⠇⠀⣼
HTB Writeup Scaffolder — ${AUTHOR_NAME}
Contact : ${AUTHOR_EMAIL}
License : ${LICENSE}
Date : ${SCRIPT_DATE}
BANNER
}
# Utilities -----------------------------------------------------------------
prompt() {
# prompt <varname> <message>
varname=$1; shift
msg=$*
printf "%s " "$msg"
# POSIX read into a temp var then export to caller via eval
IFS= read -r _answer || _answer=''
eval "$varname=\"\$_answer\""
}
choose_one() {
# choose_one <message> <opt1> <opt2> ...
msg=$1; shift
opts="$@"
while :; do
printf "%s\n" "$msg"
i=1
for opt in $opts; do
printf " %d) %s\n" "$i" "$opt"
i=$((i + 1))
done
printf "Choice [1-%d]: " $((i - 1))
IFS= read -r choice || choice=''
case "$choice" in
''|*[!0-9]*)
printf "Invalid. Enter a number.\n" ;;
*)
if [ "$choice" -ge 1 ] 2>/dev/null && [ "$choice" -lt "$i" ] 2>/dev/null; then
# return the selected option
set -- $opts
shift $((choice - 1))
printf "%s\n" "$1"
return 0
else
printf "Out of range.\n"
fi
;;
esac
done
}
lower_sanitize() {
# convert to lowercase and replace non-alnum with underscore
printf "%s" "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/_/g'
}
camelcase_join() {
# join words into CamelCase, remove non-alnum
# uses awk for portability
printf "%s" "$*" | awk '{
n = split($0, a, /[^[:alnum:]]+/)
for (i=1;i<=n;i++) {
if (a[i] == "") continue
$i = toupper(substr(a[i],1,1)) tolower(substr(a[i],2))
printf "%s", $i
}
}'
}
ensure_dir() {
if [ ! -d "$1" ]; then
mkdir -p "$1" || { printf "Failed to create %s\n" "$1" >&2; exit 1; }
printf "created: %s\n" "$1"
fi
}
# Prepare base layout -------------------------------------------------------
prepare_layout() {
ensure_dir "$BASE"
ensure_dir "$BASE/$CHALLENGES_DIR"
ensure_dir "$BASE/$MACHINES_DIR"
ensure_dir "$BASE/$STARTING_POINT_DIR"
ensure_dir "$BASE/$CHALLENGES_DIR/$ACTIVE_SUBDIR"
ensure_dir "$BASE/$CHALLENGES_DIR/$RETIRED_SUBDIR"
ensure_dir "$BASE/$MACHINES_DIR/$ACTIVE_SUBDIR"
ensure_dir "$BASE/$MACHINES_DIR/$RETIRED_SUBDIR"
ensure_dir "$BASE/$STARTING_POINT_DIR/$TIER_0_DIR"
ensure_dir "$BASE/$STARTING_POINT_DIR/$TIER_I_DIR"
ensure_dir "$BASE/$STARTING_POINT_DIR/$TIER_II_DIR"
}
# Compose writeup header exactly as requested --------------------------------
compose_writeup() {
name_print="$1"
type_print="$2"
status_print="$3"
difficulty_print="$4"
permalink_name="$5"
file_path="$6"
# ensure no blank lines inside the YAML-like front matter
cat > "$file_path" <<EOF
HTB_Name: ---
cover:
description: a write up of the ${name_print}, a ${type_print} on HackTheBox
permalink: htb-${type_print,,}-${permalink_name}
---
#### ${name_print} "Writeup"
---
Type: ${type_print}
Status: ${status_print}
Difficulty: ${difficulty_print}
Topic(s): [[HackTheBox]]
EOF
printf "Wrote: %s\n" "$file_path"
}
# Main ----------------------------------------------------------------------
main() {
ascii_banner
printf "\n"
# box name
prompt BOXNAME "What is the name of the box (letters/numbers/spaces)"
BOXNAME="$(printf "%s" "$BOXNAME" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$BOXNAME" ]; then
printf "Name required. Exiting.\n" >&2
exit 1
fi
DIR_NAME="$(lower_sanitize "$BOXNAME")"
FILE_BASENAME="$(camelcase_join "$BOXNAME")"
WRITEUP_FILENAME="${WRITEUP_PREFIX}${FILE_BASENAME}"
prepare_layout
# Type selection
TYPE="$(choose_one "Is this a Machine, Challenge, or Starting Point?" $TYPE_OPTIONS)"
TIER="N/A"
STATUS="N/A"
DIFFICULTY="N/A"
DEST_DIR=""
if [ "$TYPE" = "Starting Point" ]; then
TIER_CHOICE="$(choose_one "Which tier?" $TIER_OPTIONS)"
DIFFICULTY="$(choose_one "Starting Point difficulty" $STARTING_POINT_DIFFICULTIES)"
case "$TIER_CHOICE" in
0) TIER="$TIER_0_DIR"; DEST_DIR="$BASE/$STARTING_POINT_DIR/$TIER_0_DIR" ;;
I) TIER="$TIER_I_DIR"; DEST_DIR="$BASE/$STARTING_POINT_DIR/$TIER_I_DIR" ;;
II) TIER="$TIER_II_DIR"; DEST_DIR="$BASE/$STARTING_POINT_DIR/$TIER_II_DIR" ;;
esac
else
STATUS="$(choose_one "Is it Active or Retired?" $STATUS_OPTIONS)"
if [ "$TYPE" = "Machine" ]; then
DIFFICULTY="$(choose_one "Machine difficulty" $MACHINE_DIFFICULTIES)"
DEST_DIR="$BASE/$MACHINES_DIR/$STATUS"
else
DIFFICULTY="$(choose_one "Challenge difficulty" $CHALLENGE_DIFFICULTIES)"
DEST_DIR="$BASE/$CHALLENGES_DIR/$STATUS"
fi
fi
TARGET_DIR="${DEST_DIR}/${DIR_NAME}"
ensure_dir "$TARGET_DIR"
WRITEUP_PATH="${TARGET_DIR}/${WRITEUP_FILENAME}"
if [ -e "$WRITEUP_PATH" ]; then
CHOICE="$(choose_one "Writeup exists. Overwrite, Append, Abort?" $OVERWRITE_OPTIONS)"
case "$CHOICE" in
Overwrite) : > "$WRITEUP_PATH" ;;
Append) printf "\n\n" >> "$WRITEUP_PATH" ;;
Abort) printf "Aborted.\n"; exit 0 ;;
esac
else
: > "$WRITEUP_PATH"
fi
# Compose file header
# Convert TYPE to single-word friendly form for permalink: machine/challenge/starting_point
type_perm="$(printf "%s" "$TYPE" | tr '[:upper:]' '[:lower:]' | sed 's/ /_/g')"
# For Starting Point, use TIER as status, otherwise use STATUS
if [ "$TYPE" = "Starting Point" ]; then
compose_writeup "$BOXNAME" "$TYPE" "$TIER" "$DIFFICULTY" "$DIR_NAME" "$WRITEUP_PATH"
else
compose_writeup "$BOXNAME" "$TYPE" "$STATUS" "$DIFFICULTY" "$DIR_NAME" "$WRITEUP_PATH"
fi
printf "\nCreated writeup at: %s\n" "$WRITEUP_PATH"
printf "Open and edit to add notes.\n"
}
main "$@"