Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
198 lines
7 KiB
Bash
198 lines
7 KiB
Bash
# shellcheck shell=bash
|
|
# lib-dev-hub-mdbook.sh — install mdBook (Rust static-site doc-hub) via cargo + launchd.
|
|
#
|
|
# Renders every ~/Projects/<repo>/{CLAUDE,DECISIONS,RUNBOOK,DESIGN}.md as a
|
|
# single browsable web book served on http://127.0.0.1:7080/. Two launchd
|
|
# agents: `mdbook-server` (long-lived, --no-rebuild) and `mdbook-rebuilder`
|
|
# (timer, regenerates SUMMARY.md + chapters + `mdbook build` every 3600s).
|
|
#
|
|
# The rebuild logic is owned by ${KIT}/dev-hub/mdbook-rebuild.sh — emitted by
|
|
# write_rebuild_wrapper. regenerate_summary is a thin wrapper that invokes it
|
|
# synchronously at install time so the user sees the first render immediately.
|
|
#
|
|
# Sources: lib-log.sh (say/warn/err), lib-launchd.sh (install_service/unload_plist).
|
|
# Globals read: $KIT_DIR, $HOME_DIR.
|
|
|
|
# ---------- helpers (private) ----------
|
|
|
|
# Verify cargo is on PATH (rustup default stable). 0 OK, 1 missing.
|
|
_mdbook_check_cargo() {
|
|
if ! command -v cargo >/dev/null 2>&1; then
|
|
err "cargo not found — install via: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
|
|
return 1
|
|
fi
|
|
say " → cargo $(cargo --version 2>/dev/null) OK"
|
|
}
|
|
|
|
# Idempotent `cargo install mdbook`. Cargo no-ops if already at latest crate.
|
|
_mdbook_install_binary() {
|
|
say " → cargo install mdbook (no-op if already at latest)"
|
|
cargo install mdbook 2>&1 | grep -vE "^(\s*Compiling|\s*Finished|\s*Updating)" || true
|
|
}
|
|
|
|
# Render install/launchd-templates/book.toml.tmpl → ${DATA}/book.toml.
|
|
# Args: <data-dir>.
|
|
_mdbook_render_book_toml() {
|
|
local data_dir="$1"
|
|
local tmpl="$KIT_DIR/install/launchd-templates/book.toml.tmpl"
|
|
if [ ! -f "$tmpl" ]; then
|
|
err "book.toml.tmpl missing: $tmpl"
|
|
return 1
|
|
fi
|
|
mkdir -p "$data_dir"
|
|
sed \
|
|
-e "s|\${HOME}|${HOME_DIR}|g" \
|
|
-e "s|\${USER}|${USER}|g" \
|
|
-e "s|\${DATA}|${data_dir}|g" \
|
|
"$tmpl" > "$data_dir/book.toml"
|
|
say " → wrote $data_dir/book.toml"
|
|
}
|
|
|
|
# Emit ${KIT}/dev-hub/mdbook-serve.sh — wraps `mdbook serve` with binary discovery.
|
|
write_serve_wrapper() {
|
|
local wrapper="$HOME_DIR/.claude/agents/_primitives/dev-hub/mdbook-serve.sh"
|
|
mkdir -p "$(dirname "$wrapper")"
|
|
cat > "$wrapper" <<'WRAPPER_EOF'
|
|
#!/usr/bin/env bash
|
|
# Auto-generated by lib-dev-hub-mdbook.sh — do not edit by hand.
|
|
# Resolves mdbook binary (cargo / brew / PATH) and serves the rendered book.
|
|
set -eu
|
|
DATA="$HOME/Library/Application Support/keisei/mdbook"
|
|
MDBOOK="$(command -v mdbook || echo "$HOME/.cargo/bin/mdbook")"
|
|
[ -x "$MDBOOK" ] || { echo "mdbook not found (tried PATH and ~/.cargo/bin/mdbook)" >&2; exit 1; }
|
|
exec "$MDBOOK" serve "$DATA" \
|
|
--hostname 127.0.0.1 --port 7080 --no-rebuild
|
|
WRAPPER_EOF
|
|
chmod +x "$wrapper"
|
|
say " → wrote $wrapper"
|
|
}
|
|
|
|
# Emit ${KIT}/dev-hub/mdbook-rebuild.sh — regenerates src/ tree + `mdbook build`.
|
|
# Idempotent: every run wipes & rebuilds src/ from ~/Projects/.
|
|
write_rebuild_wrapper() {
|
|
local wrapper="$HOME_DIR/.claude/agents/_primitives/dev-hub/mdbook-rebuild.sh"
|
|
mkdir -p "$(dirname "$wrapper")"
|
|
cat > "$wrapper" <<'WRAPPER_EOF'
|
|
#!/usr/bin/env bash
|
|
# Auto-generated by lib-dev-hub-mdbook.sh — do not edit by hand.
|
|
# Regenerate SUMMARY.md + chapters from ~/Projects/ + rebuild book.
|
|
set -e
|
|
DATA="$HOME/Library/Application Support/keisei/mdbook"
|
|
LOGS="$HOME/Library/Logs/keisei/mdbook"
|
|
mkdir -p "$DATA/src" "$LOGS"
|
|
|
|
# Helper: slugify project name (lowercase, non-alphanum → '-', collapse, trim).
|
|
slugify() {
|
|
echo "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//'
|
|
}
|
|
|
|
# Find mdbook binary (cargo or brew).
|
|
MDBOOK="$(command -v mdbook || echo "$HOME/.cargo/bin/mdbook")"
|
|
[ -x "$MDBOOK" ] || { echo "mdbook not found" >&2; exit 1; }
|
|
|
|
# Wipe + regen src tree (chapter content only — book.toml stays at $DATA/book.toml).
|
|
rm -rf "$DATA/src"
|
|
mkdir -p "$DATA/src"
|
|
|
|
SUMMARY="$DATA/src/SUMMARY.md"
|
|
echo "# Summary" > "$SUMMARY"
|
|
echo "" >> "$SUMMARY"
|
|
|
|
for proj in "$HOME/Projects"/*/; do
|
|
[ -d "$proj.git" ] || continue
|
|
name="$(basename "$proj")"
|
|
slug="$(slugify "$name")"
|
|
chapter_dir="$DATA/src/$slug"
|
|
mkdir -p "$chapter_dir"
|
|
|
|
if [ -f "$proj/CLAUDE.md" ]; then
|
|
cp "$proj/CLAUDE.md" "$chapter_dir/index.md"
|
|
elif [ -f "$proj/README.md" ]; then
|
|
cp "$proj/README.md" "$chapter_dir/index.md"
|
|
else
|
|
# No CLAUDE.md and no README.md → skip empty chapter.
|
|
rmdir "$chapter_dir" 2>/dev/null || true
|
|
continue
|
|
fi
|
|
|
|
echo "- [$name](./$slug/index.md)" >> "$SUMMARY"
|
|
|
|
for doc in DECISIONS.md RUNBOOK.md DESIGN.md; do
|
|
if [ -f "$proj/$doc" ]; then
|
|
lower="$(echo "${doc%.md}" | tr '[:upper:]' '[:lower:]')"
|
|
cp "$proj/$doc" "$chapter_dir/${lower}.md" 2>/dev/null || true
|
|
echo " - [${doc%.md}](./$slug/${lower}.md)" >> "$SUMMARY"
|
|
fi
|
|
done
|
|
done
|
|
|
|
"$MDBOOK" build "$DATA" 2>>"$LOGS/rebuilder.err.log"
|
|
echo "rebuilt at $(date -u +%FT%TZ)"
|
|
WRAPPER_EOF
|
|
chmod +x "$wrapper"
|
|
say " → wrote $wrapper"
|
|
}
|
|
|
|
# ---------- public API ----------
|
|
|
|
# Walk ~/Projects/ and (re)generate ${DATA}/src/* + SUMMARY.md.
|
|
# Delegates to mdbook-rebuild.sh so install-time and timer use identical logic.
|
|
regenerate_summary() {
|
|
local rebuild="$HOME_DIR/.claude/agents/_primitives/dev-hub/mdbook-rebuild.sh"
|
|
if [ ! -x "$rebuild" ]; then
|
|
err "mdbook-rebuild.sh not found — call write_rebuild_wrapper first"
|
|
return 1
|
|
fi
|
|
say " → regenerating chapter tree from $HOME_DIR/Projects/"
|
|
"$rebuild" >/dev/null
|
|
}
|
|
|
|
# Install mdBook + render config + first build + register both launchd agents.
|
|
# Idempotent.
|
|
install_dev_hub_mdbook() {
|
|
say "installing dev-hub-mdbook"
|
|
_mdbook_check_cargo || return 1
|
|
_mdbook_install_binary
|
|
|
|
local data_dir="$HOME_DIR/Library/Application Support/keisei/mdbook"
|
|
mkdir -p "$data_dir/src" "$data_dir/book"
|
|
_mdbook_render_book_toml "$data_dir" || return 1
|
|
|
|
write_serve_wrapper
|
|
write_rebuild_wrapper
|
|
regenerate_summary || return 1
|
|
|
|
# shellcheck source=./lib-launchd.sh
|
|
. "$KIT_DIR/install/lib-launchd.sh"
|
|
install_service mdbook-server
|
|
install_service mdbook-rebuilder
|
|
|
|
say "mdBook served on http://127.0.0.1:7080/. Rebuilds every 1h."
|
|
}
|
|
|
|
# Unload + remove both plists. Optional --purge also `cargo uninstall mdbook`.
|
|
uninstall_dev_hub_mdbook() {
|
|
say "uninstalling dev-hub-mdbook"
|
|
# shellcheck source=./lib-launchd.sh
|
|
. "$KIT_DIR/install/lib-launchd.sh"
|
|
unload_plist mdbook-server
|
|
unload_plist mdbook-rebuilder
|
|
rm -f "$HOME_DIR/.claude/agents/_primitives/dev-hub/mdbook-serve.sh"
|
|
rm -f "$HOME_DIR/.claude/agents/_primitives/dev-hub/mdbook-rebuild.sh"
|
|
if [ "${1:-}" = "--purge" ] && command -v cargo >/dev/null 2>&1; then
|
|
say " → cargo uninstall mdbook"
|
|
cargo uninstall mdbook || true
|
|
fi
|
|
}
|
|
|
|
# Verify mdBook server returns 200 on http://127.0.0.1:7080/. 0 OK, 1 fail.
|
|
verify_dev_hub_mdbook() {
|
|
local code
|
|
code="$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:7080/ 2>/dev/null || echo 000)"
|
|
if [ "$code" = "200" ]; then
|
|
say "mdbook server health OK (200)"
|
|
return 0
|
|
fi
|
|
err "mdbook server health failed (HTTP $code) — check logs at $HOME_DIR/Library/Logs/keisei/mdbook-server/"
|
|
return 1
|
|
}
|