diff --git a/README.md b/README.md index 0953d5b..d2116b1 100644 --- a/README.md +++ b/README.md @@ -34,22 +34,35 @@ super-man ai "count lines in js files" # AI bash one-liner via local LLM (advan ### 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: +model. The default is `westenfelder/NL2SH`, a model purpose-built for +natural-language-to-shell — in our 25-query benchmark it was the most accurate, the +only one that handled hard multi-stage tasks. The model is configurable, so you can +still A/B other 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 +super-man ai "find python files modified last week" # default model (NL2SH) +super-man ai -q "list the 5 largest files" # quiet: print ONLY the command +super-man ai --model qwen2.5-coder:1.5b "count lines in js" # override model +super-man ai --compare "list the 5 largest files" # run several models side by side +``` + +The `-q` / `--quiet` / `--raw` flag prints only the generated command to stdout (errors +go to stderr), so it composes in scripts and pipelines: + +```bash +cmd=$(super-man ai -q "compress the logs folder into a tarball") +echo "$cmd" # tar -czf logs.tar.gz logs/ ``` | 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` | +| `SUPERMAN_AI_MODEL` | Default model for `ai` | `westenfelder/NL2SH` | +| `SUPERMAN_AI_COMPARE_MODELS` | Space-separated list for `--compare` | `westenfelder/NL2SH qwen2.5-coder:1.5b` | -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 `. +The purpose-built `westenfelder/NL2SH` model receives the raw query; general coder +models (e.g. `qwen2.5-coder`) are prompt-wrapped to emit a single bare command. Pull +either with `ollama pull `. See the +[benchmark write-up](tests/performance/) for the full model comparison. `Esc` in the flag browser returns to the command list; `Esc` on the home screen exits. diff --git a/super-man.sh b/super-man.sh index 1e0d56e..083853a 100755 --- a/super-man.sh +++ b/super-man.sh @@ -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 bash one-liner via local LLM (advanced) 🤖" + echo -e " ${GREEN}ai${NC} \"complex query\" Text-to-bash one-liner, fully local (advanced) 🤖" echo "" echo -e " ${CYAN}Browse & Explore:${NC}" echo -e " ${GREEN}category${NC} NAME Browse by category (files/text/network/system)" @@ -76,19 +76,21 @@ show_help() { echo -e " ${GREEN}✓${NC} jq (for natural language queries)" echo -e " ${GREEN}✓${NC} fzf (for interactive mode only)" echo "" - echo -e "${BOLD}${YELLOW}AI MODE${NC} 🤖" - echo -e " Generate bash one-liners from plain English via a local ollama model:" + echo -e "${BOLD}${YELLOW}AI MODE${NC} 🤖 (text-to-bash, fully local via ollama)" echo -e " ${GREEN}super-man ai \"find python files modified last week\"${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 -e " ${GREEN}super-man ai -q \"count lines in all js files\"${NC} # print ONLY the command" + echo -e " ${GREEN}super-man ai --compare \"list the 5 largest files\"${NC} # A/B models" + echo "" + echo -e " ${DIM}-q/--raw prints just the command — pipeable:${NC}" + echo -e " ${GREEN}\$(super-man ai -q \"...\")${NC} or ${GREEN}super-man ai -q \"...\" | tee cmd.sh${NC}" echo "" 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_MODEL default model (e.g. qwen2.5-coder:1.5b for speed)" echo " SUPERMAN_AI_COMPARE_MODELS space-separated list for --compare" echo "" echo -e "${YELLOW}Database:${NC} $DB_FILE" - echo -e "${YELLOW}Version:${NC} 2.2.0 (250+ commands, configurable local AI)" + echo -e "${YELLOW}Version:${NC} 2.3.0 (250+ commands, local text-to-bash AI)" echo "" } @@ -319,8 +321,8 @@ mode_category() { # 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}" +SUPERMAN_AI_MODEL="${SUPERMAN_AI_MODEL:-westenfelder/NL2SH}" +SUPERMAN_AI_COMPARE_MODELS="${SUPERMAN_AI_COMPARE_MODELS:-westenfelder/NL2SH qwen2.5-coder:1.5b}" # Build a model-appropriate prompt for a query. ai_build_prompt() { @@ -374,11 +376,13 @@ ai_explain_base() { mode_ai() { local compare=false + local quiet=false local model="$SUPERMAN_AI_MODEL" local args=() while [ $# -gt 0 ]; do case "$1" in --compare|-c) compare=true ;; + --quiet|--raw|-q) quiet=true ;; --model|-m) shift; model="$1" ;; *) args+=("$1") ;; esac @@ -387,27 +391,43 @@ mode_ai() { local user_query="${args[*]}" if [ -z "$user_query" ]; then - echo -e "${RED}Error: No query provided${NC}" - echo "Usage: super-man ai [--model NAME|--compare] \"your query\"" - echo "" - 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\"" + echo -e "${RED}Error: No query provided${NC}" >&2 + echo "Usage: super-man ai [--model NAME] [--quiet|--compare] \"your query\"" >&2 + echo "" >&2 + echo "Examples:" >&2 + echo " super-man ai \"find all python files modified in last week\"" >&2 + echo " super-man ai -q \"count lines in all js files\" # prints only the command" >&2 + echo " super-man ai --compare \"list the 5 largest files\"" >&2 return 1 fi # Check if ollama is available if ! command -v ollama &> /dev/null; then - echo -e "${RED}Error: Ollama is not installed${NC}" - echo "" - echo "Install Ollama:" - echo " curl -fsSL https://ollama.com/install.sh | sh" - echo "" - echo "For now, try: super-man ask \"$user_query\"" + echo -e "${RED}Error: Ollama is not installed${NC}" >&2 + echo "" >&2 + echo "Install Ollama:" >&2 + echo " curl -fsSL https://ollama.com/install.sh | sh" >&2 + echo "" >&2 + echo "For now, try: super-man ask \"$user_query\"" >&2 return 1 fi + # ── Quiet/raw mode: print ONLY the generated command (pipeable, scriptable) ─ + if $quiet; then + if ! ai_model_installed "$model"; then + echo "Error: model '$model' not installed. Pull with: ollama pull $model" >&2 + return 1 + fi + local out + out=$(ai_generate "$model" "$user_query") + if [ -z "$out" ]; then + echo "Error: no response from $model" >&2 + return 1 + fi + printf '%s\n' "$out" + return 0 + fi + # ── Compare mode: run every configured model side by side ────────────────── if $compare; then echo ""