fix: pre-public audit — critical install regression + 7 blockers (#41)

Mirror of keigit 3aff0029. lib-hooks.sh apostrophe-in-jq closed the bash single-quote and broke every install (fixed). cortex-ui removed from profiles+menu; forgejo/zoekt source lib-launchd; portable kei-message id (BSD date fallback); non-TTY default minimal; install stamps .kei-profile; README hook count 54; web-install warns before discarding local edits in the managed clone.
This commit is contained in:
KeiSei84 2026-05-24 10:42:16 +07:00 committed by GitHub
parent 4dbe6fd159
commit a299427367
11 changed files with 41 additions and 25 deletions

View file

@ -8,8 +8,8 @@ OpenClaw / Kimi from the same source-of-truth.
**Apache 2.0** — explicit patent grant + retaliation clause. 105 Rust **Apache 2.0** — explicit patent grant + retaliation clause. 105 Rust
crates [REAL: `grep -E '^\s*"[a-z-]+",' _primitives/_rust/Cargo.toml | wc -l`], crates [REAL: `grep -E '^\s*"[a-z-]+",' _primitives/_rust/Cargo.toml | wc -l`],
68 skills [REAL: `ls skills/ | wc -l`], 38 hooks 68 skills [REAL: `ls skills/ | wc -l`], 54 hooks
[REAL: `grep -c '"command":' settings-snippet.json`], 38 agent manifests [REAL: `ls hooks/*.sh | wc -l`], 38 agent manifests
[REAL: `ls _manifests/*.toml | wc -l`], 85 substrate blocks [REAL: `ls _manifests/*.toml | wc -l`], 85 substrate blocks
[REAL: `find _blocks/ -name '*.md' | wc -l`], 18 capability atoms [REAL: `find _blocks/ -name '*.md' | wc -l`], 18 capability atoms
[REAL: `find _capabilities/ -mindepth 2 -maxdepth 2 -type d | wc -l`], [REAL: `find _capabilities/ -mindepth 2 -maxdepth 2 -type d | wc -l`],
@ -83,7 +83,7 @@ The web installer (`web-install.sh` in this repo, served at
repo and delegates to `bootstrap.sh` — single source of truth, no repo and delegates to `bootstrap.sh` — single source of truth, no
duplicated install logic. duplicated install logic.
38 agents + 68 skills + 38 hooks + nightly consolidation wired in 38 agents + 68 skills + 54 hooks + nightly consolidation wired in
~60 seconds. Twelve install profiles (`outcome-only`, `minimal`, ~60 seconds. Twelve install profiles (`outcome-only`, `minimal`,
`core`, `frontend`, `ops`, `dev`, `mcp`, `cortex`, `local-mirror`, `core`, `frontend`, `ops`, `dev`, `mcp`, `cortex`, `local-mirror`,
`dashboard`, `full-hub`, `full`) defined in `dashboard`, `full-hub`, `full`) defined in

View file

@ -34,10 +34,10 @@ buddy = ["kei-buddy", "kei-telegram-webhook", "kei-shared", "kei-chat-store",
# previous one — `dashboard` extends `local-mirror`, `full-hub` extends # previous one — `dashboard` extends `local-mirror`, `full-hub` extends
# `dashboard`. Native macOS arm64 (brew + launchd plists). See # `dashboard`. Native macOS arm64 (brew + launchd plists). See
# `install/lib-dev-hub-*.sh` for the install logic per component. # `install/lib-dev-hub-*.sh` for the install logic per component.
local-mirror = ["kei-cortex", "cortex-ui", "kei-pet", "kei-shared", "kei-ledger", "kei-memory", "frustration-matrix", "kei-skill-importer", "kei-router", "kei-dna-index", "kei-atom-discovery", "dev-hub-forgejo", "dev-hub-forgejo-runner"] local-mirror = ["kei-cortex", "kei-pet", "kei-shared", "kei-ledger", "kei-memory", "frustration-matrix", "kei-skill-importer", "kei-router", "kei-dna-index", "kei-atom-discovery", "dev-hub-forgejo", "dev-hub-forgejo-runner"]
dashboard = ["kei-cortex", "cortex-ui", "kei-pet", "kei-shared", "kei-ledger", "kei-memory", "frustration-matrix", "kei-skill-importer", "kei-router", "kei-dna-index", "kei-atom-discovery", "dev-hub-forgejo", "dev-hub-forgejo-runner", "kei-projects-index", "kei-projects-watcher", "dev-hub-datasette"] dashboard = ["kei-cortex", "kei-pet", "kei-shared", "kei-ledger", "kei-memory", "frustration-matrix", "kei-skill-importer", "kei-router", "kei-dna-index", "kei-atom-discovery", "dev-hub-forgejo", "dev-hub-forgejo-runner", "kei-projects-index", "kei-projects-watcher", "dev-hub-datasette"]
full-hub = ["kei-cortex", "cortex-ui", "kei-pet", "kei-shared", "kei-ledger", "kei-memory", "frustration-matrix", "kei-skill-importer", "kei-router", "kei-dna-index", "kei-atom-discovery", "dev-hub-forgejo", "dev-hub-forgejo-runner", "kei-projects-index", "kei-projects-watcher", "dev-hub-datasette", "dev-hub-zoekt", "dev-hub-mdbook", "dev-hub-restic", "dev-hub-gdrive-import"] full-hub = ["kei-cortex", "kei-pet", "kei-shared", "kei-ledger", "kei-memory", "frustration-matrix", "kei-skill-importer", "kei-router", "kei-dna-index", "kei-atom-discovery", "dev-hub-forgejo", "dev-hub-forgejo-runner", "kei-projects-index", "kei-projects-watcher", "dev-hub-datasette", "dev-hub-zoekt", "dev-hub-mdbook", "dev-hub-restic", "dev-hub-gdrive-import"]
full = ["tomd", "kei-doctor", "kei-ledger", "kei-migrate", "kei-changelog", "ssh-check", "firewall-diff", "mock-render", "visual-diff", "tokens-sync", "design-scrape", "live-preview", "figma-tokens", "frontend-inspect", "screenshot-decode", "provision-hetzner", "provision-vultr", "harden-base", "metrics-scrape", "log-ship", "kei-ci-lint", "kei-docs-scaffold", "kei-memory", "kei-conflict-scan", "kei-refactor-engine", "kei-graph-check", "kei-store", "kei-router", "kei-sage", "kei-task", "kei-chat-store", "kei-crossdomain", "kei-search-core", "kei-content-store", "kei-social-store", "kei-curator", "kei-auth", "kei-artifact", "keisei", "kei-agent-runtime", "kei-capability", "kei-provision", "kei-entity-store", "kei-pipe", "kei-cache", "kei-spawn", "kei-replay", "kei-cortex", "cortex-ui", "kei-pet", "kei-shared", "frustration-matrix", "kei-skill-importer", "kei-projects-index", "kei-projects-watcher", "dev-hub-forgejo", "dev-hub-forgejo-runner", "dev-hub-datasette", "dev-hub-zoekt", "dev-hub-mdbook", "dev-hub-restic", "dev-hub-gdrive-import"] full = ["tomd", "kei-doctor", "kei-ledger", "kei-migrate", "kei-changelog", "ssh-check", "firewall-diff", "mock-render", "visual-diff", "tokens-sync", "design-scrape", "live-preview", "figma-tokens", "frontend-inspect", "screenshot-decode", "provision-hetzner", "provision-vultr", "harden-base", "metrics-scrape", "log-ship", "kei-ci-lint", "kei-docs-scaffold", "kei-memory", "kei-conflict-scan", "kei-refactor-engine", "kei-graph-check", "kei-store", "kei-router", "kei-sage", "kei-task", "kei-chat-store", "kei-crossdomain", "kei-search-core", "kei-content-store", "kei-social-store", "kei-curator", "kei-auth", "kei-artifact", "keisei", "kei-agent-runtime", "kei-capability", "kei-provision", "kei-entity-store", "kei-pipe", "kei-cache", "kei-spawn", "kei-replay", "kei-cortex", "kei-pet", "kei-shared", "frustration-matrix", "kei-skill-importer", "kei-projects-index", "kei-projects-watcher", "dev-hub-forgejo", "dev-hub-forgejo-runner", "dev-hub-datasette", "dev-hub-zoekt", "dev-hub-mdbook", "dev-hub-restic", "dev-hub-gdrive-import"]
# --- shell primitives (13) ------------------------------------------------- # --- shell primitives (13) -------------------------------------------------
@ -357,12 +357,6 @@ crate = "kei-cortex"
deps = ["python3 (>=3.9, for whisper_worker.py subprocess)", "pip install -r scripts/requirements.txt", "ffmpeg (on PATH, faster-whisper audio demux)"] deps = ["python3 (>=3.9, for whisper_worker.py subprocess)", "pip install -r scripts/requirements.txt", "ffmpeg (on PATH, faster-whisper audio demux)"]
desc = "Local HTTP daemon exposing chat/TTS/STT/portrait endpoints — backs cortex-ui browser app" desc = "Local HTTP daemon exposing chat/TTS/STT/portrait endpoints — backs cortex-ui browser app"
[primitive.cortex-ui]
kind = "node"
path = "_ts_packages/packages/cortex-ui"
deps = ["node>=18", "pnpm"]
desc = "Svelte 5 + Vite 5 web UI for kei-cortex daemon (chat panel, Live2D pet renderer, portrait uploader)"
[primitive.frustration-matrix] [primitive.frustration-matrix]
kind = "rust" kind = "rust"
crate = "frustration-matrix" crate = "frustration-matrix"

10
bin/kei
View file

@ -62,12 +62,10 @@ agent_count() {
# Profile from .installed marker file # Profile from .installed marker file
profile_name() { profile_name() {
local f="${AGENTS_DIR}/_primitives/.installed" # install.sh stamps the chosen profile here; .installed only holds primitive
if [ -f "$f" ]; then # names (so the old `grep ^profile .installed` always returned "?").
grep -E "^profile" "$f" 2>/dev/null | head -1 | awk -F= '{gsub(/[ "]/,"",$2); print $2}' || echo "?" local f="$HOME/.claude/.kei-profile"
else if [ -f "$f" ]; then head -1 "$f"; else echo "?"; fi
echo "?"
fi
} }
# Last Phase B sleep timestamp # Last Phase B sleep timestamp

View file

@ -51,7 +51,11 @@ prompt_profile() {
# Interactive iff stdin is a terminal. NOT stdout: web-install.sh tees stdout # Interactive iff stdin is a terminal. NOT stdout: web-install.sh tees stdout
# to a logfile (pipe), so -t 1 is false even in an interactive curl|bash. # to a logfile (pipe), so -t 1 is false even in an interactive curl|bash.
# Prompts print to the terminal via tee; the menu reads from stdin. # Prompts print to the terminal via tee; the menu reads from stdin.
if [ ! -t 0 ]; then PROFILE="cortex"; return 0; fi # Non-interactive (CI / piped, no controlling terminal) → minimal: fast,
# no 105-crate compile, can't half-fail. Matches install.sh's own default
# (was "cortex" here → divergent install vs direct install.sh). Opt up with
# --profile=cortex/full-hub.
if [ ! -t 0 ]; then PROFILE="minimal"; return 0; fi
cat <<'WIZARD' cat <<'WIZARD'
╔═══════════════════════════════════════════════════════════════════╗ ╔═══════════════════════════════════════════════════════════════════╗

View file

@ -147,6 +147,9 @@ case "$PROFILE" in
;; ;;
esac esac
say "profile: $PROFILE" say "profile: $PROFILE"
# Stamp the chosen profile so `kei` splash + tools can show it (bin/kei reads this).
mkdir -p "$HOME_DIR/.claude" 2>/dev/null || true
printf '%s\n' "$PROFILE" > "$HOME_DIR/.claude/.kei-profile" 2>/dev/null || true
# --- resolve profile -> primitive list (UNCONDITIONAL, SSoT) ------------- # --- resolve profile -> primitive list (UNCONDITIONAL, SSoT) -------------
# Must run BEFORE any reader of PROFILE_PRIMS: the --no-execute plan block # Must run BEFORE any reader of PROFILE_PRIMS: the --no-execute plan block

View file

@ -160,6 +160,8 @@ _dhf_bootstrap_admin_user() {
# Public — install entry point. Called from install.sh primitives phase. # Public — install entry point. Called from install.sh primitives phase.
install_dev_hub_forgejo() { install_dev_hub_forgejo() {
say "[dev-hub-forgejo] install starting" say "[dev-hub-forgejo] install starting"
# shellcheck source=./lib-launchd.sh
. "$KIT_DIR/install/lib-launchd.sh" # install_service / detect_brew_prefix (was unsourced → command not found)
_dhf_check_brew || return 1 _dhf_check_brew || return 1
_dhf_brew_install || return 1 _dhf_brew_install || return 1
_dhf_ensure_data_dir || return 1 _dhf_ensure_data_dir || return 1

View file

@ -128,6 +128,8 @@ _dhz_print_banner() {
# Public — install entry point. Called from install.sh primitives phase. # Public — install entry point. Called from install.sh primitives phase.
install_dev_hub_zoekt() { install_dev_hub_zoekt() {
say "[dev-hub-zoekt] install starting" say "[dev-hub-zoekt] install starting"
# shellcheck source=./lib-launchd.sh
. "$KIT_DIR/install/lib-launchd.sh" # install_service / detect_brew_prefix (was unsourced → command not found)
_dhz_check_brew || return 1 _dhz_check_brew || return 1
_dhz_check_go_runtime _dhz_check_go_runtime
_dhz_brew_install || return 1 _dhz_brew_install || return 1

View file

@ -60,9 +60,9 @@ _jq_merge_hooks() {
| reduce ($add.hooks | keys[]) as $phase ($orig; | reduce ($add.hooks | keys[]) as $phase ($orig;
.hooks[$phase] = ( .hooks[$phase] = (
((.hooks[$phase] // []) + ($add.hooks[$phase] // [])) ((.hooks[$phase] // []) + ($add.hooks[$phase] // []))
# Normalize null/absent matcher "" (Claude Code /doctor rejects null; # Normalize null/absent matcher to "" (Claude Code /doctor rejects null;
# user's pre-kit hooks often have no matcher field). Before group_by so # pre-kit user hooks often have no matcher field) before group_by so
# null + "" collapse into one group. # null and "" collapse into one group.
| map(.matcher //= "") | map(.matcher //= "")
| group_by(.matcher) | group_by(.matcher)
| map( | map(

View file

@ -37,7 +37,7 @@ menu_whiptail_profile() {
"ops" "+ 9 infra tools — provision, ssh-check, firewall-diff" OFF \ "ops" "+ 9 infra tools — provision, ssh-check, firewall-diff" OFF \
"dev" "+ 17 dev tools — kei-migrate, kei-memory, deep-sleep" OFF \ "dev" "+ 17 dev tools — kei-migrate, kei-memory, deep-sleep" OFF \
"mcp" "+ 10 MCP tools — kei-router, kei-sage, kei-auth, kei-pet" OFF \ "mcp" "+ 10 MCP tools — kei-router, kei-sage, kei-auth, kei-pet" OFF \
"cortex" "+ 11 cortex stack — kei-cortex daemon + cortex-ui" OFF \ "cortex" "+ 11 cortex stack — kei-cortex daemon + UI primitives" OFF \
"full" "+ all 62 primitives (~5 min, 380 MB)" OFF \ "full" "+ all 62 primitives (~5 min, 380 MB)" OFF \
"local-mirror" "dev hub: cortex + Forgejo + CI runner (+ 13 prims)" OFF \ "local-mirror" "dev hub: cortex + Forgejo + CI runner (+ 13 prims)" OFF \
"dashboard" "local-mirror + projects-index + Datasette (+ 16 prims)" OFF \ "dashboard" "local-mirror + projects-index + Datasette (+ 16 prims)" OFF \

View file

@ -37,7 +37,13 @@ case "$cmd" in
done done
body="${body# }" body="${body# }"
[ -n "$body" ] || { echo "usage: kei message send [--to <name|all>] <text>" >&2; exit 1; } [ -n "$body" ] || { echo "usage: kei message send [--to <name|all>] <text>" >&2; exit 1; }
id="$(date +%s)$(date +%N 2>/dev/null | cut -c1-6 || printf '000000')" # Sub-second component: GNU `date +%N` where available; on stock macOS (BSD
# date) %N is unsupported and prints literal "N" → fall back to /dev/urandom.
# Result id = epoch(10) + 6 digits = 16-digit integer, safely < 2^53.
ns="$(date +%N 2>/dev/null)"
case "$ns" in *[!0-9]*|'') ns="$(od -An -N4 -tu4 /dev/urandom 2>/dev/null | tr -dc 0-9)" ;; esac
sub="$(printf '%s000000' "$ns" | cut -c1-6)"
id="$(date +%s)${sub}"
jq -cn --argjson id "$id" --arg ts "$(date -u +%FT%TZ)" \ jq -cn --argjson id "$id" --arg ts "$(date -u +%FT%TZ)" \
--arg from "$me" --arg to "$to" --arg body "$body" \ --arg from "$me" --arg to "$to" --arg body "$body" \
'{id:$id, ts:$ts, from:$from, to:$to, body:$body}' >> "$LOG" '{id:$id, ts:$ts, from:$from, to:$to, body:$body}' >> "$LOG"

View file

@ -78,6 +78,13 @@ mkdir -p "$(dirname "$KEISEI_ROOT")"
if [ -d "$KEISEI_ROOT/.git" ]; then if [ -d "$KEISEI_ROOT/.git" ]; then
say "pulling $KEISEI_REF in $KEISEI_ROOT" say "pulling $KEISEI_REF in $KEISEI_ROOT"
git -C "$KEISEI_ROOT" fetch --depth=1 origin "$KEISEI_REF" git -C "$KEISEI_ROOT" fetch --depth=1 origin "$KEISEI_REF"
# reset --hard discards ANY local edits in this managed clone. It's a managed
# dir (don't hand-edit it — change the repo + push instead), but warn loudly
# so a manual tweak isn't lost silently.
if ! git -C "$KEISEI_ROOT" diff --quiet 2>/dev/null \
|| ! git -C "$KEISEI_ROOT" diff --cached --quiet 2>/dev/null; then
say " ⚠ local changes in $KEISEI_ROOT will be DISCARDED by reset --hard"
fi
git -C "$KEISEI_ROOT" reset --hard "origin/$KEISEI_REF" git -C "$KEISEI_ROOT" reset --hard "origin/$KEISEI_REF"
else else
say "cloning $KEISEI_REPO ($KEISEI_REF) → $KEISEI_ROOT" say "cloning $KEISEI_REPO ($KEISEI_REF) → $KEISEI_ROOT"