Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
#!/bin/bash# lib/menu.sh - Menu system utilities# Source after colors.sh# Display menu and get selectionshow_menu() {
local title="$1"shiftlocal options=("$@")
print_header "$title"local i=1
for option in"${options[@]}"; doif [[ "$option" == "---" ]]; then
print_separator 40
elseprintf" ${CYAN}%2d)${NC} %s\n""$i""$option"
((i++))
fidoneecho""printf" ${CYAN} 0)${NC}${DIM}Exit / Back${NC}\n"echo""read -p "$(echo -e ${BOLD}Select option: ${NC})" choice
echo"$choice"
}
# Confirm actionconfirm() {
local message="${1:-Are you sure?}"local default="${2:-n}"if [[ "$default" == "y" ]]; thenread -p "$(echo -e ${YELLOW}$message [Y/n]: ${NC})" response
response=${response:-y}elseread -p "$(echo -e ${YELLOW}$message [y/N]: ${NC})" response
response=${response:-n}fi
[[ "$response" =~ ^[Yy]$ ]]
}
# Prompt for inputprompt_input() {
local message="$1"local default="${2:-}"local result
if [[ -n "$default" ]]; thenread -p "$(echo -e ${CYAN}$message [$default]: ${NC})" result
result=${result:-$default}elseread -p "$(echo -e ${CYAN}$message: ${NC})" result
fiecho"$result"
}
# Select from listselect_from_list() {
local title="$1"shiftlocal items=("$@")
echo -e "\n${BOLD}$title${NC}\n"local i=1
for item in"${items[@]}"; doprintf" ${CYAN}%2d)${NC} %s\n""$i""$item"
((i++))
doneecho""read -p "$(echo -e ${BOLD}Select [1-${#items[@]}]: ${NC})" choice
if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#items[@]} ]]; thenecho"${items[$((choice-1))]}"elseecho""fi
}
# Multi-select from listmulti_select() {
local title="$1"shiftlocal items=("$@")
local selected=()
echo -e "\n${BOLD}$title${NC}"echo -e "${DIM}(Enter numbers separated by spaces, or 'all')${NC}\n"local i=1
for item in"${items[@]}"; doprintf" ${CYAN}%2d)${NC} %s\n""$i""$item"
((i++))
doneecho""read -p "$(echo -e ${BOLD}Select: ${NC})" choices
if [[ "$choices" == "all" ]]; then
selected=("${items[@]}")
elsefor choice in$choices; doif [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#items[@]} ]]; then
selected+=("${items[$((choice-1))]}")
fidonefiprintf'%s\n'"${selected[@]}"
}
# Wait for keypresswait_for_key() {
local message="${1:-Press any key to continue...}"echo -e "\n${DIM}$message${NC}"read -n 1 -s
}
# Clear screen with headerclear_with_header() {
local title="${1:-Menu}"
clear
print_header "$title" 60
}
4. Utilities (lib/utils.sh)
#!/bin/bash# lib/utils.sh - General utilities# Check if command existscommand_exists() {
command -v "$1" &> /dev/null
}
# Check required commandsrequire_commands() {
local missing=()
for cmd in"$@"; doif ! command_exists "$cmd"; then
missing+=("$cmd")
fidoneif [[ ${#missing[@]} -gt 0 ]]; then
log_error "Missing required commands: ${missing[*]}"return 1
fireturn 0
}
# Get script directoryget_script_dir() {
echo"$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
}
# Get project rootget_project_root() {
localdir="$(get_script_dir)"while [[ "$dir" != "/" ]]; doif [[ -f "$dir/pyproject.toml" ]] || [[ -f "$dir/CLAUDE.md" ]]; thenecho"$dir"return 0
fidir="$(dirname "$dir")"doneecho"$(pwd)"
}
# Run command with error handlingrun_cmd() {
local cmd="$*"
log_debug "Running: $cmd"ifeval"$cmd"; thenreturn 0
elselocal exit_code=$?
log_error "Command failed (exit code $exit_code): $cmd"return$exit_codefi
}
# Retry commandretry() {
local max_attempts="${1:-3}"local delay="${2:-5}"shift 2
local cmd="$*"local attempt=1
while [[ $attempt -le $max_attempts ]]; do
log_info "Attempt $attempt/$max_attempts: $cmd"ifeval"$cmd"; thenreturn 0
fiif [[ $attempt -lt $max_attempts ]]; then
log_warn "Retrying in ${delay}s..."sleep"$delay"fi
((attempt++))
done
log_error "All $max_attempts attempts failed"return 1
}
# Check if running as rootis_root() {
[[ $EUID -eq 0 ]]
}
# Check OS typeget_os() {
case"$(uname -s)"in
Linux*) echo"linux" ;;
Darwin*) echo"macos" ;;
CYGWIN*|MINGW*|MSYS*) echo"windows" ;;
*) echo"unknown" ;;
esac
}
# Check if in git repositoryis_git_repo() {
git rev-parse --is-inside-work-tree &> /dev/null
}
# Get current git branchget_git_branch() {
git rev-parse --abbrev-ref HEAD 2>/dev/null
}
# Cleanup handlercleanup() {
local exit_code=$?
# Add cleanup tasks hereexit$exit_code
}
# Setup signal handlerssetup_cleanup() {
trap cleanup EXIT INT TERM
}
# Parse argumentsparse_args() {
while [[ $# -gt 0 ]]; docase"$1"in
-v|--verbose) VERBOSE=true ;;
-q|--quiet) QUIET=true ;;
-h|--help) show_help; exit 0 ;;
--) shift; break ;;
-*) log_error "Unknown option: $1"; exit 1 ;;
*) ARGS+=("$1") ;;
esacshiftdone
}
# File operationsbackup_file() {
local file="$1"if [[ -f "$file" ]]; thenlocal backup="${file}.bak.$(date +%Y%m%d%H%M%S)"cp"$file""$backup"
log_info "Backed up: $file -> $backup"fi
}
# Ensure directory existsensure_dir() {
localdir="$1"if [[ ! -d "$dir" ]]; thenmkdir -p "$dir"
log_debug "Created directory: $dir"fi
}
5. Main Script Template
#!/bin/bash# scripts/my-tool - Main entry point# Description: Tool description hereset -e # Exit on error# Get script directory and load libraries
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"source"${SCRIPT_DIR}/lib/colors.sh"source"${SCRIPT_DIR}/lib/logging.sh"source"${SCRIPT_DIR}/lib/menu.sh"source"${SCRIPT_DIR}/lib/utils.sh"# Configuration
VERSION="1.0.0"
TOOL_NAME="My Tool"# Show helpshow_help() {
cat << EOF
${BOLD}${TOOL_NAME}${NC} v${VERSION}
${BOLD}Usage:${NC}
$0 [options] [command]
${BOLD}Commands:${NC}
menu Show interactive menu (default)
status Show status
run Run operation
help Show this help
${BOLD}Options:${NC}
-v, --verbose Enable verbose output
-q, --quiet Suppress output
-h, --help Show this help
${BOLD}Examples:${NC}
$0 # Interactive menu
$0 status # Show status
$0 run --verbose # Run with verbose output
EOF
}
# Show versionshow_version() {
echo"${TOOL_NAME} v${VERSION}"
}
# Status commandcmd_status() {
print_header "Status"
print_info "Version: ${VERSION}"
print_info "OS: $(get_os)"
print_info "User: $(whoami)"if is_git_repo; then
print_success "Git repository: $(get_git_branch)"else
print_warning "Not a git repository"fi
}
# Run commandcmd_run() {
print_header "Running Operation"
log_info "Starting operation..."# Add operation logic here
print_success "Operation completed"
}
# Main menumain_menu() {
whiletrue; do
clear_with_header "$TOOL_NAME"
choice=$(show_menu "Main Menu" \
"View Status" \
"Run Operation" \
"Settings" \
"---" \
"Help"
)
case"$choice"in
1) cmd_status; wait_for_key ;;
2) cmd_run; wait_for_key ;;
3) settings_menu ;;
4) show_help; wait_for_key ;;
0|"") break ;;
*) print_error "Invalid option"; sleep 1 ;;
esacdone
}
# Settings submenusettings_menu() {
whiletrue; do
choice=$(show_menu "Settings" \
"Set Log Level" \
"Enable File Logging" \
"View Configuration"
)
case"$choice"in
1)
level=$(select_from_list "Log Level""debug""info""warn""error")
[[ -n "$level" ]] && set_log_level "$level"
;;
2) enable_file_logging "logs/tool.log" ;;
3) print_info "Config: verbose=$VERBOSE" ;;
0|"") break ;;
esacdone
}
# Main entry pointmain() {
# Parse global optionswhile [[ $# -gt 0 ]]; docase"$1"in
-v|--verbose) set_log_level debug ;;
-q|--quiet) set_log_level error ;;
-h|--help) show_help; exit 0 ;;
--version) show_version; exit 0 ;;
-*) log_error "Unknown option: $1"; exit 1 ;;
*) break ;;
esacshiftdone# Handle commandslocal cmd="${1:-menu}"case"$cmd"in
menu) main_menu ;;
status) cmd_status ;;
run) shift; cmd_run "$@" ;;
help) show_help ;;
*) log_error "Unknown command: $cmd"; show_help; exit 1 ;;
esac
}
# Run main
main "$@"
Usage Examples
Example 1: Create New CLI Tool
# Initialize framework
/bash-script-framework init
# Create tool with menu
/bash-script-framework new repo-manager --menu
# Result: scripts/repo-manager with full menu system