Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions home/.chezmoitemplates/lib/common/install-prelude.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,32 @@ function require_command() {
return 1
}
}

#
# @description True when any argument is a non-empty string. Pass an install
# array expanded with the ${arr[@]-} form so an unset array stays safe.
# @arg $@ Candidate entries.
# @exitcode 0 At least one non-empty entry is present.
# @exitcode 1 Every entry is empty or absent.
#
function have_any() {
local item
for item in "$@"; do
[[ -n "${item}" ]] && return 0
done
return 1
}

#
# @description Warn with a count summary, then list the failed items on stderr.
# Keeps per-library wording by taking the tag and a noun phrase from the caller.
# @arg $1 Log tag, for example "uv".
# @arg $2 Noun phrase, for example "tool(s) failed to install".
# @arg $@ Remaining args: the names of the items that failed.
#
function report_failures() {
local tag="$1" summary="$2"
shift 2
log_warn "[${tag}] ${#} ${summary}:"
printf ' - %s\n' "$@" >&2
}
31 changes: 5 additions & 26 deletions home/.chezmoitemplates/lib/install/ai-mcp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,13 @@

set -Eeuo pipefail

#
# @description True when AI_MCP holds at least one non-empty entry.
# @exitcode 0 A server is present.
# @exitcode 1 No servers.
#
function _ai_mcp_have_any() {
local entry
for entry in "${AI_MCP[@]-}"; do
[[ -z "${entry}" ]] && continue
return 0
done
return 1
}

#
# @description Warn about the MCP servers that failed to register.
# @arg $@ string Names of the servers that failed.
#
function _ai_mcp_report_failures() {
log_warn "[ai-mcp] ${#} server(s) failed to register:"
printf ' - %s\n' "$@" >&2
}

#
# @description Register each MCP server on Claude Code, skipping existing ones.
# @exitcode 0 Registered, or nothing to do.
# @exitcode 1 The claude CLI is missing.
#
function ai_mcp_install_main() {
if ! _ai_mcp_have_any; then
if ! have_any "${AI_MCP[@]-}"; then
log_info "[ai-mcp] No MCP servers to register."
return 0
fi
Expand Down Expand Up @@ -69,7 +46,7 @@ function ai_mcp_install_main() {
done

if ((${#failed[@]} > 0)); then
_ai_mcp_report_failures "${failed[@]}"
report_failures ai-mcp "server(s) failed to register" "${failed[@]}"
fi

log_info "[ai-mcp] MCP servers registered."
Expand All @@ -83,9 +60,11 @@ function main() {
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# When run directly, pull in the shared log library that chezmoi otherwise
# When run directly, pull in the shared libraries that chezmoi otherwise
# concatenates ahead of this file.
# shellcheck source=/dev/null
command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh"
# shellcheck source=/dev/null
command -v have_any >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh"
main
fi
33 changes: 6 additions & 27 deletions home/.chezmoitemplates/lib/install/ai-plugins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,6 @@

set -Eeuo pipefail

#
# @description True when AI_PLUGINS holds at least one non-empty entry.
# @exitcode 0 A plugin is present.
# @exitcode 1 No plugins.
#
function _ai_plugins_have_any() {
local entry
for entry in "${AI_PLUGINS[@]-}"; do
[[ -z "${entry}" ]] && continue
return 0
done
return 1
}

#
# @description Warn about the plugins that failed to install.
# @arg $@ string Names of the plugins that failed.
#
function _ai_plugins_report_failures() {
log_warn "[ai-plugins] ${#} plugin(s) failed to install:"
printf ' - %s\n' "$@" >&2
}

#
# @description Add one plugin's marketplace and install it into Claude Code.
# @arg $1 string Plugin name.
Expand Down Expand Up @@ -76,7 +53,7 @@ function _ai_plugins_for_claude_code() {
done

if ((${#failed[@]} > 0)); then
_ai_plugins_report_failures "${failed[@]}"
report_failures ai-plugins "plugin(s) failed to install" "${failed[@]}"
fi
}

Expand All @@ -101,7 +78,7 @@ function _ai_plugins_for_copilot() {
done

if ((${#failed[@]} > 0)); then
_ai_plugins_report_failures "${failed[@]}"
report_failures ai-plugins "plugin(s) failed to install" "${failed[@]}"
fi
}

Expand All @@ -126,7 +103,7 @@ function _ai_plugins_for_target() {
# @exitcode 1 A required CLI is missing for a requested target.
#
function ai_plugins_install_main() {
if ! _ai_plugins_have_any; then
if ! have_any "${AI_PLUGINS[@]-}"; then
log_info "[ai-plugins] No plugins to install."
return 0
fi
Expand Down Expand Up @@ -158,9 +135,11 @@ function main() {
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# When run directly, pull in the shared log library that chezmoi otherwise
# When run directly, pull in the shared libraries that chezmoi otherwise
# concatenates ahead of this file.
# shellcheck source=/dev/null
command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh"
# shellcheck source=/dev/null
command -v have_any >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh"
main
fi
31 changes: 5 additions & 26 deletions home/.chezmoitemplates/lib/install/ai-skills.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,6 @@

set -Eeuo pipefail

#
# @description True when AI_SKILLS holds at least one non-empty entry.
# @exitcode 0 A skill is present.
# @exitcode 1 No skills.
#
function _ai_skills_have_any() {
local entry
for entry in "${AI_SKILLS[@]-}"; do
[[ -z "${entry}" ]] && continue
return 0
done
return 1
}

#
# @description Resolve a skill's install source, skipping a missing local skill.
# @arg $1 string Source token: a remote repo, or "local".
Expand All @@ -49,22 +35,13 @@ function _ai_skills_source() {
return 1
}

#
# @description Warn about the skills that failed to install.
# @arg $@ string Names of the skills that failed.
#
function _ai_skills_report_failures() {
log_warn "[ai-skills] ${#} skill(s) failed to install:"
printf ' - %s\n' "$@" >&2
}

#
# @description Install each selected skill for the active agent targets.
# @exitcode 0 Skills installed, or nothing to do.
# @exitcode 1 npx is not available.
#
function ai_skills_install_main() {
if ! _ai_skills_have_any; then
if ! have_any "${AI_SKILLS[@]-}"; then
log_info "[ai-skills] No skills to install."
return 0
fi
Expand Down Expand Up @@ -100,7 +77,7 @@ function ai_skills_install_main() {
done

if ((${#failed[@]} > 0)); then
_ai_skills_report_failures "${failed[@]}"
report_failures ai-skills "skill(s) failed to install" "${failed[@]}"
fi

log_info "[ai-skills] Cross-agent skills installed."
Expand All @@ -114,9 +91,11 @@ function main() {
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# When run directly, pull in the shared log library that chezmoi otherwise
# When run directly, pull in the shared libraries that chezmoi otherwise
# concatenates ahead of this file.
# shellcheck source=/dev/null
command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh"
# shellcheck source=/dev/null
command -v have_any >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh"
main
fi
27 changes: 2 additions & 25 deletions home/.chezmoitemplates/lib/install/graphify-skills.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,13 @@

set -Eeuo pipefail

#
# @description True when GRAPHIFY_PLATFORMS holds at least one non-empty entry.
# @exitcode 0 A platform is present.
# @exitcode 1 No platforms.
#
function _graphify_has_platform() {
local platform
for platform in "${GRAPHIFY_PLATFORMS[@]-}"; do
[[ -z "${platform}" ]] && continue
return 0
done
return 1
}

#
# @description Warn about the platforms that failed to install.
# @arg $@ string Names of the platforms that failed.
#
function _graphify_report_failures() {
log_warn "[graphify] ${#} platform(s) failed to install:"
printf ' - %s\n' "$@" >&2
}

#
# @description Install Graphify skills for each selected platform.
# @exitcode 0 Installed, or nothing to do.
# @exitcode 1 The graphify CLI is missing.
#
function graphify_skills_install_main() {
if ! _graphify_has_platform; then
if ! have_any "${GRAPHIFY_PLATFORMS[@]-}"; then
log_info "[graphify] No Graphify platforms to install."
return 0
fi
Expand All @@ -54,7 +31,7 @@ function graphify_skills_install_main() {
done

if ((${#failed[@]} > 0)); then
_graphify_report_failures "${failed[@]}"
report_failures graphify "platform(s) failed to install" "${failed[@]}"
fi

log_info "[graphify] Graphify agent skills installed."
Expand Down
3 changes: 1 addition & 2 deletions home/.chezmoitemplates/lib/install/uv-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ function uv_tools_install_main() {
done

if ((${#failed[@]} > 0)); then
log_warn "[uv] ${#failed[@]} tool(s) failed to install:"
printf ' - %s\n' "${failed[@]}" >&2
report_failures uv "tool(s) failed to install" "${failed[@]}"
fi

log_info "[uv] uv tools installed."
Expand Down
3 changes: 1 addition & 2 deletions home/.chezmoitemplates/lib/install/vscode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ function vscode_install_extensions_main() {
done

if ((${#failed[@]} > 0)); then
log_warn "[vscode] ${#failed[@]} extension(s) failed to install:"
printf ' - %s\n' "${failed[@]}" >&2
report_failures vscode "extension(s) failed to install" "${failed[@]}"
fi

log_info "[vscode] VS Code extensions installed."
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/lib/install/ai-mcp.bats
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
load '../../../test_helpers/load.bash'

LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh"
PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh"
LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-mcp.sh"

setup() {
Expand All @@ -24,7 +25,7 @@ CLAUDE
}

_run_main() {
run bash -c "source '$LOG_LIB' && source '$LIB' && $1 && main"
run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && $1 && main"
}

@test "main: registers each server with user scope and transport" {
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/lib/install/ai-plugins.bats
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
load '../../../test_helpers/load.bash'

LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh"
PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh"
LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-plugins.sh"

setup() {
Expand All @@ -30,7 +31,7 @@ NPX
}

_run_main() {
run bash -c "source '$LOG_LIB' && source '$LIB' && $1 && main"
run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && $1 && main"
}

@test "main: claude-code target adds marketplace and installs each plugin" {
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/lib/install/ai-skills.bats
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
load '../../../test_helpers/load.bash'

LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh"
PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh"
LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-skills.sh"

setup() {
Expand All @@ -24,7 +25,7 @@ NPX
}

_run_main() {
run bash -c "source '$LOG_LIB' && source '$LIB' && $1 && main"
run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && $1 && main"
}

@test "main: installs each skill for a single target" {
Expand Down
7 changes: 4 additions & 3 deletions tests/unit/lib/install/uv-tools.bats
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
load '../../../test_helpers/load.bash'

LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh"
PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh"
LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/uv-tools.sh"

setup() {
Expand All @@ -23,7 +24,7 @@ UV
}

@test "uv_tools_install_main: installs each tool with upgrade flag" {
run bash -c "source '$LOG_LIB' && source '$LIB' && UV_TOOLS=('graphifyy' 'tavily-cli') && uv_tools_install_main"
run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && UV_TOOLS=('graphifyy' 'tavily-cli') && uv_tools_install_main"

assert_success
assert_line "[uv] Installing uv tools..."
Expand All @@ -32,7 +33,7 @@ UV
}

@test "uv_tools_install_main: skips empty tool entries" {
run bash -c "source '$LOG_LIB' && source '$LIB' && UV_TOOLS=('graphifyy' '' 'tavily-cli') && uv_tools_install_main"
run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && UV_TOOLS=('graphifyy' '' 'tavily-cli') && uv_tools_install_main"

assert_success
[ "$(<"$UV_ARGS_FILE")" = $'tool install --upgrade graphifyy\ntool install --upgrade tavily-cli' ]
Expand All @@ -41,7 +42,7 @@ UV
@test "uv_tools_install_main: warns but succeeds when a tool fails" {
export UV_FAIL_TOOL='tavily-cli'

run bash -c "source '$LOG_LIB' && source '$LIB' && UV_TOOLS=('graphifyy' 'tavily-cli') && uv_tools_install_main"
run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && UV_TOOLS=('graphifyy' 'tavily-cli') && uv_tools_install_main"

assert_success
assert_line "[uv] Installing uv tools..."
Expand Down
Loading