fix: Fixed the bash prompt utils so that they correctly show output when being run by a tool invocation

This commit is contained in:
2026-02-17 17:19:42 -07:00
parent 3894c98b5b
commit 948466d771
3 changed files with 55 additions and 145 deletions
+42 -132
View File
@@ -7,126 +7,18 @@ export AUTO_CONFIRM=true
# @env LLM_OUTPUT=/dev/stdout # @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=. # @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() { _project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}" local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${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 # @cmd Get project information and structure
get_project_info() { get_project_info() {
local project_dir local project_dir
project_dir=$(_project_dir) project_dir=$(_project_dir)
info "Project: ${project_dir}" >> "$LLM_OUTPUT" info "Project: ${project_dir}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT"
@@ -147,17 +39,17 @@ get_project_info() {
run_build() { run_build() {
local project_dir local project_dir
project_dir=$(_project_dir) project_dir=$(_project_dir)
local project_info local project_info
project_info=$(detect_project "${project_dir}") project_info=$(detect_project "${project_dir}")
local build_cmd local build_cmd
build_cmd=$(echo "${project_info}" | jq -r '.build') build_cmd=$(echo "${project_info}" | jq -r '.build')
if [[ -z "${build_cmd}" ]] || [[ "${build_cmd}" == "null" ]]; then if [[ -z "${build_cmd}" ]] || [[ "${build_cmd}" == "null" ]]; then
warn "No build command detected for this project" >> "$LLM_OUTPUT" warn "No build command detected for this project" >> "$LLM_OUTPUT"
return 0 return 0
fi fi
info "Running: ${build_cmd}" >> "$LLM_OUTPUT" info "Running: ${build_cmd}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT"
@@ -177,17 +69,17 @@ run_build() {
run_tests() { run_tests() {
local project_dir local project_dir
project_dir=$(_project_dir) project_dir=$(_project_dir)
local project_info local project_info
project_info=$(detect_project "${project_dir}") project_info=$(detect_project "${project_dir}")
local test_cmd local test_cmd
test_cmd=$(echo "${project_info}" | jq -r '.test') test_cmd=$(echo "${project_info}" | jq -r '.test')
if [[ -z "${test_cmd}" ]] || [[ "${test_cmd}" == "null" ]]; then if [[ -z "${test_cmd}" ]] || [[ "${test_cmd}" == "null" ]]; then
warn "No test command detected for this project" >> "$LLM_OUTPUT" warn "No test command detected for this project" >> "$LLM_OUTPUT"
return 0 return 0
fi fi
info "Running: ${test_cmd}" >> "$LLM_OUTPUT" info "Running: ${test_cmd}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT"
@@ -210,13 +102,13 @@ search_files() {
local pattern="${argc_pattern}" local pattern="${argc_pattern}"
local project_dir local project_dir
project_dir=$(_project_dir) project_dir=$(_project_dir)
info "Searching for: ${pattern}" >> "$LLM_OUTPUT" info "Searching for: ${pattern}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT"
local results local results
results=$(search_files "${pattern}" "${project_dir}") results=$(search_files "${pattern}" "${project_dir}")
if [[ -n "${results}" ]]; then if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT" echo "${results}" >> "$LLM_OUTPUT"
else else
@@ -232,21 +124,21 @@ search_content() {
local file_type="${argc_file_type:-}" local file_type="${argc_file_type:-}"
local project_dir local project_dir
project_dir=$(_project_dir) project_dir=$(_project_dir)
info "Searching for: ${pattern}" >> "$LLM_OUTPUT" info "Searching for: ${pattern}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT" echo "" >> "$LLM_OUTPUT"
local grep_args="-rn" local grep_args="-rn"
if [[ -n "${file_type}" ]]; then if [[ -n "${file_type}" ]]; then
grep_args="${grep_args} --include=*.${file_type}" grep_args="${grep_args} --include=*.${file_type}"
fi fi
local results local results
# shellcheck disable=SC2086 # shellcheck disable=SC2086
results=$(grep ${grep_args} "${pattern}" "${project_dir}" 2>/dev/null | \ results=$(grep ${grep_args} "${pattern}" "${project_dir}" 2>/dev/null | \
grep -v '/target/' | grep -v '/node_modules/' | grep -v '/.git/' | \ grep -v '/target/' | grep -v '/node_modules/' | grep -v '/.git/' | \
head -30) || true head -30) || true
if [[ -n "${results}" ]]; then if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT" echo "${results}" >> "$LLM_OUTPUT"
else else
@@ -254,17 +146,27 @@ search_content() {
fi 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 --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() { ask_user() {
# shellcheck disable=SC2154 # shellcheck disable=SC2154
local question="${argc_question}" local question="${argc_question}"
# shellcheck disable=SC2154 # 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[@]}" 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" error "No options provided for ask_user" >> "$LLM_OUTPUT"
return 1 return 1
fi fi
@@ -272,7 +174,7 @@ ask_user() {
info "Asking user: ${question}" >> "$LLM_OUTPUT" info "Asking user: ${question}" >> "$LLM_OUTPUT"
local selected_index local selected_index
selected_index=$(list "${question}" "${opts[@]}") selected_index=$(list "$question" "${opts[@]}" 2>/dev/tty)
local selected_label="${opts[$selected_index]}" local selected_label="${opts[$selected_index]}"
@@ -290,7 +192,7 @@ ask_user_confirm() {
info "Asking user: ${question}" >> "$LLM_OUTPUT" info "Asking user: ${question}" >> "$LLM_OUTPUT"
local result local result
result=$(confirm "${question}") result=$(confirm "${question}" 2>/dev/tty)
if [[ "${result}" == "1" ]]; then if [[ "${result}" == "1" ]]; then
echo "User confirmed: yes" >> "$LLM_OUTPUT" 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. # @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 --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() { ask_user_checkbox() {
# shellcheck disable=SC2154 # shellcheck disable=SC2154
local question="${argc_question}" local question="${argc_question}"
# shellcheck disable=SC2154 # 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[@]}" local opts_count="${#opts[@]}"
if [[ "${opts_count}" -eq 0 ]]; then if [[ "${opts_count}" -eq 0 ]]; then
@@ -317,7 +227,7 @@ ask_user_checkbox() {
info "Asking user (select multiple): ${question}" >> "$LLM_OUTPUT" info "Asking user (select multiple): ${question}" >> "$LLM_OUTPUT"
local checked_indices local checked_indices
checked_indices=$(checkbox "${question}" "${opts[@]}") checked_indices=$(checkbox "${question}" "${opts[@]}" 2>/dev/tty)
local selected_labels=() local selected_labels=()
for idx in ${checked_indices}; do for idx in ${checked_indices}; do
@@ -345,7 +255,7 @@ ask_user_input() {
info "Asking user: ${question}" >> "$LLM_OUTPUT" info "Asking user: ${question}" >> "$LLM_OUTPUT"
local user_text local user_text
user_text=$(input "${question}") user_text=$(input "${question}" 2>/dev/tty)
cat <<-EOF >> "$LLM_OUTPUT" cat <<-EOF >> "$LLM_OUTPUT"
User input: ${user_text} User input: ${user_text}
+2 -2
View File
@@ -121,7 +121,7 @@ _cursor_blink_off() {
} }
_cursor_to() { _cursor_to() {
echo -en "\033[$1;$2H" >&2 echo -en "\033[$1;${2:-1}H" >&2
} }
# shellcheck disable=SC2154 # shellcheck disable=SC2154
@@ -133,7 +133,7 @@ _key_input() {
_read_stdin -rsn2 b _read_stdin -rsn2 b
fi fi
declare input="${a}${b}" declare input="${a}${b:-}"
case "$input" in case "$input" in
"${ESC}[A" | "k") echo up ;; "${ESC}[A" | "k") echo up ;;
"${ESC}[B" | "j") echo down ;; "${ESC}[B" | "j") echo down ;;
+11 -11
View File
@@ -66,12 +66,12 @@ Prompt for text input
**Example With Validation:** **Example With Validation:**
```bash ```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:** **Example Without Validation:**
```bash ```bash
text=$(input "Please enter something:") text=$(input "Please enter something:" 2>/dev/tty)
``` ```
### confirm ### confirm
@@ -81,7 +81,7 @@ Show a confirm dialog with options for yes/no
**Example:** **Example:**
```bash ```bash
confirmed=$(confirm "Do the thing?") confirmed=$(confirm "Do the thing?" 2>/dev/tty)
if [[ $confirmed == "0" ]]; then echo "No"; else echo "Yes"; fi if [[ $confirmed == "0" ]]; then echo "No"; else echo "Yes"; fi
``` ```
@@ -94,7 +94,7 @@ keys that then returns the chosen option.
**Example:** **Example:**
```bash ```bash
options=("one" "two" "three" "four") 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]}" echo "Your choice: ${options[$choice]}"
``` ```
@@ -107,7 +107,7 @@ and enter keys that then returns the chosen options.
**Example:** **Example:**
```bash ```bash
options=("one" "two" "three" "four") 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}" echo "Your choices: ${checked}"
``` ```
@@ -124,12 +124,12 @@ validate_password() {
exit 1 exit 1
fi 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:** **Example Without Validation:**
```bash ```bash
pass="$(password "Enter your password:")" pass="$(password "Enter your password:" 2>/dev/tty)"
``` ```
### editor ### editor
@@ -137,7 +137,7 @@ Open the default editor (`$EDITOR`); if none is set, default back to `vi`
**Example:** **Example:**
```bash ```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}" echo -e "You wrote:\n${text}"
``` ```
@@ -150,7 +150,7 @@ validation functions returns 0.
**Example:** **Example:**
```bash ```bash
# Using the built-in 'validate_present' validator # 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 # Using a custom validator; e.g. for password
validate_password() { validate_password() {
@@ -159,7 +159,7 @@ validate_password() {
exit 1 exit 1
fi fi
} }
pass=$(with_validate 'password "Enter random password"' validate_password) pass=$(with_validate 'password "Enter random password"' validate_password 2>/dev/tty)
``` ```
### validate_present ### validate_present
@@ -169,7 +169,7 @@ Validate that the prompt returned a value.
**Example:** **Example:**
```bash ```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 ### detect_os