Files

1064 lines
26 KiB
Bash
Executable File

#!/bin/bash
set -o pipefail
OMARCHY_BIN_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
METADATA_SCAN_LIMIT=80
COMMAND_KEYS=()
ROUTE_COLLISIONS=()
declare -A COMMAND_ROUTE
declare -A COMMAND_FALLBACK_ROUTE
declare -A COMMAND_BINARY
declare -A COMMAND_GROUP
declare -A COMMAND_NAME
declare -A COMMAND_SUMMARY
declare -A COMMAND_USAGE
declare -A COMMAND_ARGS
declare -A COMMAND_EXAMPLES
declare -A COMMAND_REQUIRES_SUDO
declare -A COMMAND_HIDDEN
declare -A COMMAND_ALIASES
declare -A COMMAND_HAS_SUMMARY
declare -A COMMAND_METADATA_ERRORS
declare -A ROUTE_TO_KEY
declare -A ROUTE_IS_ALIAS
declare -A BINARY_TO_KEY
declare -A GROUP_DESCRIPTIONS
GROUP_DESCRIPTIONS[ac]="AC power detection"
GROUP_DESCRIPTIONS[battery]="Battery status helpers"
GROUP_DESCRIPTIONS[branch]="Omarchy git branch management"
GROUP_DESCRIPTIONS[branding]="About and screensaver branding"
GROUP_DESCRIPTIONS[brightness]="Display and keyboard brightness"
GROUP_DESCRIPTIONS[capture]="Screenshots and screen recording"
GROUP_DESCRIPTIONS[channel]="Omarchy release channel management"
GROUP_DESCRIPTIONS[cmd]="Command and shortcut helpers"
GROUP_DESCRIPTIONS[config]="System configuration helpers"
GROUP_DESCRIPTIONS[debug]="Diagnostics and support logs"
GROUP_DESCRIPTIONS[default]="Default application selection"
GROUP_DESCRIPTIONS[dev]="Omarchy development tools"
GROUP_DESCRIPTIONS[drive]="Drive selection and encryption"
GROUP_DESCRIPTIONS[font]="Font management"
GROUP_DESCRIPTIONS[hibernation]="Hibernation setup and removal"
GROUP_DESCRIPTIONS[hook]="User hook runner"
GROUP_DESCRIPTIONS[hw]="Hardware detection and controls"
GROUP_DESCRIPTIONS[hyprland]="Hyprland window, monitor, and toggle controls"
GROUP_DESCRIPTIONS[install]="Optional software installers"
GROUP_DESCRIPTIONS[launch]="Application launchers"
GROUP_DESCRIPTIONS[menu]="Omarchy menu commands"
GROUP_DESCRIPTIONS[migrate]="Migration runner"
GROUP_DESCRIPTIONS[notification]="Notification helpers"
GROUP_DESCRIPTIONS[npx]="NPX package wrappers"
GROUP_DESCRIPTIONS[pkg]="Package management helpers"
GROUP_DESCRIPTIONS[plymouth]="Plymouth boot theme management"
GROUP_DESCRIPTIONS[powerprofiles]="Power profile management"
GROUP_DESCRIPTIONS[refresh]="Reset config to defaults"
GROUP_DESCRIPTIONS[reinstall]="Reinstall and reset workflows"
GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders"
GROUP_DESCRIPTIONS[remove]="Removal workflows"
GROUP_DESCRIPTIONS[restart]="Restart Omarchy components"
GROUP_DESCRIPTIONS[setup]="Interactive setup wizards"
GROUP_DESCRIPTIONS[screensaver]="Screensaver branding and animation"
GROUP_DESCRIPTIONS[snapshot]="System snapshots"
GROUP_DESCRIPTIONS[sudo]="Sudo configuration helpers"
GROUP_DESCRIPTIONS[swayosd]="SwayOSD status display helpers"
GROUP_DESCRIPTIONS[system]="Reboot, shutdown, logout, and lock"
GROUP_DESCRIPTIONS[theme]="Theme management"
GROUP_DESCRIPTIONS[toggle]="Toggle Omarchy features"
GROUP_DESCRIPTIONS[transcode]="Image and video transcoding"
GROUP_DESCRIPTIONS[tui]="Terminal UI launchers"
GROUP_DESCRIPTIONS[tz]="Timezone selection"
GROUP_DESCRIPTIONS[update]="Omarchy and system updates"
GROUP_DESCRIPTIONS[version]="Version and channel information"
GROUP_DESCRIPTIONS[voxtype]="Voxtype dictation"
GROUP_DESCRIPTIONS[weather]="Weather status"
GROUP_DESCRIPTIONS[webapp]="Web app launchers"
GROUP_DESCRIPTIONS[wifi]="Wi-Fi helpers"
GROUP_DESCRIPTIONS[windows]="Windows VM management"
join_words() {
local separator="$1"
shift
local joined=""
for word in "$@"; do
if [[ -z $joined ]]; then
joined="$word"
else
joined+="$separator$word"
fi
done
printf '%s' "$joined"
}
append_pipe_value() {
local current="$1"
local addition="$2"
if [[ -z $current ]]; then
printf '%s' "$addition"
else
printf '%s|%s' "$current" "$addition"
fi
}
register_route() {
local route="$1"
local key="$2"
local is_alias="${3:-false}"
[[ -z $route || -z $key ]] && return
if [[ -n ${ROUTE_TO_KEY[$route]} && ${ROUTE_TO_KEY[$route]} != "$key" ]]; then
ROUTE_COLLISIONS+=("$route -> ${ROUTE_TO_KEY[$route]} conflicts with $key")
return
fi
ROUTE_TO_KEY["$route"]="$key"
if [[ $is_alias == "true" ]]; then
ROUTE_IS_ALIAS["$route"]="true"
fi
}
register_command() {
local file="$1"
local file_binary="${file##*/}"
local group=""
local name=""
local summary=""
local usage=""
local binary=""
local args=""
local examples=""
local aliases=""
local requires_sudo=""
local hidden=""
local line=""
local metadata_key=""
local metadata_value=""
local line_count=0
local name_seen="false"
local fallback_summary=""
local has_summary="false"
local metadata_errors=""
while IFS= read -r line && (( line_count < METADATA_SCAN_LIMIT )); do
line_count=$((line_count + 1))
if (( line_count == 1 )) && [[ $line == "#!"* ]]; then
continue
fi
if [[ $line =~ ^[[:space:]]*$ ]]; then
continue
fi
if [[ ! $line =~ ^[[:space:]]*# ]]; then
break
fi
if [[ $line =~ ^[[:space:]]*#[[:space:]]*omarchy:([[:alnum:]_-]+)=(.*)$ ]]; then
metadata_key="${BASH_REMATCH[1]}"
metadata_value="${BASH_REMATCH[2]}"
metadata_value="${metadata_value%$'\r'}"
metadata_value="${metadata_value//$'\t'/ }"
case "$metadata_key" in
group)
group="$metadata_value"
;;
name)
name="$metadata_value"
name_seen="true"
;;
summary)
summary="$metadata_value"
[[ -n $metadata_value ]] && has_summary="true"
;;
args)
args="$metadata_value"
;;
examples)
examples="$metadata_value"
;;
alias | aliases)
aliases="$metadata_value"
;;
requires-sudo)
requires_sudo="$metadata_value"
[[ $metadata_value == "true" ]] || metadata_errors=$(append_pipe_value "$metadata_errors" "requires-sudo must be omitted or true")
;;
hidden)
hidden="$metadata_value"
[[ $metadata_value == "true" ]] || metadata_errors=$(append_pipe_value "$metadata_errors" "hidden must be omitted or true")
;;
*)
;;
esac
elif [[ -z $fallback_summary && $line =~ ^[[:space:]]*#[[:space:]]*(.+)$ ]]; then
fallback_summary="${BASH_REMATCH[1]}"
fallback_summary="${fallback_summary%$'\r'}"
fallback_summary="${fallback_summary//$'\t'/ }"
if [[ -z $fallback_summary || $fallback_summary == omarchy:* ]]; then
fallback_summary=""
fi
fi
done <"$file"
local stem="${file_binary#omarchy-}"
local fallback_group="$stem"
local fallback_name=""
local fallback_route="omarchy ${stem//-/ }"
if [[ $stem == *-* ]]; then
fallback_group="${stem%%-*}"
fallback_name="${stem#*-}"
fallback_name="${fallback_name//-/ }"
fi
[[ -z $binary ]] && binary="$file_binary"
[[ -z $group ]] && group="$fallback_group"
[[ $name_seen != "true" ]] && name="$fallback_name"
[[ -z $summary && -n $fallback_summary ]] && summary="$fallback_summary"
[[ -z $summary ]] && summary="Run the ${stem//-/ } command"
local route="omarchy $group"
if [[ -n $name ]]; then
route+=" $name"
fi
if [[ -z $usage ]]; then
usage="$route"
if [[ -n $args ]]; then
usage+=" $args"
fi
fi
[[ $requires_sudo == "true" ]] || requires_sudo="false"
[[ $hidden == "true" ]] || hidden="false"
local key="$file_binary"
COMMAND_KEYS+=("$key")
COMMAND_ROUTE["$key"]="$route"
COMMAND_FALLBACK_ROUTE["$key"]="$fallback_route"
COMMAND_BINARY["$key"]="$binary"
COMMAND_GROUP["$key"]="$group"
COMMAND_NAME["$key"]="$name"
COMMAND_SUMMARY["$key"]="$summary"
COMMAND_USAGE["$key"]="$usage"
COMMAND_ARGS["$key"]="$args"
COMMAND_EXAMPLES["$key"]="$examples"
COMMAND_REQUIRES_SUDO["$key"]="$requires_sudo"
COMMAND_HIDDEN["$key"]="$hidden"
COMMAND_HAS_SUMMARY["$key"]="$has_summary"
COMMAND_METADATA_ERRORS["$key"]="$metadata_errors"
BINARY_TO_KEY["$binary"]="$key"
register_route "$route" "$key"
register_route "$fallback_route" "$key"
local old_ifs="$IFS"
local alias_route=""
IFS='|'
for alias_route in $aliases; do
alias_route="${alias_route# }"
alias_route="${alias_route% }"
[[ -z $alias_route || $alias_route == "$route" ]] && continue
register_route "$alias_route" "$key" true
COMMAND_ALIASES["$key"]=$(append_pipe_value "${COMMAND_ALIASES[$key]}" "$alias_route")
done
IFS="$old_ifs"
}
load_commands() {
local file=""
for file in "$OMARCHY_BIN_DIR"/omarchy-*; do
[[ -f $file && -x $file ]] || continue
register_command "$file"
done
}
load_command_by_binary() {
local binary="$1"
local file="$OMARCHY_BIN_DIR/$binary"
[[ -f $file && -x $file ]] || return 1
register_command "$file"
}
load_child_commands_by_binary() {
local binary="$1"
local file=""
for file in "$OMARCHY_BIN_DIR/$binary"-*; do
[[ -f $file && -x $file ]] || continue
register_command "$file"
done
}
group_has_child_commands() {
local group="$1"
local file=""
for file in "$OMARCHY_BIN_DIR/omarchy-$group"-*; do
[[ -f $file && -x $file ]] && return 0
done
return 1
}
group_has_binary_commands() {
local group="$1"
[[ -x $OMARCHY_BIN_DIR/omarchy-$group ]] && return 0
group_has_child_commands "$group"
}
command_requires_args() {
local key="$1"
local args="${COMMAND_ARGS[$key]}"
local required="$args"
while [[ $required =~ ^(.*)\[[^][]*\](.*)$ ]]; do
required="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
done
required="${required// /}"
[[ -n $required ]]
}
load_group_commands() {
local group="$1"
local file=""
if [[ -f $OMARCHY_BIN_DIR/omarchy-$group && -x $OMARCHY_BIN_DIR/omarchy-$group ]]; then
register_command "$OMARCHY_BIN_DIR/omarchy-$group"
fi
for file in "$OMARCHY_BIN_DIR/omarchy-$group"-*; do
[[ -f $file && -x $file ]] || continue
register_command "$file"
done
}
resolve_direct_route() {
local argc="$1"
shift
local args=("$@")
local prefix_count=0
local route=""
local binary=""
local candidate=""
for (( prefix_count = argc; prefix_count >= 1; prefix_count-- )); do
route="omarchy $(join_words " " "${args[@]:0:prefix_count}")"
binary="omarchy-$(join_words "-" "${args[@]:0:prefix_count}")"
candidate="$OMARCHY_BIN_DIR/$binary"
if [[ -f $candidate && -x $candidate ]]; then
DIRECT_RESOLVED_BINARY="$binary"
DIRECT_RESOLVED_COUNT="$prefix_count"
DIRECT_RESOLVED_ROUTE="$route"
return 0
fi
done
return 1
}
sorted_keys() {
local include_all="$1"
local key=""
for key in "${COMMAND_KEYS[@]}"; do
if [[ $include_all != "true" && ${COMMAND_HIDDEN[$key]} == "true" ]]; then
continue
fi
printf '%s\t%s\n' "${COMMAND_ROUTE[$key]}" "$key"
done | sort -u | cut -f2-
}
fallback_group_for_key() {
local key="$1"
local fallback="${COMMAND_FALLBACK_ROUTE[$key]#omarchy }"
printf '%s' "${fallback%% *}"
}
command_route_for_group() {
local key="$1"
local group="$2"
if [[ $(fallback_group_for_key "$key") == "$group" && ${COMMAND_GROUP[$key]} != "$group" ]]; then
printf '%s' "${COMMAND_FALLBACK_ROUTE[$key]}"
else
printf '%s' "${COMMAND_ROUTE[$key]}"
fi
}
command_usage_for_group() {
local key="$1"
local group="$2"
local route=""
route=$(command_route_for_group "$key" "$group")
if [[ -n ${COMMAND_ARGS[$key]} ]]; then
route+=" ${COMMAND_ARGS[$key]}"
fi
printf '%s' "$route"
}
sorted_group_keys() {
local group="$1"
local include_all="$2"
local key=""
local fallback_group=""
local route=""
for key in "${COMMAND_KEYS[@]}"; do
if [[ $include_all != "true" && ${COMMAND_HIDDEN[$key]} == "true" ]]; then
continue
fi
fallback_group=$(fallback_group_for_key "$key")
if [[ ${COMMAND_GROUP[$key]} != "$group" && $fallback_group != "$group" ]]; then
continue
fi
route=$(command_route_for_group "$key" "$group")
printf '%s\t%s\n' "$route" "$key"
done | sort -u | cut -f2-
}
examples_as_lines() {
local examples="$1"
local old_ifs="$IFS"
local example=""
IFS='|'
for example in $examples; do
example="${example# }"
example="${example% }"
[[ -n $example ]] && printf '%s\n' "$example"
done
IFS="$old_ifs"
}
pipe_values_as_lines() {
local values="$1"
local old_ifs="$IFS"
local value=""
IFS='|'
for value in $values; do
value="${value# }"
value="${value% }"
[[ -n $value ]] && printf '%s\n' "$value"
done
IFS="$old_ifs"
}
show_group_list() {
local sorted_group=""
printf '%s\n' "${!GROUP_DESCRIPTIONS[@]}" | sort | while IFS= read -r sorted_group; do
[[ -n $sorted_group ]] || continue
printf ' %-14s %s\n' "$sorted_group" "${GROUP_DESCRIPTIONS[$sorted_group]}"
done
}
show_main_help() {
cat <<'EOF'
Omarchy command center
Usage:
omarchy <command> [args...]
omarchy commands [--all] [--json] [--check]
omarchy <group> --help
omarchy <group> <command> --help
Common commands:
omarchy update Update Omarchy and system packages
omarchy theme list List available themes
omarchy theme set <name> Apply a theme
omarchy font list List available fonts
omarchy screenshot Take a screenshot
omarchy debug Print debugging information
Groups:
EOF
show_group_list
cat <<'EOF'
Discovery:
omarchy commands List all commands
omarchy commands --all Include commands explicitly marked hidden
omarchy commands --json Machine-readable command list
omarchy commands --check Validate command metadata and routes
EOF
}
show_commands_help() {
cat <<'EOF'
Usage:
omarchy commands [--all] [--json] [--markdown] [--check]
List commands known to the Omarchy command center.
Options:
--all Include commands explicitly marked hidden
--json Emit machine-readable JSON
--markdown Emit a Markdown command table
--check Validate command metadata and route collisions
EOF
}
print_command_table() {
local rows="$1"
local max_width=0
local width=0
local command=""
local rest=""
local summary=""
while IFS=$'\t' read -r command rest; do
[[ -n $command ]] || continue
width=${#command}
(( width > max_width )) && max_width=$width
done <<<"$rows"
while IFS=$'\t' read -r command rest; do
[[ -n $command ]] || continue
printf ' %-*s %s\n' "$max_width" "$command" "$rest"
done <<<"$rows"
}
show_commands() {
local include_all="$1"
local key=""
local route=""
local usage=""
local rows=""
local alias_rows=""
if [[ $include_all == "true" ]]; then
echo "Omarchy commands (all):"
else
echo "Omarchy commands:"
fi
while IFS= read -r key; do
[[ -n $key ]] || continue
usage="${COMMAND_USAGE[$key]}"
rows+="$usage"$'\t'"${COMMAND_SUMMARY[$key]}"$'\n'
done < <(sorted_keys "$include_all")
print_command_table "$rows"
for route in "${!ROUTE_IS_ALIAS[@]}"; do
key="${ROUTE_TO_KEY[$route]}"
if [[ $include_all != "true" && ${COMMAND_HIDDEN[$key]} == "true" ]]; then
continue
fi
alias_rows+="$route"$'\t'"${COMMAND_ROUTE[$key]}"$'\n'
done
if [[ -n $alias_rows ]]; then
echo ""
echo "Aliases:"
print_command_table "$(printf '%s' "$alias_rows" | sort)"
fi
}
markdown_escape() {
local value="$1"
value="${value//|/\\|}"
printf '%s' "$value"
}
show_commands_markdown() {
local include_all="$1"
local key=""
echo "| Command | Binary | Summary |"
echo "| --- | --- | --- |"
while IFS= read -r key; do
[[ -n $key ]] || continue
printf '| `%s` | `%s` | %s |\n' \
"$(markdown_escape "${COMMAND_USAGE[$key]}")" \
"$(markdown_escape "${COMMAND_BINARY[$key]}")" \
"$(markdown_escape "${COMMAND_SUMMARY[$key]}")"
done < <(sorted_keys "$include_all")
}
emit_command_records() {
local include_all="$1"
local key=""
while IFS= read -r key; do
[[ -n $key ]] || continue
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"${COMMAND_ROUTE[$key]}" \
"${COMMAND_BINARY[$key]}" \
"${COMMAND_GROUP[$key]}" \
"${COMMAND_NAME[$key]}" \
"${COMMAND_SUMMARY[$key]}" \
"${COMMAND_REQUIRES_SUDO[$key]}" \
"${COMMAND_HIDDEN[$key]}" \
"${COMMAND_ARGS[$key]}" \
"${COMMAND_EXAMPLES[$key]}" \
"${COMMAND_ALIASES[$key]}" \
"${COMMAND_FALLBACK_ROUTE[$key]}" \
"${COMMAND_USAGE[$key]}"
done < <(sorted_keys "$include_all")
}
commands_json_filter() {
cat <<'EOF'
[inputs | split("\t") | {
route: .[0],
binary: .[1],
group: .[2],
name: .[3],
summary: .[4],
requires_sudo: (.[5] == "true"),
hidden: (.[6] == "true"),
args: .[7],
examples: (.[8] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))),
aliases: (.[9] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))),
filename_route: .[10],
routes: ([.[0], .[10]] + (.[9] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))) | unique)
}] | {ok: true, commands: .}
EOF
}
show_commands_json() {
local include_all="$1"
emit_command_records "$include_all" | jq -Rn "$(commands_json_filter)"
}
show_commands_check() {
local failures=0
local collision=""
local key=""
local error=""
for collision in "${ROUTE_COLLISIONS[@]}"; do
echo "Route collision: $collision" >&2
failures=$((failures + 1))
done
for key in "${COMMAND_KEYS[@]}"; do
if [[ ${COMMAND_HAS_SUMMARY[$key]} != "true" ]]; then
echo "Missing metadata summary: ${COMMAND_BINARY[$key]}" >&2
failures=$((failures + 1))
fi
if [[ -n ${COMMAND_METADATA_ERRORS[$key]} ]]; then
while IFS= read -r error; do
[[ -n $error ]] || continue
echo "Invalid metadata in ${COMMAND_BINARY[$key]}: $error" >&2
failures=$((failures + 1))
done < <(pipe_values_as_lines "${COMMAND_METADATA_ERRORS[$key]}")
fi
if [[ ! -x $OMARCHY_BIN_DIR/${COMMAND_BINARY[$key]} ]]; then
echo "Missing binary: ${COMMAND_BINARY[$key]}" >&2
failures=$((failures + 1))
fi
done
if (( failures > 0 )); then
echo "Command metadata check failed ($failures issues)" >&2
return 1
fi
echo "Command metadata check passed (${#COMMAND_KEYS[@]} commands)"
}
show_command_json() {
local key="$1"
printf '%s\n' "$key" | while IFS= read -r key; do
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"${COMMAND_ROUTE[$key]}" \
"${COMMAND_BINARY[$key]}" \
"${COMMAND_GROUP[$key]}" \
"${COMMAND_NAME[$key]}" \
"${COMMAND_SUMMARY[$key]}" \
"${COMMAND_REQUIRES_SUDO[$key]}" \
"${COMMAND_HIDDEN[$key]}" \
"${COMMAND_ARGS[$key]}" \
"${COMMAND_EXAMPLES[$key]}" \
"${COMMAND_ALIASES[$key]}" \
"${COMMAND_FALLBACK_ROUTE[$key]}" \
"${COMMAND_USAGE[$key]}"
done | jq -Rn "$(commands_json_filter) | {ok: true, command: .commands[0]}"
}
parse_commands_args() {
local include_all="false"
local json="false"
local markdown="false"
local check="false"
shift
while (( $# > 0 )); do
case "$1" in
--all)
include_all="true"
;;
--json)
json="true"
;;
--markdown)
markdown="true"
;;
--check)
check="true"
;;
--help | -h)
show_commands_help
return 0
;;
*)
echo "Unknown option for omarchy commands: $1" >&2
show_commands_help >&2
return 2
;;
esac
shift
done
if [[ $check == "true" ]]; then
show_commands_check
elif [[ $json == "true" ]]; then
show_commands_json "$include_all"
elif [[ $markdown == "true" ]]; then
show_commands_markdown "$include_all"
else
show_commands "$include_all"
fi
}
group_exists() {
local group="$1"
local key=""
for key in "${COMMAND_KEYS[@]}"; do
if [[ ${COMMAND_GROUP[$key]} == "$group" && -n ${COMMAND_NAME[$key]} ]]; then
return 0
fi
done
return 1
}
show_group_help() {
local group="$1"
local include_all="${2:-false}"
local key=""
local rows=""
local title="${GROUP_DESCRIPTIONS[$group]}"
if [[ -n $title ]]; then
echo "${group^} commands — $title:"
else
echo "${group^} commands:"
fi
while IFS= read -r key; do
[[ -n $key ]] || continue
rows+="$(command_usage_for_group "$key" "$group")"$'\t'$'\t'"${COMMAND_SUMMARY[$key]}"$'\n'
done < <(sorted_group_keys "$group" "$include_all")
if [[ -n $rows ]]; then
print_command_table "$rows"
else
echo " No documented commands found. Try: omarchy commands --all"
fi
}
show_prefix_help() {
local prefix="$1"
local prefix_with_space="$prefix "
local key=""
local rows=""
while IFS= read -r key; do
[[ -n $key ]] || continue
if [[ ${COMMAND_USAGE[$key]} == $prefix_with_space* ]]; then
rows+="${COMMAND_USAGE[$key]}"$'\t'$'\t'"${COMMAND_SUMMARY[$key]}"$'\n'
fi
done < <(sorted_keys false)
[[ -n $rows ]] || return 1
echo "${prefix#omarchy } commands:"
print_command_table "$rows"
}
show_related_commands() {
local key="$1"
local group="${COMMAND_GROUP[$key]}"
local child_key=""
local rows=""
[[ -z ${COMMAND_NAME[$key]} ]] || return
while IFS= read -r child_key; do
[[ -n $child_key && $child_key != "$key" ]] || continue
rows+="$(command_usage_for_group "$child_key" "$group")"$'\t'$'\t'"${COMMAND_SUMMARY[$child_key]}"$'\n'
done < <(sorted_group_keys "$group" false)
if [[ -n $rows ]]; then
echo ""
echo "Related commands:"
print_command_table "$rows"
fi
}
show_command_help() {
local key="$1"
local aliases="${COMMAND_ALIASES[$key]}"
local examples="${COMMAND_EXAMPLES[$key]}"
local args="${COMMAND_ARGS[$key]}"
local example=""
local alias=""
echo "Usage:"
echo " ${COMMAND_USAGE[$key]}"
echo ""
echo "${COMMAND_SUMMARY[$key]}"
if [[ -n $args ]]; then
echo ""
echo "Arguments:"
echo " $args"
fi
if [[ -n $examples ]]; then
echo ""
echo "Examples:"
while IFS= read -r example; do
printf ' %s\n' "$example"
done < <(examples_as_lines "$examples")
fi
if [[ -n $aliases ]]; then
echo ""
echo "Aliases:"
while IFS= read -r alias; do
printf ' %s\n' "$alias"
done < <(pipe_values_as_lines "$aliases")
fi
echo ""
echo "Binary:"
echo " ${COMMAND_BINARY[$key]}"
if [[ ${COMMAND_FALLBACK_ROUTE[$key]} != "${COMMAND_ROUTE[$key]}" ]]; then
echo ""
echo "Filename route:"
echo " ${COMMAND_FALLBACK_ROUTE[$key]}"
fi
show_related_commands "$key"
}
resolve_route() {
local argc="$1"
shift
local args=("$@")
local prefix_count=0
local route=""
local key=""
for (( prefix_count = argc; prefix_count >= 1; prefix_count-- )); do
route="omarchy $(join_words " " "${args[@]:0:prefix_count}")"
key="${ROUTE_TO_KEY[$route]}"
if [[ -n $key ]]; then
RESOLVED_KEY="$key"
RESOLVED_COUNT="$prefix_count"
RESOLVED_ROUTE="$route"
return 0
fi
done
return 1
}
suggest_command() {
local first="$1"
local candidate=""
for candidate in "${!ROUTE_TO_KEY[@]}"; do
candidate="${candidate#omarchy }"
if [[ $candidate == "$first"* ]]; then
printf '%s' "${candidate%% *}"
return 0
fi
done
return 1
}
dispatch_fast_or_help() {
local args=("$@")
local remaining=()
local key=""
local binary_path=""
if resolve_direct_route "$#" "${args[@]}"; then
remaining=("${args[@]:DIRECT_RESOLVED_COUNT}")
binary_path="$OMARCHY_BIN_DIR/$DIRECT_RESOLVED_BINARY"
if (( ${#remaining[@]} > 0 )) && [[ ${remaining[0]} == "--help" || ${remaining[0]} == "-h" ]]; then
if ! load_command_by_binary "$DIRECT_RESOLVED_BINARY"; then
echo "Binary is missing or not executable: $DIRECT_RESOLVED_BINARY" >&2
return 127
fi
if (( DIRECT_RESOLVED_COUNT == 1 )); then
load_child_commands_by_binary "$DIRECT_RESOLVED_BINARY"
fi
key="${BINARY_TO_KEY[$DIRECT_RESOLVED_BINARY]}"
if [[ " ${remaining[*]} " == *" --json "* ]]; then
show_command_json "$key"
else
show_command_help "$key"
fi
return 0
fi
if (( ${#remaining[@]} == 0 )); then
load_command_by_binary "$DIRECT_RESOLVED_BINARY"
key="${BINARY_TO_KEY[$DIRECT_RESOLVED_BINARY]}"
if [[ -n $key ]] && command_requires_args "$key"; then
if (( DIRECT_RESOLVED_COUNT == 1 )) && group_has_child_commands "$1"; then
load_group_commands "$1"
show_group_help "$1"
else
show_command_help "$key"
fi
return 0
fi
fi
exec "$binary_path" "${remaining[@]}"
fi
if (( $# == 1 )) && group_has_binary_commands "$1"; then
load_group_commands "$1"
show_group_help "$1"
return 0
fi
if (( $# >= 2 )) && [[ $2 == "--help" || $2 == "-h" ]] && group_has_binary_commands "$1"; then
load_group_commands "$1"
show_group_help "$1"
return 0
fi
load_commands
dispatch_or_help "$@"
}
dispatch_or_help() {
local args=("$@")
local remaining=()
local key=""
local binary_path=""
local suggestion=""
if resolve_route "$#" "${args[@]}"; then
key="$RESOLVED_KEY"
remaining=("${args[@]:RESOLVED_COUNT}")
if (( ${#remaining[@]} > 0 )) && [[ ${remaining[0]} == "--help" || ${remaining[0]} == "-h" ]]; then
if [[ " ${remaining[*]} " == *" --json "* ]]; then
show_command_json "$key"
else
show_command_help "$key"
fi
return 0
fi
binary_path="$OMARCHY_BIN_DIR/${COMMAND_BINARY[$key]}"
if [[ ! -x $binary_path ]]; then
echo "Binary is missing or not executable: ${COMMAND_BINARY[$key]}" >&2
return 127
fi
if (( ${#remaining[@]} == 0 )) && command_requires_args "$key"; then
if (( RESOLVED_COUNT == 1 )) && group_exists "$1"; then
show_group_help "$1"
else
show_command_help "$key"
fi
return 0
fi
exec "$binary_path" "${remaining[@]}"
fi
if (( $# == 1 )) && group_exists "$1"; then
show_group_help "$1"
return 0
fi
if (( $# >= 2 )) && [[ $2 == "--help" || $2 == "-h" ]] && group_exists "$1"; then
show_group_help "$1"
return 0
fi
if show_prefix_help "omarchy ${args[*]}"; then
return 0
fi
echo "Unknown Omarchy command: omarchy ${args[*]}" >&2
suggestion=$(suggest_command "${args[0]}")
if [[ -n $suggestion ]]; then
echo "Did you mean: omarchy $suggestion ?" >&2
fi
echo "Run 'omarchy commands --all' to discover available commands." >&2
return 127
}
main() {
if (( $# == 0 )); then
show_main_help
return 0
fi
case "$1" in
--help | -h)
show_main_help
;;
commands)
load_commands
parse_commands_args "$@"
;;
*)
dispatch_fast_or_help "$@"
;;
esac
}
main "$@"