Constructor Pattern (RULE ZERO). Zero behaviour change, zero flag
drift — all original CLI flags preserved verbatim.
Before: install.sh — 1238 LOC monolith
After: install.sh — 138 LOC dispatcher (sources libs in order)
install/lib-*.sh — 16 cubes, max 183 LOC (lib-menu)
Cubes:
lib-log 21 LOC — logging primitives
lib-backup 63 LOC — rollback trap + BACKUP_PAIRS
lib-profile 115 LOC — MANIFEST.toml profile resolution
lib-args 92 LOC — CLI parsing + --help heredoc
lib-menu 183 LOC — whiptail/dialog/plain-text interactive picker
lib-plan 150 LOC — dry-run --no-execute output
lib-prereqs 91 LOC — hard + soft dependency checks
lib-primitives 131 LOC — primitive copy + MANIFEST drive
lib-rust 114 LOC — cargo workspace build + pre-built support
lib-scaffold 144 LOC — agent/skill/block scaffolding
lib-bridges 31 LOC — project-bridge install
lib-hooks 104 LOC — settings.json jq merge
lib-agents 77 LOC — assembled agent output
lib-skills 23 LOC — skill copy
lib-wizard 20 LOC — sleep-setup wizard invocation
lib-summary 59 LOC — post-install summary
Invariants preserved:
- macOS bash 3.2 compat (no associative arrays, no [[ ]], no ${,,})
- rollback trap wired via setup_backup_trap early in dispatcher
- jq-merge behaviour verbatim in lib-hooks
- scoped Cargo.toml regeneration in lib-rust
Function LOC limits: largest non-heredoc fn 22 LOC (check_soft_prereqs).
Three functions kept >30 LOC because heredoc-dominated (print_help,
print_summary, profile_members); splitting would fragment logical unit.
62 unique function names across cubes, zero duplicates (grep-verified).
bash -n passes on all 17 files. Runtime smoke test deferred to user's
shell (bash-readonly sandbox constraint).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
3.9 KiB
Bash
91 lines
3.9 KiB
Bash
# shellcheck shell=bash
|
|
# lib-prereqs.sh — hard + soft prerequisite checks.
|
|
#
|
|
# HARD: cargo, jq. SOFT: deps based on the primitives that will be installed.
|
|
# A profile-aware soft-warn: only check deps for primitives actually in scope.
|
|
#
|
|
# Requires: err / warn / say from lib-log.sh.
|
|
# Requires: profile_members from lib-profile.sh.
|
|
# Reads globals: $PROFILE, $CUSTOM_PRIMS, $MANIFEST.
|
|
# Sets global: $PROFILE_PRIMS (space-separated primitive names).
|
|
|
|
# Hard checks: cargo + jq. Exit 1 on missing — without them the install
|
|
# (or the installed hooks afterwards) cannot function.
|
|
check_hard_prereqs() {
|
|
say "checking prerequisites"
|
|
if ! command -v cargo >/dev/null 2>&1; then
|
|
err "cargo not found. Install Rust: https://rustup.rs/"
|
|
exit 1
|
|
fi
|
|
if ! cargo --version >/dev/null 2>&1; then
|
|
err "cargo is installed but not functional. Run: rustup default stable"
|
|
exit 1
|
|
fi
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
err "jq not found. jq is REQUIRED on any machine that will activate the"
|
|
err "KeiSeiKit hooks — without it the hooks become dead weight and would"
|
|
err "otherwise abort Claude Code's Edit/Write/Bash tool calls. Install it:"
|
|
err " brew install jq (macOS)"
|
|
err " apt install jq (Debian/Ubuntu)"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Resolve primitive list for the current profile (or CUSTOM_PRIMS if custom)
|
|
# into PROFILE_PRIMS. Does not exit.
|
|
resolve_profile_prims() {
|
|
if [ "$PROFILE" = "custom" ]; then
|
|
PROFILE_PRIMS="$(echo "$CUSTOM_PRIMS" | tr ',' ' ')"
|
|
else
|
|
PROFILE_PRIMS="$(profile_members "$PROFILE" 2>/dev/null || true)"
|
|
fi
|
|
}
|
|
|
|
# Scan PROFILE_PRIMS and echo a space-separated list of tool-need flags:
|
|
# pandoc playwright sqlite hcloud vultr yq — one per line, each "1" or "0".
|
|
_soft_dep_flags() {
|
|
local needs_pandoc=0 needs_playwright=0 needs_sqlite=0
|
|
local needs_hcloud=0 needs_vultr=0 needs_yq=0 p
|
|
for p in $PROFILE_PRIMS; do
|
|
case "$p" in
|
|
tomd) needs_pandoc=1 ;;
|
|
design-scrape|live-preview|mock-render) needs_playwright=1 ;;
|
|
kei-ledger|kei-migrate) needs_sqlite=1 ;;
|
|
provision-hetzner) needs_hcloud=1 ;;
|
|
provision-vultr) needs_vultr=1 ;;
|
|
kei-ci-lint) needs_yq=1 ;;
|
|
esac
|
|
done
|
|
echo "$needs_pandoc $needs_playwright $needs_sqlite $needs_hcloud $needs_vultr $needs_yq"
|
|
}
|
|
|
|
# Soft checks: only warn for tools needed by primitives actually being installed.
|
|
check_soft_prereqs() {
|
|
local n_pandoc n_playwright n_sqlite n_hcloud n_vultr n_yq
|
|
read -r n_pandoc n_playwright n_sqlite n_hcloud n_vultr n_yq <<< "$(_soft_dep_flags)"
|
|
if [ "$n_pandoc" = "1" ] && ! command -v pandoc >/dev/null 2>&1; then
|
|
warn "pandoc not found — tomd primitive will fail on .docx/.pptx. Install: brew install pandoc"
|
|
fi
|
|
if [ "$n_playwright" = "1" ] && ! command -v playwright >/dev/null 2>&1 && ! command -v npx >/dev/null 2>&1; then
|
|
warn "playwright/npx not found — frontend primitives need them. Install: npm i -g playwright && playwright install chromium"
|
|
fi
|
|
if [ "$n_sqlite" = "1" ] && ! command -v sqlite3 >/dev/null 2>&1; then
|
|
warn "sqlite3 CLI not found — kei-ledger/kei-migrate work without it (rusqlite embedded). Install for manual DB inspection: brew install sqlite"
|
|
fi
|
|
if [ "$n_hcloud" = "1" ] && ! command -v hcloud >/dev/null 2>&1; then
|
|
warn "hcloud CLI not found — provision-hetzner requires it. Install: brew install hcloud"
|
|
fi
|
|
if [ "$n_vultr" = "1" ] && ! command -v vultr-cli >/dev/null 2>&1; then
|
|
warn "vultr-cli not found — provision-vultr requires it. Install: brew install vultr/vultr-cli/vultr-cli"
|
|
fi
|
|
if [ "$n_yq" = "1" ] && ! command -v yq >/dev/null 2>&1; then
|
|
warn "yq not found — kei-ci-lint requires yq v4+ (mikefarah/yq). Install: brew install yq"
|
|
fi
|
|
}
|
|
|
|
# Top-level orchestrator: hard first (exit on miss), then resolve + soft.
|
|
check_prereqs() {
|
|
check_hard_prereqs
|
|
resolve_profile_prims
|
|
check_soft_prereqs
|
|
}
|