manage.sh•35.7 kB
#!/usr/bin/env bash
# 🌳 Smart Tree Management Script - Because every tree needs a gardener! 🌳
set -euo pipefail
# Colors for our fancy output
if [[ -t 1 ]] && [[ "${NO_COLOR:-}" != "1" ]]; then
RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[0;34m'
PURPLE=$'\033[0;35m'
CYAN=$'\033[0;36m'
NC=$'\033[0m' # No Color
else
RED=''
GREEN=''
YELLOW=''
BLUE=''
PURPLE=''
CYAN=''
NC=''
fi
# Project info
PROJECT_NAME="Smart Tree (st)"
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BINARY_NAME="st"
# Non-interactive mode flag
NON_INTERACTIVE=${NON_INTERACTIVE:-false}
# Emojis for fun (can be disabled)
if [[ "${NO_EMOJI:-}" == "1" ]]; then
TREE="[TREE]"
ROCKET="[GO]"
GEAR="[BUILD]"
TEST="[TEST]"
CLEAN="[CLEAN]"
INFO="[INFO]"
CHECK="[OK]"
CROSS="[FAIL]"
SPARKLE="[*]"
else
TREE="🌳"
ROCKET="🚀"
GEAR="⚙️"
TEST="🧪"
CLEAN="🧹"
INFO="📊"
CHECK="✅"
CROSS="❌"
SPARKLE="✨"
fi
# Helper functions
print_header() {
echo -e "\n${CYAN}${TREE} $1 ${TREE}${NC}\n"
}
print_success() {
echo -e "${GREEN}${CHECK} $1${NC}"
}
print_error() {
echo -e "${RED}${CROSS} $1${NC}"
}
print_info() {
echo -e "${BLUE}${INFO} $1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
# Ask yes/no question
ask_yes_no() {
local prompt="$1"
local default="${2:-y}"
if [[ "$NON_INTERACTIVE" == "true" ]]; then
[[ "$default" == "y" ]] && return 0 || return 1
fi
local response
if [[ "$default" == "y" ]]; then
read -p "$(echo -e "${CYAN}$prompt [Y/n]: ${NC}")" response
[[ -z "$response" || "$response" =~ ^[Yy] ]] && return 0 || return 1
else
read -p "$(echo -e "${CYAN}$prompt [y/N]: ${NC}")" response
[[ "$response" =~ ^[Yy] ]] && return 0 || return 1
fi
}
# Show animated spinner
spinner() {
local pid=$1
local delay=0.1
local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
while ps -p $pid > /dev/null 2>&1; do
local temp=${spinstr#?}
printf " [%c] " "$spinstr"
local spinstr=$temp${spinstr%"$temp"}
sleep $delay
printf "\b\b\b\b\b\b"
done
printf " \b\b\b\b"
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Build the project
build() {
local build_type="${1:-release}"
local features="${2:-}"
print_header "Building $PROJECT_NAME in $build_type mode ${GEAR}"
cd "$PROJECT_DIR"
local feature_flags=""
if [[ -n "$features" ]]; then
feature_flags="--features $features"
print_info "Building with features: $features"
fi
if [[ "$build_type" == "release" ]]; then
print_info "Optimizing for maximum speed... ${ROCKET}"
if [[ "$NON_INTERACTIVE" == "true" ]]; then
cargo build --release $feature_flags
else
cargo build --release $feature_flags &
spinner $!
fi
print_success "Release build complete! Binary size: $(du -h target/release/$BINARY_NAME | cut -f1)"
else
print_info "Building debug version with all the debugging goodies..."
cargo build $feature_flags
print_success "Debug build complete!"
fi
}
# Run the project
run() {
print_header "Running $PROJECT_NAME ${ROCKET}"
cd "$PROJECT_DIR"
# Default to current directory if no args provided
if [[ $# -eq 0 ]]; then
print_info "No arguments provided, analyzing current directory..."
cargo run --release -- .
else
cargo run --release "$@"
fi
}
# Run tests
test() {
print_header "Testing $PROJECT_NAME ${TEST}"
cd "$PROJECT_DIR"
print_info "Running unit tests..."
cargo test
print_info "Running clippy (our friendly neighborhood linter)..."
cargo clippy -- -D warnings || print_warning "Clippy found some issues!"
print_info "Checking formatting..."
cargo fmt -- --check || print_warning "Code needs formatting! Run './manage.sh format' to fix."
print_success "All tests passed! Your tree is healthy! ${TREE}"
}
# Format code
format() {
print_header "Formatting code ${SPARKLE}"
cd "$PROJECT_DIR"
cargo fmt
print_success "Code formatted! Looking prettier than a bonsai tree! 🎋"
}
# Clean build artifacts
clean() {
print_header "Cleaning up ${CLEAN}"
cd "$PROJECT_DIR"
cargo clean
print_success "All clean! Fresh as a spring forest! 🌱"
}
# Show project status
status() {
print_header "Project Status ${INFO}"
cd "$PROJECT_DIR"
echo -e "${PURPLE}Project:${NC} $PROJECT_NAME"
echo -e "${PURPLE}Location:${NC} $PROJECT_DIR"
echo -e "${PURPLE}Rust version:${NC} $(rustc --version)"
echo -e "${PURPLE}Cargo version:${NC} $(cargo --version)"
if [[ -f "target/release/$BINARY_NAME" ]]; then
echo -e "${PURPLE}Release binary:${NC} $(du -h target/release/$BINARY_NAME | cut -f1)"
echo -e "${PURPLE}Last modified:${NC} $(date -r target/release/$BINARY_NAME '+%Y-%m-%d %H:%M:%S')"
else
echo -e "${PURPLE}Release binary:${NC} Not built yet"
fi
echo -e "\n${PURPLE}Dependencies:${NC}"
cargo tree --depth 1 | head -20
echo -e "\n${PURPLE}Git status:${NC}"
if command_exists git && git rev-parse --git-dir > /dev/null 2>&1; then
git status --short || echo " Clean working tree ${CHECK}"
else
echo " Not a git repository"
fi
}
# Version management helpers
bump_version() {
print_header "Version Management 🔢"
cd "$PROJECT_DIR"
# Get current version from Cargo.toml
current_version=$(grep "^version = " Cargo.toml | head -1 | cut -d'"' -f2)
print_info "Current version: v$current_version"
# Parse version components
IFS='.' read -r major minor patch <<< "$current_version"
# Increment based on argument or default to patch
case "${1:-patch}" in
major)
new_version="$((major + 1)).0.0"
;;
minor)
new_version="$major.$((minor + 1)).0"
;;
patch|*)
new_version="$major.$minor.$((patch + 1))"
;;
esac
print_info "Bumping to: v$new_version"
# Update Cargo.toml
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/^version = \"$current_version\"/version = \"$new_version\"/" Cargo.toml
else
sed -i "s/^version = \"$current_version\"/version = \"$new_version\"/" Cargo.toml
fi
# Update CLAUDE.md if it exists
if [[ -f "CLAUDE.md" ]]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/v$current_version/v$new_version/g" CLAUDE.md
else
sed -i "s/v$current_version/v$new_version/g" CLAUDE.md
fi
print_success "Updated CLAUDE.md"
fi
# Clean up orphaned local tags
print_info "Cleaning orphaned tags..."
git tag -l | while read tag; do
if ! git ls-remote --tags origin | grep -q "refs/tags/$tag$"; then
print_warning "Removing local-only tag: $tag"
git tag -d "$tag" >/dev/null 2>&1
fi
done
print_success "Version bumped to v$new_version! ${CHECK}"
echo -e "\n${YELLOW}Next steps:${NC}"
echo " 1. Build: ./manage.sh build"
echo " 2. Test: ./manage.sh test"
echo " 3. Commit: git add -A && git commit -m 'chore: bump version to v$new_version'"
echo " 4. Tag: git tag -a v$new_version -m 'Version $new_version'"
echo " 5. Push: git push origin main && git push origin v$new_version"
}
# Quick version bump and build
quick_bump() {
print_header "Quick Version Bump & Build 🚀"
bump_version patch
echo
build release
print_success "Ready to commit and push!"
}
# Run benchmarks
bench() {
print_header "Running Benchmarks 📈"
cd "$PROJECT_DIR"
print_info "Building optimized version..."
cargo build --release
print_info "Benchmarking on current directory..."
time ./target/release/$BINARY_NAME . -m hex > /dev/null
print_info "Benchmarking with compression..."
time ./target/release/$BINARY_NAME . -m ai -z > /dev/null
if [[ -d "/usr" ]]; then
print_info "Benchmarking on /usr (large directory)..."
time ./target/release/$BINARY_NAME /usr -m hex --depth 3 > /dev/null || true
fi
}
# Install binary
install() {
print_header "Installing $PROJECT_NAME 🎯"
cd "$PROJECT_DIR"
local install_dir="${1:-/usr/local/bin}"
print_info "Building release version..."
cargo build --release
if [[ -w "$install_dir" ]]; then
cp "target/release/$BINARY_NAME" "$install_dir/"
print_success "Installed to $install_dir/$BINARY_NAME"
else
print_warning "Need sudo access to install to $install_dir"
sudo cp "target/release/$BINARY_NAME" "$install_dir/"
print_success "Installed to $install_dir/$BINARY_NAME (with sudo)"
fi
print_info "You can now use '$BINARY_NAME' from anywhere! ${ROCKET}"
}
# Uninstall binary
uninstall() {
print_header "Uninstalling $PROJECT_NAME 😢"
local install_dir="${1:-/usr/local/bin}"
local binary_path="$install_dir/$BINARY_NAME"
if [[ -f "$binary_path" ]]; then
if [[ -w "$install_dir" ]]; then
rm "$binary_path"
else
sudo rm "$binary_path"
fi
print_success "Uninstalled from $binary_path"
else
print_error "$BINARY_NAME not found in $install_dir"
fi
}
# Create a new GitHub release
release() {
print_header "Creating a new GitHub release ${ROCKET}"
local version="${1-}"
if [[ -z "$version" ]]; then
print_error "Version tag is required. Example: v1.0.0"
exit 1
fi
if ! command_exists gh; then
print_error "GitHub CLI (gh) is not installed. Please install it to create releases."
print_info "See: https://cli.github.com/"
exit 1
fi
if [[ -n "$(git status --porcelain)" ]]; then
print_error "Your working directory is not clean. Please commit or stash your changes."
git status --short
exit 1
fi
print_info "Creating release for version: $version"
# Artifacts directory
local artifact_dir="release_artifacts"
rm -rf "$artifact_dir"
mkdir -p "$artifact_dir"
# Get target triple for artifact naming
local target_triple
target_triple=$(rustc -vV | grep "host:" | cut -d ' ' -f2)
# --- Build standard version ---
print_info "Building standard release for ${target_triple}..."
build "release" ""
# Determine artifact name based on platform
local artifact_name
local archive_ext
if [[ "$target_triple" == *"windows"* ]]; then
artifact_name="${BINARY_NAME}-${target_triple}.exe"
archive_ext="zip"
else
artifact_name="${BINARY_NAME}-${target_triple}"
archive_ext="tar.gz"
fi
# Copy binary to artifact directory
cp "target/release/${BINARY_NAME}" "${artifact_dir}/${BINARY_NAME}"
# Create archive
if [[ "$archive_ext" == "zip" ]]; then
# For Windows, include .exe in the archive
mv "${artifact_dir}/${BINARY_NAME}" "${artifact_dir}/${BINARY_NAME}.exe"
(cd "$artifact_dir" && zip -q "${artifact_name}.zip" "${BINARY_NAME}.exe")
rm "${artifact_dir}/${BINARY_NAME}.exe"
local standard_artifact_path="${artifact_dir}/${artifact_name}.zip"
else
# For Unix, create tar.gz
(cd "$artifact_dir" && tar -czf "${artifact_name}.tar.gz" "${BINARY_NAME}")
rm "${artifact_dir}/${BINARY_NAME}"
local standard_artifact_path="${artifact_dir}/${artifact_name}.tar.gz"
fi
print_success "Standard artifact created: ${standard_artifact_path}"
# Note: We only create one set of artifacts since MCP is a default feature
# --- Build DXT package ---
print_info "Building DXT package..."
if [[ -f "dxt/build-dxt.sh" ]]; then
(cd dxt && ./build-dxt.sh)
if [[ -f "dxt/smart-tree.dxt" ]]; then
cp "dxt/smart-tree.dxt" "${artifact_dir}/"
local dxt_artifact_path="${artifact_dir}/smart-tree.dxt"
print_success "DXT package created: ${dxt_artifact_path}"
else
print_warning "DXT build completed but smart-tree.dxt not found"
local dxt_artifact_path=""
fi
else
print_warning "DXT build script not found, skipping DXT package"
local dxt_artifact_path=""
fi
# --- Create GitHub Release ---
local release_title="Smart Tree ${version}"
shift # remove version from args
local release_notes="${*:-Release ${version}}"
print_info "Creating GitHub release..."
print_info "Title: $release_title"
print_info "Notes: $release_notes"
if [[ "$NON_INTERACTIVE" == "true" ]]; then
# Create git tag
print_info "Tagging and pushing version $version..."
git tag "$version"
git push origin "$version"
print_info "Creating GitHub release and uploading artifacts..."
# Note: This will only upload the artifact for the current platform
# For multi-platform releases, you'll need to build on each platform or use CI/CD
if [[ -n "$dxt_artifact_path" ]]; then
gh release create "$version" "$standard_artifact_path" "$dxt_artifact_path" --title "$release_title" --notes "$release_notes"
else
gh release create "$version" "$standard_artifact_path" --title "$release_title" --notes "$release_notes"
fi
else
echo -e "\n${YELLOW}Ready to create release. Review details above.${NC}"
read -p "Do you want to proceed? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_warning "Release cancelled."
rm -rf "$artifact_dir"
exit 1
fi
# Create git tag and push it
print_info "Tagging version $version..."
git tag "$version"
git push origin "$version"
print_success "Pushed tag $version to origin."
print_info "Creating GitHub release and uploading artifacts..."
# Note: This will only upload the artifact for the current platform
# For multi-platform releases, you'll need to build on each platform or use CI/CD
if [[ -n "$dxt_artifact_path" ]]; then
gh release create "$version" "$standard_artifact_path" "$dxt_artifact_path" --title "$release_title" --notes "$release_notes"
else
gh release create "$version" "$standard_artifact_path" --title "$release_title" --notes "$release_notes"
fi
fi
print_success "Release ${version} created successfully! ${TREE} ${SPARKLE}"
rm -rf "$artifact_dir"
}
# Setup shell completions
completions() {
print_header "Setting up shell completions 🐚"
cd "$PROJECT_DIR"
# Check if st is already built and installed
if ! command_exists st && [[ ! -f "./target/release/st" ]]; then
print_info "Building release binary to generate completions..."
cargo build --release
fi
# Use our enhanced setup script
if [[ -f "$PROJECT_DIR/scripts/setup-completions.sh" ]]; then
print_info "Running enhanced completion setup..."
if [[ "$NON_INTERACTIVE" == "true" ]]; then
# Non-interactive mode - skip prompts and use defaults
yes "y" | bash "$PROJECT_DIR/scripts/setup-completions.sh"
else
bash "$PROJECT_DIR/scripts/setup-completions.sh"
fi
else
# Fallback to basic method if setup script doesn't exist
print_warning "Enhanced setup script not found, using basic method..."
local shell_type
shell_type="$(basename "$SHELL")"
print_info "Detected shell: $shell_type"
case "$shell_type" in
bash)
local completion_dir="$HOME/.bash_completion.d"
mkdir -p "$completion_dir"
print_info "Generating bash completions..."
./target/release/st --completions bash > "$completion_dir/_st"
print_success "Bash completions installed!"
print_info "Add 'source $completion_dir/_st' to your .bashrc"
;;
zsh)
local completion_dir="$HOME/.zsh/completions"
mkdir -p "$completion_dir"
print_info "Generating zsh completions..."
./target/release/st --completions zsh > "$completion_dir/_st"
print_success "Zsh completions installed!"
print_info "Add 'fpath=($completion_dir \$fpath)' to your .zshrc"
;;
fish)
local completion_dir="$HOME/.config/fish/completions"
mkdir -p "$completion_dir"
print_info "Generating fish completions..."
./target/release/st --completions fish > "$completion_dir/st.fish"
print_success "Fish completions installed!"
;;
*)
print_warning "Unknown shell: $shell_type"
print_info "Supported shells: bash, zsh, fish"
print_info "Generate manually: st --completions <shell>"
;;
esac
fi
}
# Install/Uninstall man page
manage_man_page() {
print_header "Managing Man Page 📖"
cd "$PROJECT_DIR"
if ! command_exists pandoc; then
print_error "pandoc is not installed. Please install it to continue."
print_info "On Debian/Ubuntu: sudo apt-get install pandoc"
print_info "On macOS (Homebrew): brew install pandoc"
print_info "On Arch Linux: sudo pacman -S pandoc"
exit 1
fi
local man_dir="/usr/local/share/man/man1"
local man_page_src="docs/st-cheetsheet.md"
local man_page_dest="$man_dir/st.1"
if [[ "$1" == "install" ]]; then
print_info "Generating and installing man page from $man_page_src..."
if [[ ! -d "$man_dir" ]]; then
print_info "Creating man directory: $man_dir"
sudo mkdir -p "$man_dir"
fi
# Use a temporary file for pandoc output before moving with sudo
local temp_file
temp_file=$(mktemp)
pandoc "$man_page_src" -s -t man -o "$temp_file"
print_info "Installing to $man_page_dest"
sudo mv "$temp_file" "$man_page_dest"
print_info "Updating man database..."
sudo mandb
print_success "Man page for 'st' installed. Try 'man st'!"
elif [[ "$1" == "uninstall" ]]; then
if [[ -f "$man_page_dest" ]]; then
print_info "Uninstalling man page: $man_page_dest"
sudo rm "$man_page_dest"
print_info "Updating man database..."
sudo mandb
print_success "Man page for 'st' uninstalled."
else
print_warning "Man page not found at $man_page_dest. Nothing to do."
fi
else
print_error "Unknown argument. Use 'install' or 'uninstall'."
fi
}
# MCP server functions
mcp_build() {
print_header "Building $PROJECT_NAME (MCP included) 🤖"
build release
}
mcp_run() {
print_header "Running MCP server 🤖"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Binary not found. Building release version..."
build release
fi
print_info "Starting MCP server on stdio..."
print_info "Press Ctrl+C to stop"
./target/release/$BINARY_NAME --mcp
}
mcp_config() {
print_header "MCP Configuration 🤖"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
./target/release/$BINARY_NAME --mcp-config
}
mcp_tools() {
print_header "MCP Tools Documentation 🤖"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
./target/release/$BINARY_NAME --mcp-tools
}
# Claude Code Hooks functions
hooks_list() {
print_header "Claude Code Hooks Configuration 🎣"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
./target/release/$BINARY_NAME --hooks-config status
}
hooks_enable() {
print_header "Enabling Claude Code Hooks 🎣"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
./target/release/$BINARY_NAME --hooks-config enable
}
hooks_disable() {
print_header "Disabling Claude Code Hooks 🎣"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
./target/release/$BINARY_NAME --hooks-config disable
}
hooks_test() {
local test_input="${1:-test input}"
print_header "Testing Claude Code Hook 🧪"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
print_info "Testing hook locally with input: '$test_input'"
echo '{"prompt": "'"$test_input"'"}' | ./target/release/$BINARY_NAME --claude-user-prompt-submit
}
hooks_setup() {
print_header "Setting up Claude Code Hooks 🎣"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
./target/release/$BINARY_NAME --hooks-install
}
# Demo streaming feature
demo_stream() {
print_header "Demonstrating Streaming Mode ${ROCKET}"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
print_info "Streaming the current project directory in hex format..."
print_info "Notice how output appears immediately as files are discovered!"
echo ""
./target/release/$BINARY_NAME --stream -m hex . | head -20
echo "... (truncated for demo)"
print_info "\nStreaming with AI format:"
./target/release/$BINARY_NAME --stream -m ai . | head -25
echo "... (truncated for demo)"
print_success "Streaming is perfect for large directories! ${SPARKLE}"
}
# Demo search feature
demo_search() {
print_header "Demonstrating Search Feature 🔍"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
print_info "Searching for 'Scanner' in the source code..."
./target/release/$BINARY_NAME --search "Scanner" -m hex src | grep SEARCH || print_warning "No matches found"
print_info "\nSearching for 'TODO' comments..."
./target/release/$BINARY_NAME --search "TODO" -m hex . | grep SEARCH || print_info "Good news! No TODOs found!"
print_info "\nSearch works great with streaming too:"
./target/release/$BINARY_NAME --stream --search "fn" --type rs -m hex src | head -10
print_success "Search helps you find content quickly! ${SPARKLE}"
}
# Demo relations feature
demo_relations() {
print_header "Demonstrating Code Relations Feature 🔗"
cd "$PROJECT_DIR"
if [[ ! -f "target/release/$BINARY_NAME" ]]; then
print_warning "Building release version first..."
build release
fi
print_info "Analyzing relationships in src/ directory..."
./target/release/$BINARY_NAME -m relations src | head -30
echo "... (truncated for demo)"
print_info "\nWith import filter:"
./target/release/$BINARY_NAME -m relations --relations-filter imports src | head -20
echo "... (truncated for demo)"
print_info "\nFocusing on main.rs:"
./target/release/$BINARY_NAME -m relations --focus src/main.rs src | head -15
print_success "Relations feature provides semantic X-ray vision for codebases! ${SPARKLE}"
}
# Show usage examples
examples() {
print_header "Usage Examples ${SPARKLE}"
cat << EOF
${CYAN}Basic usage:${NC}
$BINARY_NAME # Analyze current directory
$BINARY_NAME /path/to/dir # Analyze specific directory
${CYAN}Output modes:${NC}
$BINARY_NAME -m hex # Hexadecimal format (AI-friendly)
$BINARY_NAME -m json # JSON output
$BINARY_NAME -m ai # AI-optimized format
$BINARY_NAME -m digest # Super compact digest (hash + stats)
$BINARY_NAME -m stats # Statistics only
${CYAN}Filtering:${NC}
$BINARY_NAME --find "*.rs" # Find Rust files
$BINARY_NAME --type rs # Only .rs files
$BINARY_NAME --min-size 1M # Files larger than 1MB
$BINARY_NAME --newer-than 2024-01-01 # Recent files
${CYAN}Options:${NC}
$BINARY_NAME --no-emoji # Plain text output
$BINARY_NAME --depth 3 # Limit depth
$BINARY_NAME -z # Compress output
${CYAN}🆕 Streaming Mode:${NC}
$BINARY_NAME --stream # Stream output as files are found
$BINARY_NAME --stream -m hex /large # Great for huge directories
$BINARY_NAME --stream -m ai # Real-time AI format output
${CYAN}🆕 File Content Search:${NC}
$BINARY_NAME --search "TODO" # Find TODO in all text files
$BINARY_NAME --type rs --search "fn" # Search for "fn" in Rust files
$BINARY_NAME -m hex --search "error" # Hex output with search positions
${CYAN}🔗 Code Relations (NEW!):${NC}
$BINARY_NAME -m relations # Analyze code relationships
$BINARY_NAME -m relations --focus main.rs # Focus on specific file
$BINARY_NAME -m relations --relations-filter imports # Show only imports
${CYAN}AI usage:${NC}
AI_TOOLS=1 $BINARY_NAME # Auto AI mode + compression
$BINARY_NAME -m digest # Quick digest for AI pre-check
$BINARY_NAME -m ai -z | base64 -d # Decode compressed output
${CYAN}MCP (Model Context Protocol):${NC}
$0 mcp-run # Run as MCP server
$0 mcp-config # Show Claude Desktop config
$0 mcp-tools # Show available MCP tools (20+)
EOF
}
# Show help
show_help() {
cat << EOF
${CYAN}${TREE} Smart Tree Management Script ${TREE}${NC}
${YELLOW}Usage:${NC} $0 [command] [options]
${YELLOW}Commands:${NC}
${GREEN}build${NC} [debug|release] [features] Build the project
${GREEN}run${NC} [args...] Run st with arguments
${GREEN}test${NC} Run tests, linting, and format check
${GREEN}format${NC} Format code with rustfmt
${GREEN}clean${NC} Clean build artifacts
${GREEN}status${NC} Show project status
${GREEN}bench${NC} Run performance benchmarks
${GREEN}menu${NC} Launch interactive setup menu ${SPARKLE}
${GREEN}install${NC} [dir] Install binary (default: /usr/local/bin)
${GREEN}uninstall${NC} [dir] Uninstall binary
${GREEN}release${NC} <vX.Y.Z> [notes] Create a GitHub release
${GREEN}bump${NC} [major|minor|patch] Bump version (default: patch +0.0.1)
${GREEN}quick-bump${NC} Quick version bump + build 🚀
${GREEN}completions${NC} Setup shell completions
${GREEN}man-install${NC} Generate and install the man page
${GREEN}man-uninstall${NC} Uninstall the man page
${GREEN}examples${NC} Show usage examples
${GREEN}demo-stream${NC} Demo streaming feature
${GREEN}demo-search${NC} Demo search feature
${GREEN}demo-relations${NC} Demo code relations feature 🔗
${GREEN}rename-project${NC} <old> <new> Elegant project identity transition 🚗
${GREEN}help${NC} Show this help message
${YELLOW}MCP Commands:${NC}
${GREEN}mcp-run${NC} Run as MCP server
${GREEN}mcp-config${NC} Show Claude Desktop configuration
${GREEN}mcp-tools${NC} Show available MCP tools (20+)
${YELLOW}Claude Code Hooks:${NC}
${GREEN}hooks-setup${NC} Interactive setup for Claude Code hooks
${GREEN}hooks-list${NC} List current hooks configuration
${GREEN}hooks-enable${NC} [type] Enable a specific hook (default: UserPromptSubmit)
${GREEN}hooks-disable${NC} [type] Disable a specific hook
${GREEN}hooks-test${NC} [type] [input] Test a hook with sample input
${YELLOW}Feedback System Commands:${NC}
${GREEN}feedback-build${NC} Build feedback system containers
${GREEN}feedback-run${NC} Run feedback worker locally
${GREEN}feedback-deploy${NC} [type] Deploy feedback system (local|hetzner|registry)
${GREEN}feedback-test${NC} Test feedback system
${GREEN}feedback-status${NC} Check feedback system health
${YELLOW}Environment Variables:${NC}
${PURPLE}NO_EMOJI=1${NC} Disable emojis in output
${PURPLE}NON_INTERACTIVE=true${NC} Disable interactive features
${YELLOW}Examples:${NC}
$0 build # Build release version
$0 run -- -m hex . # Run with hex output on current dir
$0 test # Run all tests
$0 release v1.0.0 "My first release!" # Create a new release
$0 mcp-run # Start MCP server
${CYAN}Made with ${SPARKLE} and 🌳 by the Smart Tree team!${NC}
EOF
}
# Feedback system functions
feedback_build() {
print_header "Building Feedback System Containers 🔨"
cd "$PROJECT_DIR/feedback-worker"
print_info "Building feedback API container..."
docker build -t ghcr.io/8b-is/smart-tree-feedback-api:latest ../feedback-api/
print_info "Building feedback worker container..."
docker build -t ghcr.io/8b-is/smart-tree-feedback-worker:latest .
print_success "Feedback containers built successfully!"
}
feedback_run() {
print_header "Running Feedback Worker Locally 🎸"
cd "$PROJECT_DIR/feedback-worker"
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
print_warning "GITHUB_TOKEN not set - worker will run without GitHub integration"
fi
print_info "Starting feedback system with docker-compose..."
docker-compose up -d
print_success "Feedback system running!"
print_info " API: http://localhost:8422"
print_info " Metrics: http://localhost:9090/metrics"
print_info " Grafana: http://localhost:3000 (admin/admin)"
}
feedback_deploy() {
local deploy_type="${1:-local}"
print_header "Deploying Feedback System - Type: $deploy_type 🚀"
cd "$PROJECT_DIR/feedback-worker"
./deploy.sh "$deploy_type"
}
feedback_test() {
print_header "Testing Feedback System 🧪"
cd "$PROJECT_DIR/feedback-worker"
if ! command_exists python3; then
print_error "Python 3 is required for testing"
return 1
fi
print_info "Running feedback system tests..."
python3 test_worker.py
}
feedback_status() {
print_header "Feedback System Status 📊"
# Check API
if curl -sf http://localhost:8422/health > /dev/null 2>&1; then
print_success "API is healthy"
curl -s http://localhost:8422/health | jq . || true
else
print_error "API is not responding"
fi
# Check worker metrics
if curl -sf http://localhost:9090/metrics > /dev/null 2>&1; then
print_success "Worker is healthy"
echo " $(curl -s http://localhost:9090/metrics | grep -E '^feedback_processed_total' | head -1)"
else
print_error "Worker is not responding"
fi
# Check Redis
if docker exec $(docker ps -qf "name=redis" 2>/dev/null) redis-cli ping > /dev/null 2>&1; then
print_success "Redis is healthy"
else
print_error "Redis is not responding"
fi
# Check containers
print_info "Running containers:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "(feedback|redis)" || echo " No feedback containers running"
}
# Main command dispatcher
main() {
if [[ $# -eq 0 ]]; then
show_help
exit 0
fi
case "$1" in
build)
shift
build "$@"
;;
run)
shift
run "$@"
;;
test)
test
;;
format|fmt)
format
;;
clean)
clean
;;
menu|setup)
# Launch interactive setup menu
print_header "Launching Interactive Setup Menu ${SPARKLE}"
"$PROJECT_DIR/scripts/interactive_setup.sh"
;;
status|info)
status
;;
bench|benchmark)
bench
;;
install)
shift
install "$@"
;;
uninstall|remove)
shift
uninstall "$@"
;;
release)
shift
release "$@"
;;
bump)
shift
bump_version "$@"
;;
quick-bump)
quick_bump
;;
completions|complete)
completions
;;
man-install)
manage_man_page install
;;
man-uninstall)
manage_man_page uninstall
;;
examples|ex)
examples
;;
mcp-build)
print_info "MCP is now built-in! Just run 'build' instead."
build release
;;
mcp-run)
mcp_run
;;
mcp-config)
mcp_config
;;
mcp-tools)
mcp_tools
;;
hooks-setup)
hooks_setup
;;
hooks-list)
hooks_list
;;
hooks-enable)
hooks_enable "${2:-UserPromptSubmit}"
;;
hooks-disable)
hooks_disable "${2:-UserPromptSubmit}"
;;
hooks-test)
hooks_test "${2:-test input}"
;;
demo-stream)
demo_stream
;;
demo-search)
demo_search
;;
demo-relations)
demo_relations
;;
feedback-build)
feedback_build
;;
feedback-run)
feedback_run
;;
feedback-deploy)
feedback_deploy "${@:2}"
;;
feedback-test)
feedback_test
;;
feedback-status)
feedback_status
;;
rename-project)
if [[ $# -lt 3 ]]; then
print_error "Usage: $0 rename-project <old_name> <new_name>"
exit 1
fi
print_header "Project Rebranding Ritual 🚗"
cd "$PROJECT_DIR"
./target/release/st --rename-project "$2" "$3"
;;
help|h|-h|--help)
show_help
;;
*)
print_error "Unknown command: $1"
echo ""
show_help
exit 1
;;
esac
}
# Let's go! 🚀
main "$@"