KeiSeiKit-1.0/install.sh
Parfii-bot 8625fe791b fix(install): scaffold ~/.claude/memory dir + placeholder MEMORY.md
_blocks/memory-protocol.md references ~/.claude/memory/MEMORY.md, but the
installer previously only scaffolded agents/ hooks/ skills/ — so the first
agent that followed the block would fail reading a non-existent index.
Now mkdir -p ~/.claude/memory and, only when MEMORY.md is absent, write a
minimal placeholder (frontmatter + pointer to the block). User-edited
MEMORY.md is never overwritten.
2026-04-21 03:07:15 +08:00

177 lines
6.6 KiB
Bash
Executable file

#!/usr/bin/env bash
# KeiSeiKit — Constructor-Pattern Agent Kit installer
# Idempotent: safe to re-run. Never overwrites settings.json or existing user manifests.
set -euo pipefail
KIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HOME_DIR="${HOME:?HOME not set}"
AGENTS_DIR="$HOME_DIR/.claude/agents"
HOOKS_DIR="$HOME_DIR/.claude/hooks"
SKILLS_DIR="$HOME_DIR/.claude/skills"
say() { printf '\033[1;36m[install]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; }
# Backup a populated target directory to a timestamped sibling before clobber.
# No-op if target is absent or contains no regular files (recursively). This
# means freshly-mkdir'd scaffolds are NOT backed up — only real user content.
# Only called on $AGENTS_DIR/_blocks, _templates, _assembler, $HOOKS_DIR,
# $SKILLS_DIR — never on $KIT_DIR source.
backup_dir() {
local target="$1"
[ -d "$target" ] || return 0
# No regular files anywhere under target → nothing worth preserving
if [ -z "$(find "$target" -type f -print -quit 2>/dev/null)" ]; then
return 0
fi
local backup="${target}.bak-$(date +%s)"
cp -a "$target" "$backup"
say "backed up existing $target to $backup"
}
# --- prerequisites ----------------------------------------------------------
say "checking prerequisites"
if ! command -v cargo >/dev/null 2>&1; then
err "cargo not found. Install Rust: https://rustup.rs/"
exit 1
fi
# Verify cargo actually runs (catches "rustup has no default toolchain")
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
# --- create target dirs -----------------------------------------------------
say "creating directories"
mkdir -p \
"$AGENTS_DIR/_blocks" \
"$AGENTS_DIR/_manifests" \
"$AGENTS_DIR/_templates" \
"$AGENTS_DIR/_assembler/src" \
"$AGENTS_DIR/_generated" \
"$HOOKS_DIR" \
"$SKILLS_DIR/new-agent" \
"$HOME_DIR/.claude/memory"
# --- scaffold MEMORY.md placeholder (user-respecting) ----------------------
# _blocks/memory-protocol.md references ~/.claude/memory/MEMORY.md; without
# this file the first agent following the protocol fails on read.
MEMORY_INDEX="$HOME_DIR/.claude/memory/MEMORY.md"
if [[ ! -f "$MEMORY_INDEX" ]]; then
cat > "$MEMORY_INDEX" <<'EOF'
# Auto Memory — Index
> File-based memory index. Add entries as you save memory files under this directory.
> See `_blocks/memory-protocol.md` for format.
EOF
say "scaffolded $MEMORY_INDEX"
fi
# --- copy blocks (overwrite ours; blocks are SSoT from kit) ----------------
say "copying shared blocks -> $AGENTS_DIR/_blocks/"
backup_dir "$AGENTS_DIR/_blocks"
cp -f "$KIT_DIR/_blocks/"*.md "$AGENTS_DIR/_blocks/"
# --- copy generic manifests, DO NOT overwrite user's existing manifests -----
say "copying generic manifests -> $AGENTS_DIR/_manifests/ (skip if exists)"
copied=0; skipped=0
for f in "$KIT_DIR/_manifests/"*.toml; do
name="$(basename "$f")"
if [[ -f "$AGENTS_DIR/_manifests/$name" ]]; then
skipped=$((skipped+1))
else
cp "$f" "$AGENTS_DIR/_manifests/$name"
copied=$((copied+1))
fi
done
say " copied $copied, skipped $skipped (already present)"
# --- copy template ---------------------------------------------------------
if compgen -G "$KIT_DIR/_templates/*.template" >/dev/null; then
say "copying specialist template"
backup_dir "$AGENTS_DIR/_templates"
cp -f "$KIT_DIR/_templates/"*.template "$AGENTS_DIR/_templates/"
fi
# --- copy assembler source (always refresh) --------------------------------
say "copying assembler source"
backup_dir "$AGENTS_DIR/_assembler"
cp -f "$KIT_DIR/_assembler/Cargo.toml" "$AGENTS_DIR/_assembler/"
cp -f "$KIT_DIR/_assembler/src/"*.rs "$AGENTS_DIR/_assembler/src/"
if [[ -f "$KIT_DIR/_assembler/.gitignore" ]]; then
cp -f "$KIT_DIR/_assembler/.gitignore" "$AGENTS_DIR/_assembler/"
fi
# --- copy hooks (refresh; hooks are logic, not config) ---------------------
say "copying hooks -> $HOOKS_DIR/"
backup_dir "$HOOKS_DIR"
for h in assemble-agents.sh assemble-validate.sh no-hand-edit-agents.sh; do
cp -f "$KIT_DIR/hooks/$h" "$HOOKS_DIR/$h"
chmod +x "$HOOKS_DIR/$h"
done
# --- copy skills -----------------------------------------------------------
if [[ -d "$KIT_DIR/skills" ]]; then
say "copying skills"
backup_dir "$SKILLS_DIR"
for skill_dir in "$KIT_DIR/skills/"*/; do
[ -d "$skill_dir" ] || continue
skill_name="$(basename "$skill_dir")"
mkdir -p "$SKILLS_DIR/$skill_name"
cp -rf "$skill_dir"* "$SKILLS_DIR/$skill_name/" 2>/dev/null || true
say " -> $skill_name"
done
fi
# --- build assembler -------------------------------------------------------
say "building Rust assembler (cargo build --release)"
( cd "$AGENTS_DIR/_assembler" && cargo build --release )
if [[ ! -x "$AGENTS_DIR/_assembler/target/release/assemble" ]]; then
err "build succeeded but binary not found at $AGENTS_DIR/_assembler/target/release/assemble"
exit 2
fi
# --- generate .md agents in-place ------------------------------------------
say "generating agent .md files (--in-place)"
AGENT_ROOT="$AGENTS_DIR" "$AGENTS_DIR/_assembler/target/release/assemble" --in-place
# --- done -----------------------------------------------------------------
echo
say "install complete"
echo
cat <<EOF
==========================================================================
NEXT STEP: merge settings-snippet.json into ~/.claude/settings.json
==========================================================================
KeiSeiKit ships 3 hooks (assemble-agents, assemble-validate, no-hand-edit).
To activate them, merge entries from:
$KIT_DIR/settings-snippet.json
into your:
$HOME_DIR/.claude/settings.json
If you have no settings.json yet, you can simply copy the snippet:
cp $KIT_DIR/settings-snippet.json $HOME_DIR/.claude/settings.json
Otherwise, open both files and append the PostToolUse / PreToolUse
entries to the matching arrays in your existing settings.json.
To verify install:
ls $AGENTS_DIR/*.md # should show ~14 generated agents
$AGENTS_DIR/_assembler/target/release/assemble --validate
To create a new project-specialist agent:
/new-agent
==========================================================================
EOF