Skip to content

Instantly share code, notes, and snippets.

@stuartcampbell
Forked from mynk322/README.md
Created July 24, 2026 15:04
Show Gist options
  • Select an option

  • Save stuartcampbell/7e0c7b702156bcb5d1d5cd6bf46b5ab7 to your computer and use it in GitHub Desktop.

Select an option

Save stuartcampbell/7e0c7b702156bcb5d1d5cd6bf46b5ab7 to your computer and use it in GitHub Desktop.
zsh_functions

πŸ› οΈ Zsh Terminal Toolkit

13 custom zsh functions that make the macOS terminal dramatically more productive β€” one-step git, instant JSON formatting, a phone-friendly file server, timers, todo lists, and more. No frameworks, no plugin managers, just plain readable shell functions.

Full write-up: [link to your Medium article]

Install (30 seconds)

# 1. Download the functions file
curl -o ~/.zsh_functions <RAW_GIST_URL>

# 2. Source it from your .zshrc
echo '[[ -f "$HOME/.zsh_functions" ]] && source "$HOME/.zsh_functions"' >> ~/.zshrc

# 3. Reload your shell
source ~/.zshrc

Then type myfuncs to see the full cheat sheet. Every command supports -h for detailed usage.

What's inside

Command What it does
myfuncs Colorized cheat sheet of everything below
glit One-step git add + commit + push, plus amend, wip, undo, clean, merge, log
cbjson Format JSON from your clipboard (or a file) β€” never paste prod data into a website again
genpass Cryptographically secure passwords via openssl, straight to clipboard
extract Unzip anything β€” .zip, .tar.gz, .rar, .7z, and more, auto-detected
mkcd mkdir -p + cd in one step
ql macOS Quick Look from the terminal
webshare Serve the current folder over Wi-Fi with a dark-mode UI anyone can open on their phone
port See what's on a port β€” port 3000 kill to free it
myip Local IP, public IP, and geolocation in one shot
pingmap DNS / TCP / TLS / first-byte latency breakdown for any URL
todo Persistent todo list backed by a plain text file
countdown Terminal timer with native macOS notification + sound
keepawake Smart caffeinate wrapper β€” stay awake exactly as long as a command runs
weather wttr.in forecasts without leaving the terminal

Requirements

  • macOS (uses pbcopy, caffeinate, osascript, BSD sed)
  • python3 (ships with macOS) β€” used by cbjson and webshare
  • Optional: jq (brew install jq) for colorized JSON previews
  • Optional: unrar / p7zip (brew install unrar p7zip) for .rar / .7z extraction

Uninstall

Delete ~/.zsh_functions and remove the source line from ~/.zshrc. That's it β€” nothing else is touched except ~/.todo_list.txt if you used todo.

License

MIT β€” copy, modify, share freely. If it saves you time, pay it forward.

# ==============================================================================
# CUSTOM UTILITY FUNCTIONS FOR ZSH (Loaded from ~/.zsh_functions)
# https://gist.github.com/YOUR_USERNAME/YOUR_GIST_ID
#
# Install:
# curl -o ~/.zsh_functions <raw-gist-url>
# echo '[[ -f "$HOME/.zsh_functions" ]] && source "$HOME/.zsh_functions"' >> ~/.zshrc
# source ~/.zshrc
#
# Then run `myfuncs` to see everything, or any command with -h for details.
# Note: written for macOS (uses pbcopy, caffeinate, osascript, BSD sed).
# ==============================================================================
# Global color variables
export C_RED=$'\e[0;31m'
export C_GREEN=$'\e[0;32m'
export C_YELLOW=$'\e[0;33m'
export C_CYAN=$'\e[0;36m'
export C_MAGENTA=$'\e[0;35m'
export C_NC=$'\e[0m'
myfuncs() {
echo "${C_CYAN}====================================================${C_NC}"
echo "${C_MAGENTA} πŸ› οΈ Custom ZSH Utilities Cheat Sheet ${C_NC}"
echo "${C_CYAN}====================================================${C_NC}"
echo "Run any command with ${C_YELLOW}-h${C_NC} or ${C_YELLOW}help${C_NC} for detailed usage."
echo ""
echo "${C_YELLOW}πŸ“‚ File & Directory Ops${C_NC}"
echo " ${C_GREEN}mkcd${C_NC} - Create a directory and enter it immediately"
echo " ${C_GREEN}extract${C_NC} - Unzip/Extract ANY archive file(s) instantly"
echo " ${C_GREEN}ql${C_NC} - Open a file in macOS Quick Look"
echo ""
echo "${C_YELLOW}πŸ’» Developer Tools${C_NC}"
echo " ${C_GREEN}glit${C_NC} - One-step git add, commit, push, and cleanup"
echo " ${C_GREEN}cbjson${C_NC} - Format unreadable JSON in your clipboard or file"
echo " ${C_GREEN}genpass${C_NC} - Generate a secure password & copy to clipboard"
echo ""
echo "${C_YELLOW}🌐 Networking & Web${C_NC}"
echo " ${C_GREEN}webshare${C_NC} - Share current directory over local network HTTP"
echo " ${C_GREEN}port${C_NC} - Check what's running on a port (and optionally kill it)"
echo " ${C_GREEN}myip${C_NC} - Show local & public IP, plus geolocation"
echo " ${C_GREEN}pingmap${C_NC} - Detailed network latency and status breakdown"
echo ""
echo "${C_YELLOW}⏱️ Productivity & System${C_NC}"
echo " ${C_GREEN}todo${C_NC} - Manage a persistent CLI task list"
echo " ${C_GREEN}countdown${C_NC} - Start a timer with UI notifications (supports s/m/h)"
echo " ${C_GREEN}keepawake${C_NC} - Prevent your Mac from sleeping"
echo " ${C_GREEN}weather${C_NC} - Get terminal weather forecast"
echo "${C_CYAN}====================================================${C_NC}"
}
# ==============================================================================
# FILE & DIRECTORY OPS
# ==============================================================================
mkcd() {
if [[ "$1" == "help" || "$1" == "-h" || -z "$1" ]]; then
echo "${C_CYAN}πŸ“ mkcd${C_NC}: Create a directory and enter it in one step."
echo "Usage: ${C_YELLOW}mkcd <directory_name>${C_NC}"
return 0
fi
mkdir -p "$1" && cd "$1" || return
echo "${C_GREEN}πŸ“‚ Directory created. Now in: $(pwd)${C_NC}"
}
extract() {
if [[ "$1" == "help" || "$1" == "-h" || -z "$1" ]]; then
echo "${C_CYAN}πŸ“¦ extract${C_NC}: Universal archive extractor (supports multiple files)."
echo "Usage: ${C_YELLOW}extract <file1> [file2] [file3]${C_NC}"
return 0
fi
for file in "$@"; do
if [ -f "$file" ] ; then
echo "${C_CYAN}Extracting '$file'...${C_NC}"
case "$file" in
*.tar.bz2) tar xjf "$file" ;;
*.tar.gz) tar xzf "$file" ;;
*.bz2) bunzip2 "$file" ;;
*.rar) unrar x "$file" ;;
*.gz) gunzip "$file" ;;
*.tar) tar xf "$file" ;;
*.tbz2) tar xjf "$file" ;;
*.tgz) tar xzf "$file" ;;
*.zip) unzip "$file" ;;
*.Z) uncompress "$file" ;;
*.7z) 7z x "$file" ;;
*) echo "${C_RED}❌ '$file' cannot be extracted via extract().${C_NC}" ;;
esac
else
echo "${C_RED}❌ '$file' is not a valid file.${C_NC}"
fi
done
echo "${C_GREEN}βœ… Extraction complete!${C_NC}"
}
ql() {
if [[ "$1" == "help" || "$1" == "-h" || -z "$1" ]]; then
echo "${C_CYAN}πŸ‘οΈ ql${C_NC}: Open file(s) in macOS Quick Look (like pressing Space in Finder)."
echo "Usage: ${C_YELLOW}ql <file> [file2] ...${C_NC}"
return 0
fi
qlmanage -p "$@" >/dev/null 2>&1 &
}
# ==============================================================================
# DEVELOPER PRODUCTIVITY
# ==============================================================================
cbjson() {
if [[ "$1" == "help" || "$1" == "-h" ]]; then
echo "${C_CYAN}πŸ“‹ cbjson${C_NC}: Formats minified JSON."
echo "Usage:"
echo " ${C_YELLOW}cbjson${C_NC} - Formats JSON currently in your clipboard"
echo " ${C_YELLOW}cbjson <file>${C_NC} - Formats a specific JSON file"
return 0
fi
local input_data
local source_name
if [[ -n "$1" ]]; then
if [[ ! -f "$1" ]]; then
echo "${C_RED}❌ Error: File '$1' not found.${C_NC}"
return 1
fi
input_data=$(cat "$1")
source_name="file '$1'"
else
input_data=$(pbpaste)
source_name="clipboard"
fi
# Format JSON using Python safely
local formatted=$(echo "$input_data" | python3 -c 'import sys, json; print(json.dumps(json.load(sys.stdin), indent=2))' 2>/dev/null)
if [[ -n "$formatted" ]]; then
echo "$formatted" | pbcopy
echo "${C_GREEN}βœ… JSON from $source_name formatted and copied to clipboard!${C_NC}"
echo "${C_CYAN}Preview:${C_NC}"
# If jq is installed, use it for color output in terminal, otherwise just echo
if command -v jq >/dev/null 2>&1; then
echo "$formatted" | jq -C '.' | head -n 15
else
echo "$formatted" | head -n 15
fi
echo "${C_YELLOW}... (truncated) ...${C_NC}"
else
echo "${C_RED}❌ Error: $source_name does not contain valid JSON data.${C_NC}"
fi
}
glit() {
if [[ "$1" == "help" || "$1" == "-h" || -z "$1" ]]; then
echo "${C_CYAN}πŸš€ glit${C_NC}: The lazy way to git."
echo "Usage:"
echo " ${C_YELLOW}glit <message>${C_NC} - Add all, commit, and push (quotes optional)"
echo " ${C_YELLOW}glit amend${C_NC} - Add all, amend last commit, and safely force push"
echo " ${C_YELLOW}glit wip${C_NC} - Add all, commit as 'WIP' with timestamp, and push"
echo " ${C_YELLOW}glit undo${C_NC} - Undo the last local commit (keeps your file changes)"
echo " ${C_YELLOW}glit clean${C_NC} - Delete local branches merged to main/master"
echo " ${C_YELLOW}glit merge <branch>${C_NC} - Fetch and merge a remote branch into your current branch"
echo " ${C_YELLOW}glit status${C_NC} - Show full git status"
echo " ${C_YELLOW}glit log [num]${C_NC} - Show a beautiful, colorized git history tree"
echo ""
echo "${C_YELLOW}Quick Status:${C_NC}"
git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git status -s || echo "Not a git repository."
return 0
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "${C_RED}❌ Error: You are not in a git repository.${C_NC}"
return 1
fi
local branch_name=$(git rev-parse --abbrev-ref HEAD)
case "$1" in
"amend")
echo "${C_CYAN}Adding changes and amending last commit...${C_NC}"
git add .
git commit --amend --no-edit
echo "${C_CYAN}Force pushing to origin $branch_name...${C_NC}"
git push origin "$branch_name" --force-with-lease
echo "${C_GREEN}βœ… Amend & push complete!${C_NC}"
;;
"undo")
echo "${C_YELLOW}Undoing last commit (your files remain changed)...${C_NC}"
git reset HEAD~1
echo "${C_GREEN}βœ… Last commit undone. You can now make changes and commit again.${C_NC}"
;;
"wip")
echo "${C_CYAN}Committing as WIP...${C_NC}"
git add .
git commit -m "WIP: $(date '+%Y-%m-%d %H:%M:%S')"
git push origin "$branch_name"
echo "${C_GREEN}βœ… WIP pushed!${C_NC}"
;;
"clean")
echo "${C_CYAN}Searching for merged local branches...${C_NC}"
local merged_branches=$(git branch --merged | egrep -v "(^\*|master|main|dev)")
if [[ -z "$merged_branches" ]]; then
echo "${C_GREEN}✨ Your local repository is already perfectly clean!${C_NC}"
else
echo "$merged_branches" | xargs git branch -d
echo "${C_GREEN}βœ… Cleaned up merged branches!${C_NC}"
fi
;;
"status")
echo "${C_CYAN}πŸ“Š Current Git Status for branch '${C_YELLOW}$branch_name${C_CYAN}':${C_NC}"
echo "------------------------------------------------------------"
git status
;;
"merge")
local target_branch=$2
if [[ -z "$target_branch" ]]; then
echo "${C_RED}❌ Error: Please specify a branch to merge.${C_NC}"
echo "Usage: ${C_YELLOW}glit merge <branch_name>${C_NC}"
return 1
fi
echo "${C_CYAN}Fetching latest changes for '$target_branch' from origin...${C_NC}"
git fetch origin "$target_branch"
echo "${C_CYAN}Merging origin/$target_branch into $branch_name...${C_NC}"
if git merge "origin/$target_branch"; then
echo "${C_GREEN}βœ… Successfully merged '$target_branch' into '$branch_name'!${C_NC}"
else
echo ""
echo "${C_RED}⚠️ Merge conflicts detected!${C_NC}"
echo "Please resolve the conflicts in your editor, then run ${C_YELLOW}glit \"Merge resolution\"${C_NC} to finish."
fi
;;
"log")
local count=${2:-15}
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit -n "$count"
;;
*)
echo "${C_CYAN}Executing: git add .${C_NC}"
git add .
echo "${C_CYAN}Executing: git commit -m \"$*\"${C_NC}"
git commit -m "$*"
echo "${C_CYAN}Executing: git push origin $branch_name${C_NC}"
git push origin "$branch_name"
echo "${C_GREEN}βœ… All done! Code pushed successfully.${C_NC}"
;;
esac
}
genpass() {
if [[ "$1" == "help" || "$1" == "-h" ]]; then
echo "${C_CYAN}πŸ”‘ genpass${C_NC}: Generate a secure password and copy to clipboard."
echo "Usage: ${C_YELLOW}genpass [length]${C_NC}"
echo "Example: genpass 24"
return 0
fi
local len=${1:-16}
local pass=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9!@#$%^&*' | fold -w "$len" | head -n 1)
echo "$pass" | pbcopy
echo "${C_GREEN}βœ… Password generated and copied to clipboard!${C_NC}"
echo "Length: $len chars"
}
# ==============================================================================
# NETWORKING & WEB
# ==============================================================================
port() {
if [[ "$1" == "help" || "$1" == "-h" || -z "$1" ]]; then
echo "${C_CYAN}πŸ”Œ port${C_NC}: See what's running on a port and optionally kill it."
echo "Usage: ${C_YELLOW}port <number> [kill]${C_NC}"
echo "Examples:"
echo " port 8080 (Views processes on port 8080)"
echo " port 3000 kill (Kills the process running on port 3000)"
return 0
fi
local target_port=$1
if [[ "$2" == "kill" ]]; then
echo "${C_RED}Attempting to kill process on port $target_port...${C_NC}"
local pid=$(lsof -t -i:"$target_port")
if [[ -n "$pid" ]]; then
kill -9 $pid
echo "${C_GREEN}βœ… Process $pid killed successfully.${C_NC}"
else
echo "${C_YELLOW}⚠️ No process found running on port $target_port.${C_NC}"
fi
else
echo "${C_CYAN}Processes listening on port $target_port:${C_NC}"
lsof -i :"$target_port" || echo "${C_YELLOW}No active processes on this port.${C_NC}"
fi
}
webshare() {
if [[ "$1" == "help" || "$1" == "-h" || "$1" == "--help" ]]; then
echo "${C_CYAN}πŸ“ webshare${C_NC}: Instantly serve the current directory over HTTP with a beautiful UI."
echo "Usage: ${C_YELLOW}webshare [port] [-o]${C_NC}"
echo ""
echo "Options:"
echo " ${C_YELLOW}-o, --open${C_NC} Automatically open the server in your default browser"
echo ""
echo "Examples:"
echo " webshare (Serves on port 8000 by default)"
echo " webshare 3000 (Serves on port 3000)"
echo " webshare 8080 -o (Serves on 8080 and opens browser)"
return 0
fi
local port=8000
local open_browser=0
for arg in "$@"; do
case "$arg" in
-o|--open) open_browser=1 ;;
-*) echo "${C_RED}❌ Unknown option: $arg${C_NC}"; return 1 ;;
*)
if [[ "$arg" =~ ^[0-9]+$ ]]; then port=$arg
else echo "${C_RED}❌ Invalid port: $arg${C_NC}"; return 1; fi
;;
esac
done
while lsof -Pi :$port -sTCP:LISTEN -t >/dev/null; do
echo "${C_YELLOW}⚠️ Port $port is in use. Trying $((port+1))...${C_NC}"
port=$((port+1))
done
local local_ip=$(route -n get default 2>/dev/null | grep 'interface:' | awk '{print $2}' | xargs ipconfig getifaddr 2>/dev/null)
[[ -z "$local_ip" ]] && local_ip="127.0.0.1"
# Friendly Bonjour hostname, detected dynamically (e.g. http://my-macbook.local:8000)
local host_name=$(scutil --get LocalHostName 2>/dev/null)
local local_alias=""
[[ -n "$host_name" ]] && local_alias="http://${host_name}.local:$port"
echo "${C_GREEN}βœ… Web server is running!${C_NC}"
echo "${C_CYAN}πŸ“‚ Serving path:${C_NC} $(pwd)"
echo "------------------------------------------------------------"
echo "${C_MAGENTA}πŸ“± How to use & share:${C_NC}"
echo " β€’ ${C_YELLOW}For yourself:${C_NC} Click or open ${C_GREEN}http://localhost:$port${C_NC}"
echo " β€’ ${C_YELLOW}For others:${C_NC} Tell them to go to one of these links on their device:"
echo " ${C_GREEN}http://$local_ip:$port${C_NC} (Always works)"
if [[ -n "$local_alias" ]]; then
echo " ${C_CYAN}$local_alias${C_NC} (Friendly name, works on most devices)"
fi
echo " ${C_YELLOW}(Note: They must be on the same Wi-Fi/network)${C_NC}"
echo "------------------------------------------------------------"
echo "${C_YELLOW}Press Ctrl+C to stop the server${C_NC}"
echo ""
if [[ $open_browser -eq 1 ]]; then
open "http://localhost:$port"
fi
local py_script=$(cat <<'EOF'
import http.server, socketserver, os, sys, urllib.parse, html, io
PORT = int(sys.argv[1])
class BeautifulHandler(http.server.SimpleHTTPRequestHandler):
def list_directory(self, path):
try:
files = os.listdir(path)
except os.error:
self.send_error(404, "No permission to list directory")
return None
files.sort(key=lambda a: a.lower())
displaypath = html.escape(urllib.parse.unquote(self.path))
enc = sys.getfilesystemencoding()
r = []
r.append('<!DOCTYPE html><html><head><meta charset="utf-8">')
r.append('<meta name="viewport" content="width=device-width, initial-scale=1">')
r.append('<title>Shared Files</title>')
r.append('''<style>
body { font-family: system-ui, -apple-system, sans-serif; background: #121212; color: #e0e0e0; margin: 0; padding: 20px; }
.container { max-width: 800px; margin: 0 auto; background: #1e1e1e; border-radius: 12px; padding: 20px; box-shadow: 0 8px 16px rgba(0,0,0,0.5); }
h1 { margin-top: 0; font-size: 1.4rem; border-bottom: 1px solid #333; padding-bottom: 15px; color: #fff; word-break: break-all; }
ul { list-style: none; padding: 0; margin: 0; }
li { border-bottom: 1px solid #2a2a2a; }
li:last-child { border-bottom: none; }
a { display: flex; align-items: center; padding: 12px 10px; color: #bbb; text-decoration: none; font-size: 16px; border-radius: 8px; transition: all 0.2s; word-break: break-word; }
a:hover { background: #2a2a2a; color: #fff; transform: translateX(4px); }
.icon { font-size: 24px; margin-right: 15px; min-width: 30px; text-align: center; }
@media (max-width: 600px) { body { padding: 10px; } .container { padding: 15px; } }
</style>''')
r.append('</head><body><div class="container">')
r.append('<h1>πŸ“‘ Network Share <br><span style="color:#888; font-size:1rem; font-weight:normal;">%s</span></h1>' % displaypath)
r.append('<ul>')
if self.path != "/":
r.append('<li><a href=".."><span class="icon">πŸ”™</span> Parent Directory</a></li>')
for name in files:
if name.startswith('.'):
continue
fullname = os.path.join(path, name)
displayname = linkname = name
icon = "πŸ“„"
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
icon = "πŸ“"
else:
ext = name.lower().split('.')[-1] if '.' in name else ''
if ext in ['mkv', 'mp4', 'avi', 'mov', 'webm']: icon = "🎬"
elif ext in ['jpg', 'jpeg', 'png', 'gif', 'webp']: icon = "πŸ–ΌοΈ"
elif ext in ['mp3', 'wav', 'flac', 'm4a']: icon = "🎡"
elif ext in ['zip', 'rar', '7z', 'tar', 'gz']: icon = "πŸ“¦"
elif ext in ['pdf']: icon = "πŸ“•"
elif ext in ['txt', 'md', 'csv', 'json']: icon = "πŸ“"
elif ext in ['py', 'js', 'html', 'css', 'java', 'cpp', 'sh']: icon = "πŸ’»"
r.append('<li><a href="%s"><span class="icon">%s</span> %s</a></li>'
% (urllib.parse.quote(linkname, errors='surrogatepass'), icon, html.escape(displayname)))
r.append('</ul></div></body></html>')
encoded = ''.join(r).encode(enc, 'surrogateescape')
f = io.BytesIO()
f.write(encoded)
f.seek(0)
self.send_response(200)
self.send_header("Content-type", "text/html; charset=%s" % enc)
self.send_header("Content-Length", str(len(encoded)))
self.end_headers()
return f
class QuietServer(socketserver.TCPServer):
allow_reuse_address = True
def handle_error(self, request, client_address):
import sys
if issubclass(sys.exc_info()[0], (BrokenPipeError, ConnectionResetError)):
return
super().handle_error(request, client_address)
with QuietServer(("", PORT), BeautifulHandler) as httpd:
httpd.serve_forever()
EOF
)
python3 -c "$py_script" "$port"
}
myip() {
if [[ "$1" == "help" || "$1" == "-h" ]]; then
echo "${C_CYAN}🌐 myip${C_NC}: Fetches your networking info."
echo "Usage: ${C_YELLOW}myip${C_NC}"
return 0
fi
echo "${C_CYAN}Fetching IP details...${C_NC}"
local local_ip=$(route -n get default 2>/dev/null | grep 'interface:' | awk '{print $2}' | xargs ipconfig getifaddr 2>/dev/null)
[[ -z "$local_ip" ]] && local_ip="127.0.0.1"
local public_ip=$(curl -s https://ifconfig.me)
local geo_info=$(curl -s "https://ipinfo.io/${public_ip}/city")
local country_info=$(curl -s "https://ipinfo.io/${public_ip}/country")
echo "🏠 ${C_YELLOW}Local IP:${C_NC} $local_ip"
echo "🌍 ${C_YELLOW}Public IP:${C_NC} $public_ip"
echo "πŸ“ ${C_YELLOW}Location:${C_NC} $geo_info, $country_info"
}
pingmap() {
if [[ "$1" == "help" || "$1" == "-h" || -z "$1" ]]; then
echo "${C_CYAN}πŸ“ pingmap${C_NC}: Detailed connection latency breakdown."
echo "Usage: ${C_YELLOW}pingmap <url>${C_NC}"
echo "Example: pingmap google.com"
return 0
fi
local url="$1"
# Auto-prepend https:// if missing
[[ "$url" != http* ]] && url="https://$url"
echo "${C_CYAN}Mapping network route to: $url${C_NC}"
echo "----------------------------------------"
curl -w " HTTP Status : %{http_code}\n DNS Lookup : %{time_namelookup}s\n TCP Connect : %{time_connect}s\n TLS Handshake : %{time_appconnect}s\n Pre-Transfer : %{time_pretransfer}s\n First Byte : %{time_starttransfer}s\n----------------------------------------\n ${C_GREEN}Total Time : %{time_total}s${C_NC}\n\n" -o /dev/null -s "$url"
}
# ==============================================================================
# PRODUCTIVITY & SYSTEM
# ==============================================================================
todo() {
local todo_file="$HOME/.todo_list.txt"
touch "$todo_file"
check_bounds() {
local target=$1
local max_line
max_line=$(wc -l < "$todo_file" | awk '{print $1}')
if (( target > max_line || target < 1 )); then
echo "${C_RED}❌ Task number $target not found.${C_NC}"
return 1
fi
return 0
}
case "$1" in
"help"|"-h"|"--help")
echo "${C_CYAN}πŸ’‘ CLI Todo List β€” Help Menu${C_NC}"
echo "Usage: todo [command] [arguments]"
echo ""
echo " ${C_YELLOW}todo <text>${C_NC} Add a task"
echo " ${C_YELLOW}todo list${C_NC} Display tasks"
echo " ${C_YELLOW}todo done <num>${C_NC} Mark task completed"
echo " ${C_YELLOW}todo undo <num>${C_NC} Revert task to pending"
echo " ${C_YELLOW}todo edit <num> <txt>${C_NC} Update task"
echo " ${C_YELLOW}todo rm <num>${C_NC} Delete task"
echo " ${C_YELLOW}todo clear done${C_NC} Remove completed tasks"
echo " ${C_YELLOW}todo clear${C_NC} Wipe entire list"
;;
"list"|"")
if [ ! -s "$todo_file" ]; then
echo "${C_GREEN}πŸŽ‰ No tasks! Live free.${C_NC}"
else
echo "${C_CYAN}πŸ“ Your Todo List:${C_NC}"
local count=1
while IFS= read -r line || [ -n "$line" ]; do
local stripped="${line//[[:space:]]/}"
if [[ -z "$stripped" || "$stripped" == "+children"* || "$stripped" == "--format"* || "$stripped" == "display="* || "$stripped" == "-[ ]-f" ]]; then
continue
fi
if [[ "$line" == *"[ ]"* ]]; then
line="${line//"[ ]"/${C_YELLOW}[ ]${C_NC}}"
elif [[ "$line" == *"[x]"* ]]; then
line="${line//"[x]"/${C_GREEN}[x]${C_NC}}"
fi
printf "%2d. %s\n" "$count" "$line"
((count++))
done < "$todo_file"
if [ "$count" -eq 1 ]; then
echo "${C_GREEN}πŸŽ‰ No tasks! Live free.${C_NC}"
fi
fi
;;
"done")
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
check_bounds "$2" || return 1
sed -i "" "${2}s/\[ \]/\[x\]/" "$todo_file"
echo "${C_GREEN}βœ… Task $2 marked as done!${C_NC}"
else
echo "${C_YELLOW}⚠️ Usage: todo done <task_number>${C_NC}"
fi
;;
"undo")
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
check_bounds "$2" || return 1
sed -i "" "${2}s/\[x\]/\[ \]/" "$todo_file"
echo "${C_YELLOW}βͺ Task $2 marked as pending!${C_NC}"
else
echo "${C_YELLOW}⚠️ Usage: todo undo <task_number>${C_NC}"
fi
;;
"rm")
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
check_bounds "$2" || return 1
sed -i "" "${2}d" "$todo_file"
echo "${C_RED}πŸ—‘οΈ Task $2 deleted!${C_NC}"
else
echo "${C_YELLOW}⚠️ Usage: todo rm <task_number>${C_NC}"
fi
;;
"edit")
if [[ -n "$2" && "$2" =~ ^[0-9]+$ && -n "$3" ]]; then
local line_num=$2
check_bounds "$line_num" || return 1
shift 2
sed -i "" "${line_num}s|.*|- [ ] $*|" "$todo_file"
echo "${C_CYAN}✏️ Task $line_num updated!${C_NC}"
else
echo "${C_YELLOW}⚠️ Usage: todo edit <task_number> <new text>${C_NC}"
fi
;;
"clear")
if [ "$2" = "done" ]; then
grep -v '\[x\]' "$todo_file" > "$todo_file.tmp"
mv "$todo_file.tmp" "$todo_file"
echo "${C_GREEN}🧹 Cleared completed tasks!${C_NC}"
elif [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
echo "${C_YELLOW}⚠️ Did you mean 'todo rm $2'?${C_NC}"
elif [ -n "$2" ]; then
echo "${C_YELLOW}⚠️ Unknown argument. Use 'todo clear done' or 'todo clear'.${C_NC}"
else
echo -n "${C_RED}⚠️ Wipe ENTIRE todo list? (y/N): ${C_NC}"
read -r response
if [[ "$response" == [Yy]* ]]; then
rm -f "$todo_file" && touch "$todo_file"
echo "${C_RED}πŸ’₯ Todo list wiped completely clean!${C_NC}"
else
echo "${C_CYAN}❌ Action aborted.${C_NC}"
fi
fi
;;
-*)
return 0
;;
*)
echo "- [ ] $*" >> "$todo_file"
echo "${C_GREEN}βž• Task added!${C_NC}"
;;
esac
}
countdown() {
if [[ "$1" == "help" || "$1" == "-h" ]]; then
echo "${C_CYAN}⏳ countdown${C_NC}: A simple terminal timer with notifications."
echo "Usage: ${C_YELLOW}countdown <time>[s|m|h]${C_NC}"
echo "Examples: countdown 45, countdown 5m, countdown 1h"
return 0
fi
local input=${1:-60}
local seconds=0
if [[ $input == *m ]]; then seconds=$((${input%m} * 60))
elif [[ $input == *h ]]; then seconds=$((${input%h} * 3600))
elif [[ $input == *s ]]; then seconds=${input%s}
else seconds=$input; fi
if ! [[ "$seconds" =~ ^[0-9]+$ ]]; then
echo "${C_RED}❌ Invalid time format.${C_NC} Use numbers, optionally followed by m or h."
return 1
fi
echo "${C_CYAN}Starting timer for $input...${C_NC}"
while [ "$seconds" -gt 0 ]; do
printf "\r${C_YELLOW}⏳ Time remaining: %02d:%02d${C_NC}\033[0K" $((seconds/60)) $((seconds%60))
sleep 1
: $((seconds--))
done
printf "\r\033[0K"
echo "${C_GREEN}πŸ”” Time's up! ($input)${C_NC}"
if [[ "$OSTYPE" == "darwin"* ]]; then
osascript -e "display notification \"Your $input timer has finished!\" with title \"⏳ Countdown\" sound name \"Glass\""
say "Timer is done" >/dev/null 2>&1 &!
else
echo -e "\a"
fi
}
keepawake() {
if [[ "$1" == "help" || "$1" == "-h" ]]; then
echo "${C_CYAN}β˜• keepawake${C_NC}: Prevent your Mac from sleeping."
echo "Usage: ${C_YELLOW}keepawake [time | -p PID | -c command | stop]${C_NC}"
echo ""
echo "Options:"
echo " ${C_YELLOW}[time]${C_NC} Keep awake for duration (e.g., 2h, 30m) and notify when done"
echo " ${C_YELLOW}-p <PID>${C_NC} Keep awake until a Process ID finishes, then notify"
echo " ${C_YELLOW}-c <cmd>${C_NC} Run a command, keep awake until it completes, then notify"
echo " ${C_YELLOW}-s${C_NC} System-only mode: Keep tasks running, but let the screen turn off"
echo " ${C_YELLOW}stop${C_NC} Kill any existing keepawake sessions running in the background"
echo " ${C_YELLOW}(none)${C_NC} Keep awake indefinitely (Ctrl+C to stop)"
echo ""
echo "Examples:"
echo " keepawake -c npm run build"
echo " keepawake -s 2h"
return 0
fi
# Helper function for macOS notifications
notify_done() {
local msg="$1"
if [[ "$OSTYPE" == "darwin"* ]]; then
osascript -e "display notification \"$msg\" with title \"β˜• Keepawake\" sound name \"Glass\""
say "Task finished" >/dev/null 2>&1 &!
else
echo -e "\a"
fi
}
# The Kill Switch
if [[ "$1" == "stop" ]]; then
killall caffeinate 2>/dev/null
echo "${C_GREEN}πŸ›‘ Stopped all active keepawake sessions. Mac can sleep normally.${C_NC}"
return 0
fi
# Determine caffeinate flags (default includes -d for display, -s drops -d)
local flags="-dism"
if [[ "$1" == "-s" || "$1" == "--system" ]]; then
flags="-ism"
shift
echo "${C_YELLOW}πŸŒ™ System-only mode: Display will sleep, but background processes will run.${C_NC}"
fi
if [[ -z "$1" ]]; then
echo "${C_GREEN}β˜• Mac is now caffeinated. Press Ctrl+C to let it sleep.${C_NC}"
caffeinate $flags
echo "${C_CYAN}πŸ›Œ Mac can sleep normally now.${C_NC}"
return 0
fi
case "$1" in
-p|--pid)
local pid=$2
if [[ -z "$pid" || ! "$pid" =~ ^[0-9]+$ ]]; then
echo "${C_RED}❌ Error: Please provide a valid Process ID.${C_NC}"
return 1
fi
echo "${C_GREEN}β˜• Mac will stay awake until process $pid finishes...${C_NC}"
caffeinate $flags -w "$pid"
echo "${C_CYAN}πŸ›Œ Process $pid finished. Mac can sleep normally now.${C_NC}"
notify_done "Process $pid has finished!"
;;
-c|--command)
shift
if [[ -z "$*" ]]; then
echo "${C_RED}❌ Error: Please provide a command to run.${C_NC}"
return 1
fi
echo "${C_GREEN}β˜• Mac will stay awake while running: ${C_YELLOW}$*${C_NC}"
echo "------------------------------------------------------------"
caffeinate $flags "$@"
echo "------------------------------------------------------------"
echo "${C_CYAN}πŸ›Œ Command finished. Mac can sleep normally now.${C_NC}"
notify_done "Your command has finished running!"
;;
*)
local input=$1
local seconds=0
if [[ $input == *m ]]; then seconds=$((${input%m} * 60))
elif [[ $input == *h ]]; then seconds=$((${input%h} * 3600))
elif [[ $input == *s ]]; then seconds=${input%s}
else seconds=$input; fi
if ! [[ "$seconds" =~ ^[0-9]+$ ]]; then
echo "${C_RED}❌ Invalid format.${C_NC} Use a time (30m), -p <PID>, or -c <cmd>."
return 1
fi
echo "${C_GREEN}β˜• Mac will stay awake for $input. Press Ctrl+C to cancel.${C_NC}"
caffeinate $flags -t "$seconds"
echo "${C_CYAN}πŸ›Œ Timer finished. Mac can sleep normally now.${C_NC}"
notify_done "Your $input keepawake timer has finished!"
;;
esac
}
weather() {
if [[ "$1" == "help" || "$1" == "-h" ]]; then
echo "${C_CYAN}☁️ weather${C_NC}: Advanced terminal weather and forecast."
echo "Usage: ${C_YELLOW}weather [options] [location]${C_NC}"
echo ""
echo "Options:"
echo " ${C_YELLOW}-s, --short${C_NC} Single-line compact format (Temp & Condition)"
echo " ${C_YELLOW}-o, --oneline${C_NC} Rich single-line format (Temp, Wind, Humidity)"
echo " ${C_YELLOW}-f, --forecast${C_NC} Detailed visual graph forecast"
echo " ${C_YELLOW}-m, --moon${C_NC} Show current moon phase"
echo ""
echo "Examples:"
echo " weather London (Default 3-day text forecast)"
echo " weather -o New York (One-line rich weather)"
echo " weather -f (Detailed forecast for your current IP)"
return 0
fi
local format=""
local args=()
for arg in "$@"; do
case "$arg" in
-s|--short) format="?format=3" ;;
-o|--oneline) format="?format=4" ;;
-f|--forecast) format="?format=v2" ;;
-m|--moon) format="?Moon" ;;
-*)
echo "${C_RED}❌ Unknown option: $arg${C_NC}"
echo "Run ${C_YELLOW}weather -h${C_NC} for usage."
return 1
;;
*) args+=("$arg") ;;
esac
done
local loc="${args[*]}"
loc="${loc// /+}"
echo "${C_CYAN}Fetching weather data...${C_NC}"
curl -s "https://wttr.in/${loc}${format}"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment