#!/usr/bin/env bash
set -euo pipefail

root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# Whatever name the user actually typed. `agentlas` is a symlink to this same
# runner, and answering "hephaestus:" to someone who ran `agentlas` is the
# half-renamed confusion this symlink exists to remove.
self="$(basename "${BASH_SOURCE[0]}")"
# The host spelling is compatibility syntax. Core resolves it to an internal
# commandId after Python starts, so aliases and runtime adapters never need to
# duplicate semantic routing rules.
export AGENTLAS_COMMAND_NAME="${1:-}"
export PYTHONUTF8="${PYTHONUTF8:-1}"
export PYTHONIOENCODING="${PYTHONIOENCODING:-utf-8}"
export PYTHONDONTWRITEBYTECODE=1
source "$root/bin/agentlas-python-cache-boundary"
if ! agentlas_export_python_cache_boundary; then
  echo "$self: could not establish a safe external Python cache directory." >&2
  exit 78
fi

resolve_python() {
  local candidate
  for candidate in \
    "${HEPHAESTUS_PYTHON:-}" \
    "$root/bin/python3" \
    python3 \
    python
  do
    [[ -n "$candidate" ]] || continue
    if "$candidate" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 9) else 1)' >/dev/null 2>&1; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  if command -v py >/dev/null 2>&1 && py -3 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 9) else 1)' >/dev/null 2>&1; then
    printf '%s\n' "py -3"
    return 0
  fi
  return 1
}

HEPHAESTUS_PY="$(resolve_python || true)"
# The interpreter may be a native Windows python launched from MSYS bash, which
# cannot read a POSIX root. Convert once: this value is both PYTHONPATH and the
# sys.path entry the bootstrap below inserts.
root_native="$(agentlas_native_path "$root")"
path_sep="$(agentlas_path_sep)"

run_python_module() {
  if [[ -z "$HEPHAESTUS_PY" ]]; then
    echo "$self: Python 3.9+ not found. On macOS/Linux install python3 (python.org or your package manager); on Windows install it from python.org or run 'py -3' once. Then rerun hephaestus doctor." >&2
    exit 127
  fi
  # shellcheck disable=SC2086
  HEPHAESTUS_RUNTIME_ROOT="$root_native" PYTHONPATH="$root_native${PYTHONPATH:+$path_sep$PYTHONPATH}" $HEPHAESTUS_PY -c 'import os, runpy, sys; cwd=os.getcwd(); root=os.environ["HEPHAESTUS_RUNTIME_ROOT"]; sys.path=[p for p in sys.path if p not in ("", cwd, root)]; sys.path.insert(0, root); sys.argv=sys.argv[1:]; runpy.run_module(sys.argv[0], run_name="__main__", alter_sys=True)' "$@"
}

upload_question() {
  cat <<'EOF'
Cloud에 업로드할까요? 다른 사람들은 볼 수 없어요.
Upload to Cloud? Other people cannot see it.

Agentlas Hub에 업로드할까요? 다른 사람들이 빌려 쓸 수 있어요.
Upload to Agentlas Hub? Other people can borrow it.

1) Cloud
2) Agentlas Hub
EOF
}

resolve_upload_folder() {
  local arg candidate
  for arg in "$@"; do
    candidate="${arg#file://}"
    candidate="${candidate%\"}"
    candidate="${candidate#\"}"
    if [[ -d "$candidate" ]]; then
      (cd "$candidate" && pwd)
      return 0
    fi
    if [[ -d "$PWD/$candidate" ]]; then
      (cd "$PWD/$candidate" && pwd)
      return 0
    fi
  done
  return 1
}

upload_usage() {
  cat <<'EOF' | sed "s|bin/hephaestus|$self|g"
Usage:
  bin/hephaestus hep-upload <agent-folder> --visibility <private-link|marketplace> [options]
  bin/hephaestus hep-upload <agent-folder>                         # ask Cloud vs Hub interactively

Destinations:
  private-link                 Agentlas Cloud, private access
  marketplace                  Agentlas Hub, public listing
  --cloud / --hub              Select the corresponding destination

Options:
  --dry-run                    Package and review without registering
  --slug <slug>                Override the package slug
  --base-url <url>             Use an explicit service base URL
  --expected-package-hash <sha256>
  --expected-upload-receipt <sha256>
  --overwrite-cloud-id <id>    Confirm overwrite of this exact Cloud asset
  --secret-review <file>       Host-model secret review (give the same file to dry-run and publish)
  --no-open                    Do not open a browser for sign-in
  -h, --help                   Show this help without selecting a destination
EOF
}

run_upload_gate() {
  local target choice visibility="" arg
  local -a folder_args=() publish_args=()
  for arg in "$@"; do
    case "$arg" in
      -h|--help)
        upload_usage
        return 0
        ;;
    esac
  done
  set_upload_visibility() {
    local requested="$1" normalized
    case "$requested" in
      cloud|private|private-link) normalized="private-link" ;;
      hub|marketplace|public|agentlas-hub) normalized="marketplace" ;;
      *)
        echo "Unknown upload visibility: $requested. Use private-link or marketplace." >&2
        return 2
        ;;
    esac
    if [[ -n "$visibility" && "$visibility" != "$normalized" ]]; then
      echo "Contradictory upload destinations: both $visibility and $normalized were requested." >&2
      return 2
    fi
    visibility="$normalized"
  }
  while [[ "$#" -gt 0 ]]; do
    arg="$1"
    case "$arg" in
      --visibility)
        if [[ "$#" -lt 2 || "${2:-}" == --* ]]; then
          echo "Missing value for --visibility. Use private-link or marketplace." >&2
          return 2
        fi
        set_upload_visibility "${2:-}" || return $?
        shift 2
        ;;
      --visibility=*)
        set_upload_visibility "${arg#--visibility=}" || return $?
        shift
        ;;
      --cloud|cloud|private|private-link)
        set_upload_visibility private-link || return $?
        shift
        ;;
      --hub|hub|marketplace|public|agentlas-hub)
        set_upload_visibility marketplace || return $?
        shift
        ;;
      --dry-run|--no-open)
        publish_args+=("$arg")
        shift
        ;;
      --slug|--base-url|--expected-package-hash|--expected-upload-receipt|--overwrite-cloud-id|--secret-review|--rent-credits|--ingest-credits|--fork-credits)
        if [[ "$#" -lt 2 || "${2:-}" == --* ]]; then
          echo "Missing value for $arg." >&2
          return 2
        fi
        publish_args+=("$arg" "${2:-}")
        shift 2
        ;;
      --slug=*|--base-url=*|--expected-package-hash=*|--expected-upload-receipt=*|--overwrite-cloud-id=*|--secret-review=*|--rent-credits=*|--ingest-credits=*|--fork-credits=*)
        publish_args+=("$arg")
        shift
        ;;
      *)
        folder_args+=("$arg")
        shift
        ;;
    esac
  done
  unset -f set_upload_visibility
  if [[ "${#folder_args[@]}" -gt 1 ]]; then
    echo "Choose exactly one upload folder." >&2
    return 2
  fi
  if [[ -n "${visibility:-}" ]]; then
    if ! target="$(resolve_upload_folder ${folder_args[@]+"${folder_args[@]}"})"; then
      echo "업로드 대상 에이전트 폴더를 찾지 못했습니다. /hep-upload <agent-folder> --visibility <private-link|marketplace> 형태로 다시 실행하세요." >&2
      return 2
    fi
    run_python_module agentlas_cloud publish "$target" --visibility "$visibility" ${publish_args[@]+"${publish_args[@]}"}
    return $?
  fi
  upload_question
  if [[ ! -t 0 ]]; then
    echo '{"status":"input_required","error":"upload_destination_required","performed":false}' >&2
    echo "No upload performed. Choose Cloud or Agentlas Hub in the chat, then run one explicit command:" >&2
    echo "  hep-upload <agent-folder> --visibility private-link   # Cloud" >&2
    echo "  hep-upload <agent-folder> --visibility marketplace    # Agentlas Hub" >&2
    return 3
  fi
  printf 'Select 1 or 2: ' >&2
  read -r choice
  if ! target="$(resolve_upload_folder ${folder_args[@]+"${folder_args[@]}"})"; then
    echo "업로드 대상 에이전트 폴더를 찾지 못했습니다. /hep-upload <agent-folder> 형태로 다시 실행하세요." >&2
    return 2
  fi
  case "$(printf '%s' "$choice" | tr '[:upper:]' '[:lower:]')" in
    1|cloud|c)
      run_python_module agentlas_cloud publish "$target" --visibility private-link ${publish_args[@]+"${publish_args[@]}"}
      ;;
    2|hub|agentlas\ hub|agentlas|h)
      run_python_module agentlas_cloud publish "$target" --visibility marketplace ${publish_args[@]+"${publish_args[@]}"}
      ;;
    *)
      echo "Upload cancelled. Choose 1 for Cloud or 2 for Agentlas Hub." >&2
      return 2
      ;;
  esac
}

usage() {
  cat <<'EOF' | sed "s|bin/hephaestus|$self|g"
Usage:
  bin/hephaestus --version
  bin/hephaestus career-graph status|ingest|verify|trace|public-card [--project <dir>]
  bin/hephaestus career-graph query "<question>" [--project <dir>]
  bin/hephaestus ontology [--gui] [--no-open] [project-dir]
  bin/hephaestus ontology add <path> [--project <dir>] [--kind company|project|personal] [--scope public|internal|private]
  bin/hephaestus wizard <agent-folder> [--name <name>]
  bin/hephaestus security scan <agent-folder> [--strict] [--acknowledge-warn] [--llm-judgment <path>]
  bin/hephaestus runtime bundle <agent-folder>
  bin/hephaestus runtime read-agent-file <agent-folder> <path>
  bin/hephaestus contract scaffold <agent-folder> [--mode single|team|package]
  bin/hephaestus contract complete <agent-folder> [--slug <slug>] [--mode single|team|package]
  bin/hephaestus contract verify <agent-folder> [--mode single|team|package]
  bin/hephaestus contract prompt [--mode single|team|package]
  bin/hephaestus session [inspect|normalize|validate|preview|merge|ir|compile|promote] [--input <session.jsonl>] [--global-agent]
  bin/hephaestus package <agent-folder> [--visibility marketplace|private-link]
  bin/hephaestus publish <agent-folder> --visibility marketplace|private-link [--dry-run] [--expected-package-hash <sha256>] [--expected-upload-receipt <sha256>] [--overwrite-cloud-id <id>] [--secret-review <file>]
  bin/hephaestus field-test
	  bin/hephaestus auth status|login|ensure|logout [--base-url <url>]
  bin/hephaestus doctor
  bin/hephaestus hep-update [--check]
  bin/hephaestus global install|status|remove [--target codex] [--target claude] [--target antigravity]
  bin/hephaestus plugins list [--project <dir>]
  bin/hephaestus plugins resolve "<need>" [--project <dir>] [--no-hub]
  bin/hephaestus project ensure|status [--project <dir>]
  bin/hephaestus meta-agent "<request>"
  bin/hephaestus hep-build "<request>"             # build/create/package surface
  bin/hephaestus build "<request>"                 # shorthand for hep-build
  bin/hephaestus network init|status|reindex|bench
  bin/hephaestus network add-source <path> | remove-source <path>
  bin/hephaestus network grant <capability> --target <id> [--scope ...]
  bin/hephaestus workforce goal-bind <goal-id> <prepared-plan.json> [--project <dir>]
  bin/hephaestus workforce goal-status [--goal-id <id>] [--project <dir>]
  bin/hephaestus workforce goal-runtime [--goal-id <id>] [--project <dir>]
  bin/hephaestus workforce goal-turn <goal-id> <turn-id> reuse|recruit|local-only|blocked|standby [--project <dir>]
  bin/hephaestus workforce goal-complete <goal-id> --explicit [--project <dir>]
  bin/hephaestus cards lint [path] | cards migrate <root> --tier restricted|private|plugin|local
  bin/hephaestus ao lint [dir] | ao graph [--agent <agent>] [dir] | ao query "<query>" [dir]
  bin/hephaestus ao plan <from> <to> [--max-depth N] [dir]
  bin/hephaestus ao diff [dir] | ao migrate [dir] [--no-write] [--overwrite]
  bin/hephaestus route "<request>" [--project <dir>] [--no-hub] [--approve-hub] [--hub-only] [--scope network|cloud] [--caller <agent-id>]
  bin/hephaestus local-gui "<shortcut>" [--detach] [--no-open]   # restore/open packaged GUI shortcuts
  bin/hephaestus hep-network "<request>"           # Local + owner Cloud + public Hub temporary TF
  bin/hephaestus hep-local "<request>"             # registered Local only
  bin/hephaestus hep-hub "<request>"               # public Hub only
  bin/hephaestus stormbreaker run "<request>" [--background] [--executor-command <cmd>] [--research-evidence] [--research-loadout auto|safe|public-web|social|browser|full|recommended]  # auto-run pipeline packets
  bin/hephaestus hep-storm "<request>" [--background] [--executor-command <cmd>] [--research-evidence] [--research-loadout auto|safe|public-web|social|browser|full|recommended]  # shorthand
  bin/hephaestus hep-cloud "<request>"              # owner Cloud only (보관함)
  bin/hephaestus hep-search "<request>" [--limit 10]     # show cloud + Hub candidates only
  bin/hephaestus hep-browser <url-or-query> [--setup|--check]  # use Agentlas browser hardpoint first
  bin/hephaestus research doctor|status|credentials|modules|armory|profile|bridge-contract|bridge-check|platform-contract|platform-check|loadouts | research plan <url> [--loadout public-web|browser] [--depth deep] | research gather "<query>" [--variant docs] [--follow-results 3] | research search "<query>" [--variant reddit] [--loadout safe|public-web|social] | research read <url> [--loadout public-web|browser] [--depth deep] [--max-weight browser_heavy]  # lightweight Research Engine
  bin/hephaestus hep-call "agent-a,agent-b" "<context>"  # prepare named Hub/cloud agents
  bin/hephaestus hep-upload <agent-folder>          # ask Cloud vs Hub before upload
  bin/hephaestus hep-global install                 # install Codex/Claude/Antigravity global router prompt blocks
  bin/hephaestus mcp serve              # stdio MCP server (hephaestus_route tool)

Examples:
  bin/hephaestus career-graph ingest --project .
  bin/hephaestus career-graph query "release failures" --project .
  bin/hephaestus career-graph public-card --write --project .
  bin/hephaestus ontology
  bin/hephaestus ontology --gui .
  bin/hephaestus ontology add ./company-docs --kind company --scope private
  bin/hephaestus wizard ./some-agent --name instagram-operator
  bin/hephaestus security scan ./some-agent --strict
  bin/hephaestus runtime bundle ./some-agent
EOF
}

if [[ "${1:-}" == "--version" || "${1:-}" == "-V" || "${1:-}" == "version" ]]; then
  release=""
  if [[ -f "$root/RELEASE" ]]; then
    release="$(tr -d '\r\n' < "$root/RELEASE")"
  elif [[ -f "$root/scripts/install-all-runtimes.sh" ]]; then
    release="$(sed -n 's/.*HEPHAESTUS_REF:-\(v[0-9.]*\)}.*/\1/p' "$root/scripts/install-all-runtimes.sh" | head -1)"
  fi
  if [[ -z "$release" ]]; then
    echo "$self: release metadata is missing." >&2
    exit 1
  fi
  printf '%s %s\n' "$self" "${release#v}"
  exit 0
fi

if [[ "${1:-}" == "" || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
  usage
  exit 0
fi

command="$1"
shift

case "$command" in
	  career-graph|career_graph)
	    run_python_module career_graph "$@"
	    ;;
	  ontology)
	    if [[ "${1:-}" == "add" ]]; then
	      shift
	      run_python_module ontology sources add "$@"
	      exit $?
	    fi

    open_flag=1
    if [[ "${1:-}" == "--gui" ]]; then
      shift
    fi
    if [[ "${1:-}" == "--no-open" ]]; then
      open_flag=0
      shift
    fi
	    project="${1:-.}"
	    if [[ "$open_flag" == "1" ]]; then
	      run_python_module ontology gui "$project"
	    else
	      run_python_module ontology gui "$project" --no-open
	    fi
	    ;;
	  wizard)
	    run_python_module agentlas_cloud wizard "$@"
	    ;;
	  security)
	    run_python_module agentlas_cloud security "$@"
	    ;;
  runtime)
    runtime_sub="${1:-}"
    shift || true
    case "$runtime_sub" in
	      bundle)
	        run_python_module agentlas_cloud bundle "$@"
	        ;;
	      read-agent-file)
	        run_python_module agentlas_cloud read-agent-file "$@"
	        ;;
      *)
        echo "$self: unknown runtime command: $runtime_sub" >&2
        usage >&2
        exit 2
        ;;
    esac
    ;;
	  field-test)
	    run_python_module agentlas_cloud field-test
	    ;;
	  auth)
	    run_python_module agentlas_cloud auth "$@"
	    ;;
	  doctor)
	    run_python_module agentlas_cloud doctor "$@"
	    ;;
	  update|hep-update)
	    run_python_module agentlas_cloud hep-update "$@"
	    ;;
	  global|hep-global|hephaestus-global)
	    run_python_module agentlas_cloud global "$@"
	    ;;
	  goal|hep-goal|hephaestus-goal|agentlas-goal)
	    # Goal plan tree with KPI states; /goal plus a plan, nothing more.
	    run_python_module agentlas_cloud goal "$@"
	    ;;
	  orch|hep-orch|hephaestus-orch|agentlas-orch)
	    # Which model runs the orchestrator, and which runs the workers.
	    # The allocator reads this policy before every role-split invocation;
	    # without it every worker inherited the orchestrator's frontier model.
	    exec "$(dirname "${BASH_SOURCE[0]}")/agentlas-one" orch "$@"
	    ;;
	  plugins)
	    run_python_module agentlas_cloud plugins "$@"
	    ;;
	  meta-agent|hep-build|hephaestus-build|Hephaestus-build|build)
	    # `session` is the deterministic local source/IR boundary used by the
	    # host-LM /hep-build session flow. Other build requests remain host-LLM
	    # orchestration and must not silently fall back to this CLI.
	    if [[ "${1:-}" == "session" ]]; then
	      shift
	      run_python_module agentlas_cloud session "$@"
	      exit $?
	    fi
	    request="${*:-Create or package an Agentlas-compatible agent.}"
	    escaped_request="${request//\\/\\\\}"
	    escaped_request="${escaped_request//\"/\\\"}"
	    escaped_request="${escaped_request//$'\n'/\\n}"
	    printf '{"status":"error","error":"host_llm_required","command":"hep-build","request":"%s","message":"hep-build requires a connected host LLM; run /hep-build in an installed Hephaestus host adapter"}\n' "$escaped_request" >&2
	    exit 3
	    ;;
	  session)
	    run_python_module agentlas_cloud session "$@"
	    ;;
	  command|network|cards|ao|route|mcp|workforce|local-gui|package|publish|project|context|contract|feature-map|revision)
	    # `contract` was the one subcommand the Python CLI exposed that this
	    # dispatcher never listed. It fell through to the natural-language
	    # router, which read the package path as a private path and refused
	    # (`unsafe_route_input`) — so the only machine-readable list of what a
	    # package is still missing was unreachable from every runtime adapter.
	    run_python_module agentlas_cloud "$command" "$@"
	    ;;
	  hep-network|hephaestus-network|hephaests-network)
	    if [[ "${HEPHAESTUS_LEGACY_ROUTER:-0}" == "1" ]]; then
	      # Explicit compatibility/debug opt-in only. The normal command path
	      # remains host-LLM staffing through the Workforce MCP contract below.
	      if [[ "${HEPHAESTUS_NETWORK_GUI_SHORTCUTS:-1}" != "0" ]]; then
	        set +e
	        gui_args=("$@" "--detach")
	        if [[ "${HEPHAESTUS_NETWORK_GUI_NO_OPEN:-0}" == "1" ]]; then
	          gui_args+=("--no-open")
	        fi
	        gui_output="$(run_python_module agentlas_cloud local-gui "${gui_args[@]}" 2>/dev/null)"
	        gui_status=$?
	        set -e
	        if [[ "$gui_status" == "0" ]]; then
	          printf '%s\n' "$gui_output"
	          exit 0
	        elif [[ "$gui_status" != "4" ]]; then
	          printf '%s\n' "$gui_output"
	          exit "$gui_status"
	        fi
	      fi
	      if [[ "${HEPHAESTUS_NETWORK_AUTO_RUN:-1}" == "0" ]]; then
	        run_python_module agentlas_cloud route "$@" --hub-only --scope network
	      else
	        run_python_module agentlas_cloud route "$@" --hub-only --scope network --auto-run --background
	      fi
	      exit $?
	    fi
	    printf '%s\n' '{"action":"workforce_orchestration_required","status":"host_llm_required","sourceScope":"network","sources":["local","cloud","hub"],"decisionOwner":"host_llm","legacyRouter":"disabled","detail":"Run /hep-network or @Hephaestus inside an MCP-capable host. Core federates source menus; the host LLM selects the team."}'
	    exit 3
	    ;;
	  connect|hep-connect|hephaestus-connect|agentlas-connect)
	    # Telegram Connect is a guided Desktop flow (registry: agentlas.connect is
	    # identity_only, no installed entrypoint). Until 2026-09-05 this verb fell
	    # through to the generic router and printed a route menu — a dead end for
	    # the person who typed it. A raised stop must carry its way out.
	    printf '%s\n' '{"action":"desktop_connect_required","status":"host_llm_required","command":"hep-connect","surface":"desktop","detail":"Telegram Connect runs in Agentlas Desktop → Connect (choose the agent, team, or group; connect the bot; pair a chat; send a test message). From Claude Code or Codex type /hep-connect for the guided flow. This shell form only points the way."}' >&2
	    exit 3
	    ;;
	  hep-local|hephaestus-local)
	    printf '%s\n' '{"action":"workforce_orchestration_required","status":"host_llm_required","sourceScope":"local","sources":["local"],"decisionOwner":"host_llm","legacyRouter":"disabled","detail":"Run /hep-local inside an MCP-capable host (Claude Code, Codex, Cursor, Antigravity, ...). Core federates the registered Local menu; the host LLM does the staffing, so this shell form has nothing to select with."}'
	    exit 3
	    ;;
	  hep-hub|hephaestus-hub)
	    printf '%s\n' '{"action":"workforce_orchestration_required","status":"host_llm_required","sourceScope":"hub","sources":["hub"],"decisionOwner":"host_llm","legacyRouter":"disabled","detail":"Run /hep-hub inside an MCP-capable host (Claude Code, Codex, Cursor, Antigravity, ...). Core federates the public Hub menu; the host LLM does the staffing, so this shell form has nothing to select with."}'
	    exit 3
	    ;;
		  stormbreaker)
		    run_python_module agentlas_cloud stormbreaker "$@"
		    ;;
		  hep-storm|hephaestus-storm|storm)
		    # The user-facing Storm shortcut follows the same Hub-first contract as
		    # hep-network. Operators who intentionally want local-card debug routing
		    # can still use the lower-level `stormbreaker run` command directly.
		    # set -u 아래에서 빈 배열을 "${a[@]}" 로 펴면 bash 3.2(맥 기본)가
		    # unbound variable 로 죽는다. 인자 없이 부른 사용자가 셸 내부 오류를
		    # 보게 되므로, 이 파일이 다른 곳에서 쓰는 것과 같은 보호 관용구를 쓴다.
		    storm_args=("$@")
		    storm_hub_only=1
		    for storm_arg in ${storm_args[@]+"${storm_args[@]}"}; do
		      if [[ "$storm_arg" == "--no-hub" ]]; then
		        storm_hub_only=0
		        break
		      fi
		    done
		    if [[ "$storm_hub_only" == "1" ]]; then
		      storm_args+=("--hub-only")
		    fi
		    run_python_module agentlas_cloud stormbreaker run ${storm_args[@]+"${storm_args[@]}"}
		    ;;
		  search|hep-search|hephaestus-search|Hephaestus-search)
		    run_python_module agentlas_cloud search "$@"
		    ;;
		  browser|hep-browser|hephaestus-browser|Hephaestus-browser)
		    run_python_module agentlas_cloud hep-browser "$@"
		    ;;
		  research)
		    run_python_module agentlas_cloud research "$@"
		    ;;
		  call|hep-call|hephaestus-call|Hephaestus-call)
		    run_python_module agentlas_cloud call "$@"
		    ;;
		  login|hep-login|agentlas-login)
		    # 로그인 전용 진입 — 브라우저 로그인 창을 열고 완료를 기다린다.
		    run_python_module agentlas_cloud auth login "$@"
		    ;;
		  cloud|hep-cloud|hephaestus-cloud)
		    printf '%s\n' '{"action":"workforce_orchestration_required","status":"host_llm_required","sourceScope":"cloud","sources":["cloud"],"decisionOwner":"host_llm","legacyRouter":"disabled","detail":"Run /hep-cloud inside an MCP-capable host (Claude Code, Codex, Cursor, Antigravity, ...). Core federates the owner Cloud menu; the host LLM does the staffing, so this shell form has nothing to select with."}'
		    exit 3
		    ;;
      upload|hep-upload|hephaestus-upload)
        run_upload_gate "$@"
        ;;
  -*)
    echo "$self: unknown command: $command" >&2
    usage >&2
    exit 2
    ;;
	  *)
	    # Natural-language shorthand: hephaestus "<request>" routes through the network router.
	    #
	    # A bare single word is a typo, not a request. This branch used to swallow
	    # every unrecognised token — `hephaestus login`, `status`, `doctorr`, `init`
	    # — turning it into a Hub search that exited 0 after 1-4s with no notice
	    # that the command did not exist. Two things broke: the surface reported
	    # success for something the user never asked for, and any script gating on
	    # the exit code could not detect a typo or a renamed subcommand. Measured
	    # 2026-08-03 on 1.1.95: 21 of 28 probed tokens exited 0 this way, and every
	    # one of them was a single word with no whitespace. Real natural-language
	    # requests are phrases, so require one.
	    #
	    # Sub-command typos under a real command are already rejected correctly by
	    # that command's own parser (`auth statuss` -> exit 2). This makes the
	    # top-level dispatcher agree with the level below it.
	    if [[ $# -eq 0 && "$command" != *[[:space:]]* ]]; then
	      echo "$self: '$command' is not a hephaestus command." >&2
	      echo "See: $self --help  ·  to route it as a request: $self \"$command …\"" >&2
	      exit 2
	    fi
	    # A word this launcher does not own, but the sibling CLI does, is a
	    # wrong-tool mistake — not a request. The single-word guard above never
	    # sees it because these are typed with a subcommand (`graph list`),
	    # and the router then answered with Hub candidates at exit 0: a list of
	    # marketplace agents that reads exactly like the list of saved
	    # automations the user asked for (audit F9-4). Name the right tool.
	    case "$command" in
	      graph|automation|automations)
	        echo "$self: '$command' belongs to the agentlas CLI, not hephaestus." >&2
	        echo "Try: agentlas $command $*" >&2
	        exit 2
	        ;;
	    esac
	    # Whitespace-only input is not a request either.
	    if [[ -z "${command// /}" && $# -eq 0 ]]; then
	      echo "$self: no command given." >&2
	      usage >&2
	      exit 2
	    fi
	    run_python_module agentlas_cloud route "$command $*" --allow-local-routing
	    ;;
esac
