feat(ai): make ai mode model-configurable + add --compare for A/B testing

Default the ai mode to qwen2.5-coder:1.5b instead of the hardcoded (and
uninstalled) westenfelder/NL2SH, which left ai mode broken. The model is now
configurable via SUPERMAN_AI_MODEL / --model, with markdown-fence stripping and
a coder-specific prompt wrapper so general coder models emit a single bare
one-liner. NL2SH still receives the raw query. Adds --compare to run multiple
models side by side (SUPERMAN_AI_COMPARE_MODELS) with timing for benchmarking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-06-09 14:24:40 -07:00
parent 0c6b871713
commit a699989088
2 changed files with 146 additions and 37 deletions
+21 -1
View File
@@ -28,9 +28,29 @@ super-man ask "find large files" # ask a question
super-man task "monitor cpu usage" # describe what you want to do
super-man category files # browse by category (files/text/network/system)
super-man explain "tar -czf" # explain what a command does
super-man ai "count lines in js files" # AI-powered NL2SH model (advanced)
super-man ai "count lines in js files" # AI bash one-liner via local LLM (advanced)
```
### AI mode (local LLM via ollama)
`ai` generates a bash one-liner from plain English using a local [ollama](https://ollama.com)
model. The model is configurable, so you can A/B different local LLMs:
```bash
super-man ai "find python files modified last week" # default model
super-man ai --model qwen2.5:3b "count lines in all js files" # override model
super-man ai --compare "list the 5 largest files" # run several models side by side
```
| Env var | Purpose | Default |
|---------|---------|---------|
| `SUPERMAN_AI_MODEL` | Default model for `ai` | `qwen2.5-coder:1.5b` |
| `SUPERMAN_AI_COMPARE_MODELS` | Space-separated list for `--compare` | `qwen2.5-coder:1.5b westenfelder/NL2SH` |
General coder models (e.g. `qwen2.5-coder`) are prompt-wrapped to emit a single bare
command; the purpose-built `westenfelder/NL2SH` model receives the raw query. Pull
either with `ollama pull <model>`.
`Esc` in the flag browser returns to the command list; `Esc` on the home screen
exits.
+125 -36
View File
@@ -41,7 +41,7 @@ show_help() {
echo -e " ${CYAN}Natural Language Queries:${NC}"
echo -e " ${GREEN}ask${NC} \"query\" Ask in natural language (database)"
echo -e " ${GREEN}task${NC} \"description\" Describe what you want to do (database)"
echo -e " ${GREEN}ai${NC} \"complex query\" AI-powered NL2SH model (advanced) 🤖"
echo -e " ${GREEN}ai${NC} \"complex query\" AI bash one-liner via local LLM (advanced) 🤖"
echo ""
echo -e " ${CYAN}Browse & Explore:${NC}"
echo -e " ${GREEN}category${NC} NAME Browse by category (files/text/network/system)"
@@ -77,15 +77,18 @@ show_help() {
echo -e " ${GREEN}${NC} fzf (for interactive mode only)"
echo ""
echo -e "${BOLD}${YELLOW}AI MODE${NC} 🤖"
echo -e " ${GREEN}✓ NL2SH model detected!${NC} Use AI for complex queries:"
echo -e " Generate bash one-liners from plain English via a local ollama model:"
echo -e " ${GREEN}super-man ai \"find python files modified last week\"${NC}"
echo -e " ${GREEN}super-man ai \"count lines in all js files\"${NC}"
echo -e " ${GREEN}super-man ai --model qwen2.5:3b \"count lines in all js files\"${NC}"
echo -e " ${GREEN}super-man ai --compare \"list the 5 largest files\"${NC} # A/B models"
echo ""
echo " Other models available: llama3.2, qwen2.5"
echo " To switch models, edit mode_ai() function"
echo -e " Default model: ${CYAN}${SUPERMAN_AI_MODEL}${NC}"
echo " Configure via env vars:"
echo " SUPERMAN_AI_MODEL default model (e.g. westenfelder/NL2SH)"
echo " SUPERMAN_AI_COMPARE_MODELS space-separated list for --compare"
echo ""
echo -e "${YELLOW}Database:${NC} $DB_FILE"
echo -e "${YELLOW}Version:${NC} 2.1.0 (250+ commands, AI-powered)"
echo -e "${YELLOW}Version:${NC} 2.2.0 (250+ commands, configurable local AI)"
echo ""
}
@@ -308,15 +311,85 @@ mode_category() {
}
# AI mode - use NL2SH model for complex queries
# Generate bash one-liners from natural language via a local ollama model.
# The model is configurable so we can A/B different local LLMs:
# SUPERMAN_AI_MODEL default model (default: qwen2.5-coder:1.5b)
# SUPERMAN_AI_COMPARE_MODELS space-separated list used by `ai --compare`
# Override per call with `--model NAME`; run every comparison model side by
# side with `--compare`. NL2SH is purpose-built for NL->shell so it receives
# the raw query; general coder models get an instruction wrapper plus markdown
# fence stripping so they too emit a single bare command.
SUPERMAN_AI_MODEL="${SUPERMAN_AI_MODEL:-qwen2.5-coder:1.5b}"
SUPERMAN_AI_COMPARE_MODELS="${SUPERMAN_AI_COMPARE_MODELS:-qwen2.5-coder:1.5b westenfelder/NL2SH}"
# Build a model-appropriate prompt for a query.
ai_build_prompt() {
local model="$1" query="$2"
case "$model" in
*NL2SH*|*nl2sh*)
printf '%s' "$query"
;;
*)
printf 'You are a bash expert. Output ONLY a single bash one-liner that accomplishes the task. No explanation, no comments, no markdown code fences. Task: %s' "$query"
;;
esac
}
# Strip markdown fences and surrounding blank lines from a raw model response.
ai_clean_response() {
awk '/^[[:space:]]*```/ { next } { print }' \
| sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \
| sed '/^$/d'
}
# True if an ollama model is pulled locally.
ai_model_installed() {
ollama list 2>/dev/null | grep -q "$1"
}
# Generate a cleaned command from one model for a query.
ai_generate() {
local model="$1" query="$2"
ollama run "$model" "$(ai_build_prompt "$model" "$query")" 2>/dev/null | ai_clean_response
}
# Print the man/help NAME line for the base command of a generated one-liner.
ai_explain_base() {
local base_cmd
base_cmd=$(echo "$1" | awk '{print $1}')
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
echo -e "${YELLOW}Command Info:${NC}"
help "$base_cmd" 2>/dev/null | head -3
echo ""
elif command -v "$base_cmd" &> /dev/null; then
echo -e "${YELLOW}Command Info:${NC}"
man "$base_cmd" 2>/dev/null | col -b | grep -A 2 "^NAME" | tail -2
echo ""
fi
}
mode_ai() {
local user_query="$1"
local compare=false
local model="$SUPERMAN_AI_MODEL"
local args=()
while [ $# -gt 0 ]; do
case "$1" in
--compare|-c) compare=true ;;
--model|-m) shift; model="$1" ;;
*) args+=("$1") ;;
esac
shift
done
local user_query="${args[*]}"
if [ -z "$user_query" ]; then
echo -e "${RED}Error: No query provided${NC}"
echo "Usage: super-man ai \"your complex query\""
echo "Usage: super-man ai [--model NAME|--compare] \"your query\""
echo ""
echo "Example:"
echo "Examples:"
echo " super-man ai \"find all python files modified in last week\""
echo " super-man ai --model qwen2.5:3b \"count lines in all js files\""
echo " super-man ai --compare \"list the 5 largest files\""
return 1
fi
@@ -331,18 +404,50 @@ mode_ai() {
return 1
fi
# Check if NL2SH model is available
if ! ollama list | grep -q "westenfelder/NL2SH"; then
echo -e "${YELLOW}NL2SH model not found. Available models:${NC}"
# ── Compare mode: run every configured model side by side ──────────────────
if $compare; then
echo ""
echo -e "${CYAN}🤖 AI Compare${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${YELLOW}Query:${NC} $user_query"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
local m
for m in $SUPERMAN_AI_COMPARE_MODELS; do
echo ""
echo -e "${BOLD}${MAGENTA}$m${NC}"
if ! ai_model_installed "$m"; then
echo -e " ${DIM}not installed — pull with: ollama pull $m${NC}"
continue
fi
local start end elapsed out
start=$(date +%s.%N)
out=$(ai_generate "$m" "$user_query")
end=$(date +%s.%N)
elapsed=$(awk "BEGIN{printf \"%.1f\", $end-$start}")
if [ -z "$out" ]; then
echo -e " ${RED}(no response)${NC} ${DIM}(${elapsed}s)${NC}"
else
echo -e " ${GREEN}$out${NC} ${DIM}(${elapsed}s)${NC}"
fi
done
echo ""
echo -e "${DIM}Note: Always review AI-generated commands before running them!${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
return 0
fi
# ── Single-model mode ──────────────────────────────────────────────────────
if ! ai_model_installed "$model"; then
echo -e "${YELLOW}Model '$model' not found. Installed models:${NC}"
ollama list
echo ""
echo "Pull NL2SH model with:"
echo " ollama pull westenfelder/NL2SH"
echo "Pull it with: ollama pull $model"
echo "Or pick another: super-man ai --model NAME \"$user_query\""
return 1
fi
echo ""
echo -e "${CYAN}🤖 AI Assistant - Using NL2SH Model${NC}"
echo -e "${CYAN}🤖 AI Assistant $model${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${YELLOW}Query:${NC} $user_query"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
@@ -350,8 +455,7 @@ mode_ai() {
echo -e "${DIM}Generating bash command...${NC}"
echo ""
# Query the NL2SH model
local response=$(ollama run westenfelder/NL2SH "$user_query" 2>/dev/null)
local response=$(ai_generate "$model" "$user_query")
if [ -z "$response" ]; then
echo -e "${RED}Error: No response from AI model${NC}"
@@ -365,19 +469,7 @@ mode_ai() {
echo -e " ${MAGENTA}$response${NC}"
echo ""
# Extract the base command for explanation
local base_cmd=$(echo "$response" | awk '{print $1}')
# Try to provide explanation
if command -v "$base_cmd" &> /dev/null || type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
echo -e "${YELLOW}Command Info:${NC}"
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
help "$base_cmd" 2>/dev/null | head -3
else
man "$base_cmd" 2>/dev/null | col -b | grep -A 2 "^NAME" | tail -2
fi
echo ""
fi
ai_explain_base "$response"
echo -e "${DIM}Note: Always review AI-generated commands before running them!${NC}"
echo ""
@@ -1250,8 +1342,9 @@ else
QUERY="$2"
;;
ai)
MODE="ai"
QUERY="$2"
shift
mode_ai "$@"
exit $?
;;
-*)
echo "Unknown option: $1"
@@ -1280,10 +1373,6 @@ if [ -n "$MODE" ]; then
mode_category "$QUERY"
exit $?
;;
ai)
mode_ai "$QUERY"
exit $?
;;
esac
fi