Vibe coding a shell script
I recently converted a codebase from yarn to pnpm. Several times now I've ran yarn install
only to see No lockfile found.
and then I have to kill the process and run pnpm i
. I was curious what it'd take to write a shell script to catch this sort of thing, so I turned to my good friend Claude.
Here's what we came up with, with support for npm
, yarn
, pnpm
, bun
, and deno
:
#!/usr/bin/env bash
detect_package_manager() {
if [ -f "pnpm-lock.yaml" ]; then
echo "pnpm"
elif [ -f "yarn.lock" ]; then
echo "yarn"
elif [ -f "package-lock.json" ] || [ -f "npm-shrinkwrap.json" ]; then
echo "npm"
elif [ -f "bun.lockb" ]; then
echo "bun"
elif [ -f "deno.lock" ] || [ -f "deno.json" ] || [ -f "deno.jsonc" ]; then
echo "deno"
else
echo ""
fi
}
handle_package_manager() {
local command_name="$1"
shift
local detected_package_manager
local args=("$@")
detected_package_manager=$(detect_package_manager)
if [ -n "$detected_package_manager" ] && [ "$detected_package_manager" != "$command_name" ]; then
echo "⚠️ Warning: This project uses $detected_package_manager, not $command_name."
echo -n "Proceed? [yN] "
read -r REPLY
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Command aborted. Try using $detected_package_manager instead."
return 1
fi
fi
command "$command_name" "${args[@]}"
return $?
}
# Create wrapper functions for package managers
npm() {
handle_package_manager "npm" "$@"
}
yarn() {
handle_package_manager "yarn" "$@"
}
pnpm() {
handle_package_manager "pnpm" "$@"
}
bun() {
handle_package_manager "bun" "$@"
}
deno() {
handle_package_manager "deno" "$@"
}
Here's what it looks like in practice:
➜ yarn
⚠️ Warning: This project uses pnpm, not yarn.
Proceed? [yN]
Command aborted. Try using pnpm instead.
I also came up with an installer script, which detects your shell environment (bash
and zsh
are supported, not fish
) and installs the script to ~/.shell-scripts
.
Click to download, or to copy and paste the code below.
#!/usr/bin/env bash
# Create a directory for your custom scripts if you don't have one
mkdir -p ~/.shell-scripts
# Create the package manager guard script
cat > ~/.shell-scripts/pm-guard.sh << 'EOF'
#!/usr/bin/env bash
# Function to detect project's package manager
detect_package_manager() {
if [ -f "pnpm-lock.yaml" ]; then
echo "pnpm"
elif [ -f "yarn.lock" ]; then
echo "yarn"
elif [ -f "package-lock.json" ] || [ -f "npm-shrinkwrap.json" ]; then
echo "npm"
elif [ -f "bun.lockb" ]; then
echo "bun"
elif [ -f "deno.lock" ] || [ -f "deno.json" ] || [ -f "deno.jsonc" ]; then
echo "deno"
else
echo ""
fi
}
# Helper function to handle package manager mismatch checks
# Arguments:
# $1: package manager command name
# $@: all original arguments
handle_package_manager() {
local command_name="$1"
shift
local detected_package_manager
local args=("$@")
detected_package_manager=$(detect_package_manager)
if [ -n "$detected_package_manager" ] && [ "$detected_package_manager" != "$command_name" ]; then
echo "⚠️ Warning: This project uses $detected_package_manager, not $command_name."
echo -n "Proceed? [yN] "
read REPLY
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Command aborted. Try using $detected_package_manager instead."
return 1
fi
fi
command "$command_name" "${args[@]}"
return $?
}
# Create wrapper functions for package managers
npm() {
handle_package_manager "npm" "$@"
}
yarn() {
handle_package_manager "yarn" "$@"
}
pnpm() {
handle_package_manager "pnpm" "$@"
}
bun() {
handle_package_manager "bun" "$@"
}
deno() {
handle_package_manager "deno" "$@"
}
EOF
# Make the script executable
chmod +x ~/.shell-scripts/pm-guard.sh
# Function to add source line to shell config
add_to_shell_config() {
local config_file="$1"
local source_line="source ~/.shell-scripts/pm-guard.sh"
# Add the line with a comment
echo "" >> "$config_file"
echo "# Source package manager guard" >> "$config_file"
echo "$source_line" >> "$config_file"
echo "Added PM Guard to $config_file"
return 0
}
# Determine which shell config file to use
if [ -f "$HOME/.zshrc" ]; then
add_to_shell_config "$HOME/.zshrc"
elif [ -f "$HOME/.bashrc" ]; then
add_to_shell_config "$HOME/.bashrc"
elif [ -f "$HOME/.bash_profile" ]; then
add_to_shell_config "$HOME/.bash_profile"
else
echo "Could not find shell config file. Please add the following line to your shell config:"
echo "source ~/.shell-scripts/pm-guard.sh"
exit 1
fi
echo "Setup complete! Reload your shell or run 'source ~/.shell-scripts/pm-guard.sh' to activate."
echo ""
echo "Usage:"
echo " - Normal usage: npm, yarn, pnpm, bun, deno (will check for mismatches)"
I'm blown away by how quickly I can go from idea to production script with LLM tools like Claude and V0. I was able to explore the solution space, suss out the scope, and iterate on implementation all in a few cycles of prompting.