From 444c4b410907a434a423f0ff29aa45ea78e0e50a Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Fri, 10 Oct 2025 21:45:30 +0000 Subject: [PATCH 1/6] attempt 1 --- Dockerfile | 18 +++---------- README.md | 24 +++++++++++++++++ build.yaml | 1 - rootfs/etc/ha-cli/.repl_rc | 54 ++++++++++++++++++++++++++++++++++++++ rootfs/usr/bin/cli.sh | 35 +++++++++++------------- 5 files changed, 98 insertions(+), 34 deletions(-) create mode 100644 rootfs/etc/ha-cli/.repl_rc diff --git a/Dockerfile b/Dockerfile index 372f780..91c2f79 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,20 +7,10 @@ SHELL ["/bin/ash", "-o", "pipefail", "-c"] ARG BUILD_ARCH WORKDIR /usr/src -# Install rlwrap -ARG RLWRAP_VERSION -RUN apk add --no-cache --virtual .build-deps \ - build-base \ - readline-dev \ - ncurses-dev \ - && curl -L -s "https://github.com/hanslub42/rlwrap/releases/download/${RLWRAP_VERSION}/rlwrap-${RLWRAP_VERSION}.tar.gz" \ - | tar zxvf - -C /usr/src/ \ - && cd rlwrap-${RLWRAP_VERSION} \ - && ./configure \ - && make \ - && make install \ - && apk del .build-deps \ - && rm -rf /usr/src/* +# Install dependencies +RUN apk add --no-cache \ + bash-completion +# No longer need rlwrap; base image already provides Bash/readline # Install CLI ARG CLI_VERSION diff --git a/README.md b/README.md index ede7df1..b8623ab 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,27 @@ # CLI for the Home Assistant Operating System This is for the Home Assistant Operating System and is the login shell. + +## REPL behavior + +- Interactive prompt is a Bash-based REPL (no rlwrap required) with history persisted to `/tmp/.cli_history`. +- You can type commands without the leading `ha`; e.g. `core info` is equivalent to `ha core info`. +- Type `login` to exit to the underlying OS shell, or `exit` to quit. + +## Bash completion + +- Tab completion is available inside the REPL. +- The REPL loads the official completion by evaluating `ha completion bash` at startup and wires Tab to use it. +- If `ha completion bash` isn't supported by the installed `ha` binary, the REPL runs without completion. + +To inspect or use the completion outside the REPL, you can generate it directly: + +``` +ha completion bash +``` + +You can add it to your environment by evaluating it in your shell profile, for example: + +``` +eval "$(ha completion bash)" +``` diff --git a/build.yaml b/build.yaml index 19a085f..2b53e28 100644 --- a/build.yaml +++ b/build.yaml @@ -13,7 +13,6 @@ cosign: identity: https://github.com/home-assistant/plugin-cli/.* args: CLI_VERSION: 4.41.0 - RLWRAP_VERSION: 0.46.1 labels: io.hass.type: cli org.opencontainers.image.title: Home Assistant CLI Plugin diff --git a/rootfs/etc/ha-cli/.repl_rc b/rootfs/etc/ha-cli/.repl_rc new file mode 100644 index 0000000..c401cab --- /dev/null +++ b/rootfs/etc/ha-cli/.repl_rc @@ -0,0 +1,54 @@ +# ha REPL rcfile +# shopt -s histappend +# shopt -s checkwinsize +# shopt -s progcomp 2>/dev/null || true + +# History configuration +HISTFILE=/tmp/.cli_history +HISTSIZE=1000 +HISTFILESIZE=2000 +PROMPT_COMMAND='history -a; history -c; history -r' + +# Prompt +PS1='\[\e[32m\]ha > \[\e[0m\]' + +# Friendly helpers +help() { + echo 'Note: Use "login" to enter operating system shell' + echo '--------------------------------' + ha help +} +login() { + echo 'Entering OS shell...' + exit 10; + } + +# Load ha completion if available +if command -v ha >/dev/null 2>&1; then + if [ -f /etc/bash_completion.d/ha ]; then + # Load from standard location + . /etc/bash_completion.d/ha + + # Treat 'ha' as implied: use ha's completion for bare subcommands too + # Common generators define either __start_ha (Cobra) or _ha (generic) + if declare -F __start_ha >/dev/null 2>&1; then + complete -D -F __start_ha + elif declare -F _ha >/dev/null 2>&1; then + complete -D -F _ha + fi + fi +else + echo 'Warning: ha command not found; commands will fail' >&2 +fi + +# Route unknown commands to ha (supports shorthand like: core info) +command_not_found_handle() { + if command -v ha >/dev/null 2>&1; then + ha "$@" + return $? + fi + echo "bash: $1: command not found" >&2 + return 127 +} + +echo 'Tip: You can omit the leading "ha". For example: core info' \ No newline at end of file diff --git a/rootfs/usr/bin/cli.sh b/rootfs/usr/bin/cli.sh index 67d5dba..d71b720 100755 --- a/rootfs/usr/bin/cli.sh +++ b/rootfs/usr/bin/cli.sh @@ -1,24 +1,21 @@ #!/bin/bash -ha banner || true +# Print banner (best-effort) +# ha banner || true -# Run CLI -COMMAND="" -while true; do - COMMAND="$(rlwrap -S $'\e[32mha > \e[0m' -H /tmp/.cli_history -o cat)" +# Create a minimal rcfile to drive an interactive Bash as our REPL +RC_FILE=/etc/ha-cli/.repl_rc +HISTFILE=/tmp/.cli_history - # Abort to host? - if [ "$COMMAND" == "help" ]; then - echo "Note: Use \"login\" to enter operating system shell" - elif [ "$COMMAND" == "login" ]; then - exit 10 - elif [ "$COMMAND" == "exit" ]; then - exit - elif [ -z "${COMMAND##ha *}" ]; then - echo "Note: Leading 'ha' is not necessary in this HA CLI" - COMMAND=$(echo "$COMMAND" | cut -b 3-) - fi +# Ensure history file exists +touch "$HISTFILE" 2>/dev/null || true +if command -v ha >/dev/null 2>&1; then + completion_file_content=$(ha completion bash 2>/dev/null || true) + if [ -n "$completion_file_content" ]; then + echo "$completion_file_content" >/etc/bash_completion.d/ha 2>/dev/null || true + fi +fi - echo "$COMMAND" | xargs -o ha - echo "" -done + +# Start interactive bash with our rcfile; no user profiles +exec bash --noprofile --rcfile "$RC_FILE" -i From f7b0f8f33bf2f2829c807f91612f77c4efa7b041 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 11 Oct 2025 00:16:49 +0000 Subject: [PATCH 2/6] Enhance REPL help and command completion for 'ha' CLI --- rootfs/etc/ha-cli/.repl_rc | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/rootfs/etc/ha-cli/.repl_rc b/rootfs/etc/ha-cli/.repl_rc index c401cab..2426449 100644 --- a/rootfs/etc/ha-cli/.repl_rc +++ b/rootfs/etc/ha-cli/.repl_rc @@ -15,6 +15,7 @@ PS1='\[\e[32m\]ha > \[\e[0m\]' # Friendly helpers help() { echo 'Note: Use "login" to enter operating system shell' + echo '' echo '--------------------------------' ha help } @@ -28,14 +29,27 @@ if command -v ha >/dev/null 2>&1; then if [ -f /etc/bash_completion.d/ha ]; then # Load from standard location . /etc/bash_completion.d/ha - - # Treat 'ha' as implied: use ha's completion for bare subcommands too - # Common generators define either __start_ha (Cobra) or _ha (generic) + # Treat 'ha' as implied for completion: wrap to inject 'ha' at position 0 + # Use the reference-generated function name __start_ha when available if declare -F __start_ha >/dev/null 2>&1; then - complete -D -F __start_ha - elif declare -F _ha >/dev/null 2>&1; then - complete -D -F _ha + __start_ha_implicit() { + local __save_words __save_cword + __save_words=("${COMP_WORDS[@]}") + __save_cword=${COMP_CWORD} + COMP_WORDS=(ha "${COMP_WORDS[@]}") + ((COMP_CWORD++)) + __start_ha + COMP_WORDS=("${__save_words[@]}") + COMP_CWORD=${__save_cword} + } + # Use default completion for unknown commands so bare subcommands complete + if [[ $(type -t compopt) = builtin ]]; then + complete -D -o default -F __start_ha_implicit + else + complete -D -o default -o nospace -F __start_ha_implicit + fi fi + fi else echo 'Warning: ha command not found; commands will fail' >&2 From 96c4141d10e747610b7c281fb8ca80af42d33864 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 11 Oct 2025 20:14:41 +0000 Subject: [PATCH 3/6] Enhance bash completion for 'ha' CLI by improving wrapper function and handling of command line inputs --- rootfs/etc/ha-cli/.repl_rc | 71 ++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/rootfs/etc/ha-cli/.repl_rc b/rootfs/etc/ha-cli/.repl_rc index 2426449..034d8bb 100644 --- a/rootfs/etc/ha-cli/.repl_rc +++ b/rootfs/etc/ha-cli/.repl_rc @@ -24,45 +24,56 @@ login() { exit 10; } -# Load ha completion if available +# Load bash-completion core and ha completion if available if command -v ha >/dev/null 2>&1; then if [ -f /etc/bash_completion.d/ha ]; then - # Load from standard location + # Load from standard location (native HA completion registers itself for 'ha') . /etc/bash_completion.d/ha - # Treat 'ha' as implied for completion: wrap to inject 'ha' at position 0 - # Use the reference-generated function name __start_ha when available + + # Wrapper function to inject 'ha' as first argument for all completions if declare -F __start_ha >/dev/null 2>&1; then - __start_ha_implicit() { - local __save_words __save_cword - __save_words=("${COMP_WORDS[@]}") - __save_cword=${COMP_CWORD} - COMP_WORDS=(ha "${COMP_WORDS[@]}") - ((COMP_CWORD++)) + __start_ha >/dev/null 2>&1 || true + + # Clear ALL existing completions to ensure our wrapper is used + while read -r cmd; do + complete -r "$cmd" 2>/dev/null || true + done < <(complete -p | awk '{print $NF}') + + # Universal completion handler - wraps everything as 'ha ...' + __ha_repl_complete() { + local cur prev words cword + + # Handle empty command line specially + if [[ ${#COMP_WORDS[@]} -eq 0 || ( ${#COMP_WORDS[@]} -eq 1 && -z "${COMP_WORDS[0]}" ) ]]; then + COMP_LINE="ha " + COMP_POINT=3 + COMP_WORDS=(ha "") + COMP_CWORD=1 + else + # Prepend 'ha' to existing words + COMP_LINE="ha $COMP_LINE" + COMP_POINT=$((COMP_POINT + 3)) + COMP_WORDS=(ha "${COMP_WORDS[@]}") + COMP_CWORD=$((COMP_CWORD + 1)) + fi + + COMPREPLY=() __start_ha - COMP_WORDS=("${__save_words[@]}") - COMP_CWORD=${__save_cword} } - # Use default completion for unknown commands so bare subcommands complete - if [[ $(type -t compopt) = builtin ]]; then - complete -D -o default -F __start_ha_implicit - else - complete -D -o default -o nospace -F __start_ha_implicit - fi - fi + + # Register our completion as the default for EVERYTHING + complete -D -F __ha_repl_complete # Default for any command + complete -E -F __ha_repl_complete # Empty command line + complete -I -F __ha_repl_complete 2>/dev/null || true # Initial word (bash 5+) + fi fi + + # Note: __start_ha is defined by the ha completion script + + # Use native completion only; no implicit injection or extra bindings. else echo 'Warning: ha command not found; commands will fail' >&2 fi -# Route unknown commands to ha (supports shorthand like: core info) -command_not_found_handle() { - if command -v ha >/dev/null 2>&1; then - ha "$@" - return $? - fi - echo "bash: $1: command not found" >&2 - return 127 -} - -echo 'Tip: You can omit the leading "ha". For example: core info' \ No newline at end of file +# Use commands with explicit 'ha' prefix; native completion supports 'ha ...' \ No newline at end of file From c61e204478ae5535f97aee326571f7490dbf6f6d Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 11 Oct 2025 20:17:23 +0000 Subject: [PATCH 4/6] Refactor .repl_rc by removing unused shell options and ensuring unknown commands are handled by 'ha' --- rootfs/etc/ha-cli/.repl_rc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rootfs/etc/ha-cli/.repl_rc b/rootfs/etc/ha-cli/.repl_rc index 034d8bb..148564e 100644 --- a/rootfs/etc/ha-cli/.repl_rc +++ b/rootfs/etc/ha-cli/.repl_rc @@ -1,7 +1,5 @@ # ha REPL rcfile -# shopt -s histappend -# shopt -s checkwinsize -# shopt -s progcomp 2>/dev/null || true + # History configuration HISTFILE=/tmp/.cli_history @@ -12,6 +10,13 @@ PROMPT_COMMAND='history -a; history -c; history -r' # Prompt PS1='\[\e[32m\]ha > \[\e[0m\]' +# Intercept unknown commands and pass them to 'ha' +command_not_found_handle() { + # Execute as 'ha ' + ha "$@" + return $? +} + # Friendly helpers help() { echo 'Note: Use "login" to enter operating system shell' From abfe7877ffbaf32b2f2b8dd38f9c0ba8f160812c Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 11 Oct 2025 20:19:17 +0000 Subject: [PATCH 5/6] Uncomment banner display in cli.sh for better visibility --- rootfs/usr/bin/cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootfs/usr/bin/cli.sh b/rootfs/usr/bin/cli.sh index d71b720..7482ceb 100755 --- a/rootfs/usr/bin/cli.sh +++ b/rootfs/usr/bin/cli.sh @@ -1,7 +1,7 @@ #!/bin/bash # Print banner (best-effort) -# ha banner || true +ha banner || true # Create a minimal rcfile to drive an interactive Bash as our REPL RC_FILE=/etc/ha-cli/.repl_rc From f74ef44f961a3712ca5c8c883ab9a3a63fb5899d Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 11 Oct 2025 20:28:19 +0000 Subject: [PATCH 6/6] Get rid if AI README --- README.md | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/README.md b/README.md index b8623ab..87acb3d 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,3 @@ # CLI for the Home Assistant Operating System -This is for the Home Assistant Operating System and is the login shell. - -## REPL behavior - -- Interactive prompt is a Bash-based REPL (no rlwrap required) with history persisted to `/tmp/.cli_history`. -- You can type commands without the leading `ha`; e.g. `core info` is equivalent to `ha core info`. -- Type `login` to exit to the underlying OS shell, or `exit` to quit. - -## Bash completion - -- Tab completion is available inside the REPL. -- The REPL loads the official completion by evaluating `ha completion bash` at startup and wires Tab to use it. -- If `ha completion bash` isn't supported by the installed `ha` binary, the REPL runs without completion. - -To inspect or use the completion outside the REPL, you can generate it directly: - -``` -ha completion bash -``` - -You can add it to your environment by evaluating it in your shell profile, for example: - -``` -eval "$(ha completion bash)" -``` +This is for the Home Assistant Operating System and is the login shell. \ No newline at end of file