diff --git a/bin/kei b/bin/kei index d8a6606..457f26a 100755 --- a/bin/kei +++ b/bin/kei @@ -22,6 +22,7 @@ # kei mcp-wire --list # show enforcement tier per CLI # kei limits # probe each CLI's subscription quota (best-effort) # # (4 of 5 CLIs have no public API — honest report) +# kei onboard # post-install wizard (pick primary + mcp-wire + check) # kei --on= # one-shot launch of (does not change primary) # kei [args...] # splash → exec primary CLI (default: claude) # @@ -72,6 +73,10 @@ case "${1:-}" in shift exec "$HOME/.claude/scripts/kei-limits.sh" "$@" ;; + onboard|setup|wizard) + shift + exec "$HOME/.claude/scripts/kei-onboard.sh" "$@" + ;; esac # --- one-shot --on= override (does not write primary.toml) ------- @@ -230,7 +235,7 @@ ${C1} ██╔═██╗ ██╔══╝ ██║╚════█ ${C1} ██║ ██╗███████╗██║███████║███████╗██║${C0} ${C1} ╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚══════╝╚═╝${C0} -${C2} KeiSeiKit · substrate v0.44${C0} +${C2} KeiSeiKit · substrate v0.45${C0} ${C3} ─────────────────────────────────────${C0} primary CLI : ${CV}${PRIMARY}${C0} profile : ${CV}${p}${C0} diff --git a/bootstrap.sh b/bootstrap.sh index a2802df..e741d85 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -204,6 +204,25 @@ log "" log "===========================================================================" log "DONE — KeiSeiKit installed (profile: $PROFILE)" log "===========================================================================" + +# v0.45: post-install onboarding wizard. +# Auto-triggers if stdin is a TTY (real terminal). Wizard itself re-checks +# and exits cleanly if non-interactive — so curl|bash one-liner runs work too. +ONBOARD_SH="$HOME/.claude/scripts/kei-onboard.sh" +if [ -x "$ONBOARD_SH" ] && [ -t 0 ] && [ "${KEI_NO_ONBOARD:-0}" != "1" ]; then + log "" + log "Starting post-install onboarding (pick primary CLI + wire MCP)..." + log "Skip with KEI_NO_ONBOARD=1; re-run anytime with 'kei onboard'." + log "" + "$ONBOARD_SH" || log "(onboarding exited non-zero; re-run with 'kei onboard')" +else + log "" + log "Post-install wizard skipped (no TTY or KEI_NO_ONBOARD=1)." + log "Run interactively to configure primary CLI:" + log " kei onboard # full wizard" + log " kei pick # just pick primary" + log " kei mcp-wire # wire MCP into installed CLIs" +fi log "" log "Next steps:" log " - Open a new shell so PATH picks up ~/.cargo/bin and the kei-* binaries." diff --git a/install/lib-dev-hub-forgejo-runner.sh b/install/lib-dev-hub-forgejo-runner.sh index 2b94fd7..2ead810 100644 --- a/install/lib-dev-hub-forgejo-runner.sh +++ b/install/lib-dev-hub-forgejo-runner.sh @@ -80,14 +80,29 @@ _mint_runner_token() { printf '%s' "$token" } -# Internal: register act_runner with the local Forgejo. Writes ${DATA}/.runner. -# Args: . +# v0.45 fix: brew installs `gitea-runner` (not `act_runner`); the binary is +# named `gitea-runner`. Resolver tries both names so future brew packaging +# changes don't re-break this. act_runner upstream and gitea-runner fork are +# functionally equivalent and both register with Forgejo. +_runner_bin() { + if command -v act_runner >/dev/null 2>&1; then + echo "act_runner" + elif command -v gitea-runner >/dev/null 2>&1; then + echo "gitea-runner" + else + return 1 + fi +} + +# Internal: register the runner with the local Forgejo. Writes ${DATA}/.runner. _register_act_runner() { local data_dir="$1" local token="$2" local label="self-hosted,macos-arm64,native" local name="$(hostname -s)-keisei" - ( cd "$data_dir" && act_runner register \ + local runner + runner="$(_runner_bin)" || { err "no runner binary found (looked for act_runner + gitea-runner)"; return 1; } + ( cd "$data_dir" && "$runner" register \ --no-interactive \ --instance http://127.0.0.1:3001 \ --token "$token" \ @@ -97,12 +112,19 @@ _register_act_runner() { # Public entry: install + register + bootstrap the runner. install_dev_hub_forgejo_runner() { - say "installing dev-hub-forgejo-runner (act_runner)" + say "installing dev-hub-forgejo-runner (Forgejo Actions runner)" _require_forgejo_binary || return 1 _require_forgejo_running || return 1 - say "brew install act_runner" - brew install act_runner + # Prefer the Forgejo-official runner; fall back to the gitea-runner fork + # (which is what `brew install gitea-runner` actually provides today). + if ! _runner_bin >/dev/null 2>&1; then + say "brew install gitea-runner (Forgejo-compatible)" + brew install gitea-runner || { + warn "brew install gitea-runner failed — try 'brew tap actions/runner' for act_runner" + return 1 + } + fi local data_dir data_dir="$(_runner_data_dir)" @@ -125,7 +147,9 @@ install_dev_hub_forgejo_runner() { . "$KIT_DIR/install/lib-launchd.sh" install_service forgejo-runner - say "act_runner registered + running. Polling http://127.0.0.1:3001 for jobs." + local runner_name + runner_name="$(_runner_bin 2>/dev/null || echo runner)" + say "$runner_name registered + running. Polling http://127.0.0.1:3001 for jobs." } # Public entry: stop + unload the runner. Keeps ${DATA}/.runner so re-install diff --git a/install/lib-dev-hub-forgejo.sh b/install/lib-dev-hub-forgejo.sh index 127d4b9..95c61ed 100644 --- a/install/lib-dev-hub-forgejo.sh +++ b/install/lib-dev-hub-forgejo.sh @@ -97,11 +97,19 @@ _dhf_bootstrap_admin_user() { local kc_token_svc kc_pass_svc config="$(_dhf_app_ini)" username="${KEI_FORGEJO_ADMIN_USER:-${USER:-denis}}" - # Single-source Keychain service names (override per-host via env). - # Wizard MUST read identical names — see drive-import-wizard.sh.tmpl. kc_token_svc="${KEI_FORGEJO_KC_TOKEN_SERVICE:-forgejo-api-token}" kc_pass_svc="${KEI_FORGEJO_KC_PASS_SERVICE:-forgejo-admin-password}" - # Detection: any rows beyond header in `admin user list`? + + # v0.45 fix: Forgejo on first install needs `migrate` to create the sqlite + # schema. Without it, `admin user create` fails with "no such table: user" + # (verified bug 2026-05-26 in prod curl|bash test). `migrate` is idempotent + # — safe to re-run. + if ! forgejo --config "$config" migrate 2>/dev/null; then + warn " → forgejo migrate failed; daemon may need restart before admin create" + fi + + # Detection: any rows beyond header in `admin user list`? Now safe to + # parse since migrate has ensured the user table exists. user_count="$(forgejo --config "$config" admin user list 2>/dev/null \ | tail -n +2 | grep -cv '^$' || echo 0)" if [ "$user_count" -gt 0 ]; then diff --git a/install/lib-dev-hub-zoekt.sh b/install/lib-dev-hub-zoekt.sh index b552005..b76a9d1 100644 --- a/install/lib-dev-hub-zoekt.sh +++ b/install/lib-dev-hub-zoekt.sh @@ -41,13 +41,38 @@ _dhz_check_go_runtime() { fi } -# Step b — brew install zoekt (idempotent). +# Step b — install zoekt. Zoekt is NOT in homebrew/core — try tap first, +# then fall back to building from source via Go (if installed). On total +# failure, skip cleanly rather than aborting the whole install. +# v0.45 fix: prior version errored hard ("No formula") and bailed the entire +# dev-hub install. Now degrades gracefully. _dhz_brew_install() { - say "installing zoekt via brew (idempotent)" - if ! brew install zoekt; then - err "brew install zoekt failed — see brew log above" - return 1 + say "installing zoekt (idempotent)" + if command -v zoekt-webserver >/dev/null 2>&1 && command -v zoekt-index >/dev/null 2>&1; then + say " → zoekt already installed; skipping" + return 0 fi + if brew install zoekt 2>/dev/null; then + say " → installed via brew core" + return 0 + fi + if brew install sourcegraph/zoekt/zoekt 2>/dev/null \ + || brew install hyperdiscovery/zoekt/zoekt 2>/dev/null; then + say " → installed via tap" + return 0 + fi + if command -v go >/dev/null 2>&1; then + say " → falling back to 'go install' from sourcegraph/zoekt" + if go install github.com/sourcegraph/zoekt/cmd/zoekt-webserver@latest \ + && go install github.com/sourcegraph/zoekt/cmd/zoekt-index@latest; then + say " → installed via go" + return 0 + fi + fi + warn "zoekt unavailable: not in brew core/taps + no go fallback." + warn "Skipping zoekt service install. Other dev-hub services continue." + warn "To install later: brew install --HEAD sourcegraph/zoekt/zoekt" + return 2 # signal partial — caller treats as skip, not fatal } # Step c — ensure data dir tree (+ index dir). diff --git a/plugin.json b/plugin.json index deb7abc..30fe8c7 100644 --- a/plugin.json +++ b/plugin.json @@ -3,7 +3,7 @@ "name": "keisei", "displayName": "KeiSei", "description": "Constructor Pattern multi-LLM agent substrate — 38 agents, 69 skills, 54 hooks, 86 blocks. Cross-CLI policy enforcement (Claude/Grok/Copilot/Agy/Kimi) via kei-mcp + kei_bash/kei_edit/kei_write. Rust primitives via classic ./install.sh.", - "version": "0.44.0", + "version": "0.45.0", "homepage": "https://keisei.app", "repository": "https://github.com/KeiSeiLab/KeiSeiKit-1.0.git", "author": { diff --git a/scripts/kei-onboard.sh b/scripts/kei-onboard.sh new file mode 100755 index 0000000..f862473 --- /dev/null +++ b/scripts/kei-onboard.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +# kei-onboard — post-install wizard. +# +# Runs after install.sh / bootstrap.sh to guide the user through: +# Step 1: pick the primary LLM orchestrator (default for `kei` no-args) +# Step 2: wire kei-mcp into the chosen CLI (cross-CLI policy + spawn_agent) +# Step 3: optional MOONSHOT_API_KEY hint for kei limits +# Step 4: quick health check +# +# Idempotent — safe to re-run anytime via `kei onboard`. +# Honors TTY gate: non-interactive runs print summary + exit, no prompts. + +set -eu + +KEI_PRIMARY_CFG="${KEI_PRIMARY_CFG:-$HOME/.claude/config/primary.toml}" +PICK_SH="$HOME/.claude/scripts/kei-pick.sh" +WIRE_SH="$HOME/.claude/scripts/kei-mcp-wire.sh" + +# Colors only if stdout is a TTY (TTY-INTERACTIVITY-GATE: -t 1 for color is OK). +C0= CB= CC= CG= CD= CR= +if [ -t 1 ]; then + C0=$'\033[0m' + CB=$'\033[1;38;5;39m' # blue + CC=$'\033[1;38;5;220m' # gold + CG=$'\033[32m' # green + CR=$'\033[31m' # red + CD=$'\033[2m' # dim +fi + +# Non-interactive (no stdin TTY): print summary + exit. +# Per tty-interactivity-gate.md: -t 0 not -t 1. +if [ ! -t 0 ]; then + cat </dev/null 2>&1; then + mark="${CG}✓${C0}" + else + mark="${CR}✗${C0} ${CD}(not installed)${C0}" + fi + printf " ${CB}%d${C0}) %s %-20s %s\n" "$i" "$mark" "$b" "${LABELS[$b]}" + i=$((i+1)) +done +echo " ${CB}s${C0}) skip — keep current primary (claude default)" +echo + +current="" +[ -f "$KEI_PRIMARY_CFG" ] && current=$(awk -F'=' '/^provider/ {gsub(/[" ]/, "", $2); print $2; exit}' "$KEI_PRIMARY_CFG") +printf "Current primary: ${CC}%s${C0}\n" "${current:-claude (default)}" +printf "Pick [1-${#BACKENDS[@]}/s, default=s]: " +read -r choice +choice="${choice:-s}" + +primary_set="" +case "$choice" in + s|S|"") + echo " ${CD}— keeping ${current:-claude}${C0}" + primary_set="${current:-claude}" + ;; + [1-9]) + idx=$((choice-1)) + if [ $idx -ge ${#BACKENDS[@]} ] || [ $idx -lt 0 ]; then + echo " ${CR}invalid; keeping ${current:-claude}${C0}" + primary_set="${current:-claude}" + else + new="${BACKENDS[$idx]}" + mkdir -p "$(dirname "$KEI_PRIMARY_CFG")" + printf '# kei primary — written %s by onboarding\nprovider = "%s"\n' \ + "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$new" > "$KEI_PRIMARY_CFG" + echo " ${CG}✓${C0} primary set: ${CC}${new}${C0} → $KEI_PRIMARY_CFG" + primary_set="$new" + fi + ;; + *) + echo " ${CR}invalid; keeping ${current:-claude}${C0}" + primary_set="${current:-claude}" + ;; +esac + +# ── Step 2: mcp-wire ─────────────────────────────────────────────── +echo +echo "${CB}── Step 2/4 — Wire kei-mcp into installed CLIs ──${C0}" +echo +echo "kei-mcp exposes ${CC}spawn_agent${C0} + ${CC}kei_bash/kei_edit/kei_write${C0} (with" +echo "policy chain) to any MCP-capable CLI. Enables cross-CLI agent invocation" +echo "AND hook enforcement on non-Claude backends." +echo +printf "Run ${CC}kei mcp-wire${C0} now (writes to ~/.grok/, ~/.copilot/, etc.)? [Y/n]: " +read -r wire_ans +wire_ans="${wire_ans:-Y}" +case "$wire_ans" in + y|Y|yes) + if [ -x "$WIRE_SH" ]; then + "$WIRE_SH" + else + echo " ${CR}— $WIRE_SH not found; skip${C0}" + fi + ;; + *) + echo " ${CD}— skipped. Run later: ${CC}kei mcp-wire${C0}${CD}${C0}" + ;; +esac + +# ── Step 3: MOONSHOT key hint ────────────────────────────────────── +echo +echo "${CB}── Step 3/4 — Live subscription limits (optional) ──${C0}" +echo +echo "${CC}kei limits${C0} probes each CLI's subscription quota. Research found that" +echo "only Kimi exposes a public API; the others are dashboard-only." +echo +if [ -n "${MOONSHOT_API_KEY:-}" ]; then + echo " ${CG}✓${C0} MOONSHOT_API_KEY is set — Kimi balance probing enabled" +else + cat </dev/null 2>&1; then + kei-doctor 2>&1 | head -20 || true +else + echo " ${CD}— kei-doctor not on PATH yet. Open new shell + run: ${CC}kei-doctor${C0}" +fi + +# ── Done ─────────────────────────────────────────────────────────── +cat <