From 948466d7716c30d074d0e5d5cd96a102eb9d6691 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Tue, 17 Feb 2026 17:19:42 -0700 Subject: [PATCH] fix: Fixed the bash prompt utils so that they correctly show output when being run by a tool invocation --- assets/agents/sisyphus/tools.sh | 174 +++++-------------- assets/functions/utils/prompt-utils.sh | 4 +- docs/function-calling/BASH-PROMPT-HELPERS.md | 22 +-- 3 files changed, 55 insertions(+), 145 deletions(-) diff --git a/assets/agents/sisyphus/tools.sh b/assets/agents/sisyphus/tools.sh index ef7c97b..7d8e70f 100755 --- a/assets/agents/sisyphus/tools.sh +++ b/assets/agents/sisyphus/tools.sh @@ -7,126 +7,18 @@ export AUTO_CONFIRM=true # @env LLM_OUTPUT=/dev/stdout # @env LLM_AGENT_VAR_PROJECT_DIR=. -# @describe Sisyphus orchestrator tools for delegating to specialized agents +# @describe Sisyphus orchestrator tools (project info, build, test, user interaction) _project_dir() { local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}" (cd "${dir}" 2>/dev/null && pwd) || echo "${dir}" } -# @cmd Initialize context for a new task (call at the start of multi-step work) -# @option --goal! Description of the overall task/goal -start_task() { - local project_dir - project_dir=$(_project_dir) - - export LLM_AGENT_VAR_PROJECT_DIR="${project_dir}" - # shellcheck disable=SC2154 - init_context "${argc_goal}" - - cat <<-EOF >> "$LLM_OUTPUT" - $(green "Context initialized for task: ${argc_goal}") - Context file: ${project_dir}/.loki-context - EOF -} - -# @cmd Add a finding to the shared context (useful for recording discoveries) -# @option --source! Source of the finding (e.g., "manual", "explore", "coder") -# @option --finding! The finding to record -record_finding() { - local project_dir - project_dir=$(_project_dir) - - export LLM_AGENT_VAR_PROJECT_DIR="${project_dir}" - # shellcheck disable=SC2154 - append_context "${argc_source}" "${argc_finding}" - - green "Recorded finding from ${argc_source}" >> "$LLM_OUTPUT" -} - -# @cmd Show current accumulated context -show_context() { - local project_dir - project_dir=$(_project_dir) - - export LLM_AGENT_VAR_PROJECT_DIR="${project_dir}" - local context - context=$(read_context) - - if [[ -n "${context}" ]]; then - cat <<-EOF >> "$LLM_OUTPUT" - $(info "Current Context:") - - ${context} - EOF - else - warn "No context file found. Use start_task to initialize." >> "$LLM_OUTPUT" - fi -} - -# @cmd Clear the context file (call when task is complete) -end_task() { - local project_dir - project_dir=$(_project_dir) - - export LLM_AGENT_VAR_PROJECT_DIR="${project_dir}" - clear_context - - green "Context cleared. Task complete." >> "$LLM_OUTPUT" -} - -# @cmd Delegate a task to a specialized agent -# @option --agent! Agent to delegate to: explore, coder, or oracle -# @option --task! Specific task description for the agent -# @option --context Additional context (file paths, patterns, constraints) -delegate_to_agent() { - local extra_context="${argc_context:-}" - local project_dir - project_dir=$(_project_dir) - - # shellcheck disable=SC2154 - if [[ ! "${argc_agent}" =~ ^(explore|coder|oracle)$ ]]; then - error "Invalid agent: ${argc_agent}. Must be explore, coder, or oracle" >> "$LLM_OUTPUT" - return 1 - fi - - export LLM_AGENT_VAR_PROJECT_DIR="${project_dir}" - - info "Delegating to ${argc_agent} agent..." >> "$LLM_OUTPUT" - echo "" >> "$LLM_OUTPUT" - - # shellcheck disable=SC2154 - local prompt="${argc_task}" - if [[ -n "${extra_context}" ]]; then - prompt="$(printf "%s\n\nAdditional Context:\n%s\n" "${argc_task}" "${extra_context}")" - fi - - cat <<-EOF >> "$LLM_OUTPUT" - $(cyan "------------------------------------------") - DELEGATING TO: ${argc_agent} - TASK: ${argc_task} - $(cyan "------------------------------------------") - - EOF - - local output - output=$(invoke_agent_with_summary "${argc_agent}" "${prompt}" \ - --agent-variable project_dir "${project_dir}" 2>&1) || true - - cat <<-EOF >> "$LLM_OUTPUT" - ${output} - - $(cyan "------------------------------------------") - DELEGATION COMPLETE: ${argc_agent} - $(cyan "------------------------------------------") - EOF -} - # @cmd Get project information and structure get_project_info() { local project_dir project_dir=$(_project_dir) - + info "Project: ${project_dir}" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT" @@ -147,17 +39,17 @@ get_project_info() { run_build() { local project_dir project_dir=$(_project_dir) - + local project_info project_info=$(detect_project "${project_dir}") local build_cmd build_cmd=$(echo "${project_info}" | jq -r '.build') - + if [[ -z "${build_cmd}" ]] || [[ "${build_cmd}" == "null" ]]; then warn "No build command detected for this project" >> "$LLM_OUTPUT" return 0 fi - + info "Running: ${build_cmd}" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT" @@ -177,17 +69,17 @@ run_build() { run_tests() { local project_dir project_dir=$(_project_dir) - + local project_info project_info=$(detect_project "${project_dir}") local test_cmd test_cmd=$(echo "${project_info}" | jq -r '.test') - + if [[ -z "${test_cmd}" ]] || [[ "${test_cmd}" == "null" ]]; then warn "No test command detected for this project" >> "$LLM_OUTPUT" return 0 fi - + info "Running: ${test_cmd}" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT" @@ -210,13 +102,13 @@ search_files() { local pattern="${argc_pattern}" local project_dir project_dir=$(_project_dir) - + info "Searching for: ${pattern}" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT" local results results=$(search_files "${pattern}" "${project_dir}") - + if [[ -n "${results}" ]]; then echo "${results}" >> "$LLM_OUTPUT" else @@ -232,21 +124,21 @@ search_content() { local file_type="${argc_file_type:-}" local project_dir project_dir=$(_project_dir) - + info "Searching for: ${pattern}" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT" - + local grep_args="-rn" if [[ -n "${file_type}" ]]; then grep_args="${grep_args} --include=*.${file_type}" fi - + local results # shellcheck disable=SC2086 results=$(grep ${grep_args} "${pattern}" "${project_dir}" 2>/dev/null | \ grep -v '/target/' | grep -v '/node_modules/' | grep -v '/.git/' | \ head -30) || true - + if [[ -n "${results}" ]]; then echo "${results}" >> "$LLM_OUTPUT" else @@ -254,17 +146,27 @@ search_content() { fi } -# @cmd Ask the user to select ONE option from a list. The first option should be your recommended choice; append '(Recommended)' to its label. Returns the selected option's label text. +# @cmd Ask the user to select ONE option from a list. The first option should be your recommended choice — append '(Recommended)' to its label. Returns the selected option's label text. # @option --question! The question to present to the user -# @option --options+ The list of options to present (first option = recommended, append '(Recommended)' to its label) +# @option --options! The JSON array of options to present (first option = recommended, append '(Recommended)' to its label) ask_user() { # shellcheck disable=SC2154 local question="${argc_question}" + # shellcheck disable=SC2154 - local opts=("${argc_options[@]}") + local options_json="${argc_options}" + + local opts=() + + # Convert JSON array string -> Bash array + if ! mapfile -t opts < <(jq -r '.[]' <<<"$options_json"); then + error "Invalid JSON in argc_options" >> "$LLM_OUTPUT" + return 1 + fi + local opts_count="${#opts[@]}" - if [[ "${opts_count}" -eq 0 ]]; then + if [[ "$opts_count" -eq 0 ]]; then error "No options provided for ask_user" >> "$LLM_OUTPUT" return 1 fi @@ -272,7 +174,7 @@ ask_user() { info "Asking user: ${question}" >> "$LLM_OUTPUT" local selected_index - selected_index=$(list "${question}" "${opts[@]}") + selected_index=$(list "$question" "${opts[@]}" 2>/dev/tty) local selected_label="${opts[$selected_index]}" @@ -290,7 +192,7 @@ ask_user_confirm() { info "Asking user: ${question}" >> "$LLM_OUTPUT" local result - result=$(confirm "${question}") + result=$(confirm "${question}" 2>/dev/tty) if [[ "${result}" == "1" ]]; then echo "User confirmed: yes" >> "$LLM_OUTPUT" @@ -301,12 +203,20 @@ ask_user_confirm() { # @cmd Ask the user to select MULTIPLE options from a list (checkbox). Returns the labels of all selected items. # @option --question! The question to present to the user -# @option --options+ The list of options the user can select from (multiple selections allowed) +# @option --options! The JSON array of options to present ask_user_checkbox() { # shellcheck disable=SC2154 local question="${argc_question}" # shellcheck disable=SC2154 - local opts=("${argc_options[@]}") + local options_json="${argc_options}" + + local opts=() + + # Convert JSON array string -> Bash array + if ! mapfile -t opts < <(jq -r '.[]' <<<"$options_json"); then + error "Invalid JSON in argc_options" >> "$LLM_OUTPUT" + return 1 + fi local opts_count="${#opts[@]}" if [[ "${opts_count}" -eq 0 ]]; then @@ -317,7 +227,7 @@ ask_user_checkbox() { info "Asking user (select multiple): ${question}" >> "$LLM_OUTPUT" local checked_indices - checked_indices=$(checkbox "${question}" "${opts[@]}") + checked_indices=$(checkbox "${question}" "${opts[@]}" 2>/dev/tty) local selected_labels=() for idx in ${checked_indices}; do @@ -345,7 +255,7 @@ ask_user_input() { info "Asking user: ${question}" >> "$LLM_OUTPUT" local user_text - user_text=$(input "${question}") + user_text=$(input "${question}" 2>/dev/tty) cat <<-EOF >> "$LLM_OUTPUT" User input: ${user_text} diff --git a/assets/functions/utils/prompt-utils.sh b/assets/functions/utils/prompt-utils.sh index 934967f..14badfd 100755 --- a/assets/functions/utils/prompt-utils.sh +++ b/assets/functions/utils/prompt-utils.sh @@ -121,7 +121,7 @@ _cursor_blink_off() { } _cursor_to() { - echo -en "\033[$1;$2H" >&2 + echo -en "\033[$1;${2:-1}H" >&2 } # shellcheck disable=SC2154 @@ -133,7 +133,7 @@ _key_input() { _read_stdin -rsn2 b fi - declare input="${a}${b}" + declare input="${a}${b:-}" case "$input" in "${ESC}[A" | "k") echo up ;; "${ESC}[B" | "j") echo down ;; diff --git a/docs/function-calling/BASH-PROMPT-HELPERS.md b/docs/function-calling/BASH-PROMPT-HELPERS.md index b2d3ed4..bf0c891 100644 --- a/docs/function-calling/BASH-PROMPT-HELPERS.md +++ b/docs/function-calling/BASH-PROMPT-HELPERS.md @@ -66,12 +66,12 @@ Prompt for text input **Example With Validation:** ```bash -text=$(with_validation 'input "Please enter something:"' validate_present) +text=$(with_validation 'input "Please enter something:"' validate_present 2>/dev/tty) ``` **Example Without Validation:** ```bash -text=$(input "Please enter something:") +text=$(input "Please enter something:" 2>/dev/tty) ``` ### confirm @@ -81,7 +81,7 @@ Show a confirm dialog with options for yes/no **Example:** ```bash -confirmed=$(confirm "Do the thing?") +confirmed=$(confirm "Do the thing?" 2>/dev/tty) if [[ $confirmed == "0" ]]; then echo "No"; else echo "Yes"; fi ``` @@ -94,7 +94,7 @@ keys that then returns the chosen option. **Example:** ```bash options=("one" "two" "three" "four") -choice=$(list "Select an item" "${options[@]}") +choice=$(list "Select an item" "${options[@]}" 2>/dev/tty) echo "Your choice: ${options[$choice]}" ``` @@ -107,7 +107,7 @@ and enter keys that then returns the chosen options. **Example:** ```bash options=("one" "two" "three" "four") -checked=$(checkbox "Select one or more items" "${options[@]}") +checked=$(checkbox "Select one or more items" "${options[@]}" 2>/dev/tty) echo "Your choices: ${checked}" ``` @@ -124,12 +124,12 @@ validate_password() { exit 1 fi } -pass=$(with_validate 'password "Enter your password"' validate_password) +pass=$(with_validate 'password "Enter your password"' validate_password 2>/dev/tty) ``` **Example Without Validation:** ```bash -pass="$(password "Enter your password:")" +pass="$(password "Enter your password:" 2>/dev/tty)" ``` ### editor @@ -137,7 +137,7 @@ Open the default editor (`$EDITOR`); if none is set, default back to `vi` **Example:** ```bash -text=$(editor "Please enter something in the editor") +text=$(editor "Please enter something in the editor" 2>/dev/tty) echo -e "You wrote:\n${text}" ``` @@ -150,7 +150,7 @@ validation functions returns 0. **Example:** ```bash # Using the built-in 'validate_present' validator -text=$(with_validate 'input "Please enter something and confirm with enter"' validate_present) +text=$(with_validate 'input "Please enter something and confirm with enter"' validate_present 2>/dev/tty) # Using a custom validator; e.g. for password validate_password() { @@ -159,7 +159,7 @@ validate_password() { exit 1 fi } -pass=$(with_validate 'password "Enter random password"' validate_password) +pass=$(with_validate 'password "Enter random password"' validate_password 2>/dev/tty) ``` ### validate_present @@ -169,7 +169,7 @@ Validate that the prompt returned a value. **Example:** ```bash -text=$(with_validate 'input "Please enter something and confirm with enter"' validate_present) +text=$(with_validate 'input "Please enter something and confirm with enter"' validate_present 2>/dev/tty) ``` ### detect_os