KeiSeiKit-1.0/_primitives/kei-sleep-setup.sh

192 lines
6.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# kei-sleep-setup.sh — KeiSeiKit v0.11 sleep-sync first-time wizard.
# Generates deploy key, scaffolds sync-repo, writes env refs (RULE 0.8).
# Idempotent. Invoked by `/sleep-setup` skill or `install.sh --with-sleep-sync`.
set -eu
KEI_HOME="${HOME}/.claude"
SECRETS_FILE="${KEI_HOME}/secrets/.env"
SSH_KEY="${HOME}/.ssh/keisei-memory-sync"
REPO_PATH="${KEI_HOME}/memory/sync-repo"
say() { printf '[sleep-setup] %s\n' "$*"; }
warn() { printf '[sleep-setup] warn: %s\n' "$*" >&2; }
err() { printf '[sleep-setup] error: %s\n' "$*" >&2; }
validate_ssh_url() {
printf '%s' "$1" | grep -Eq '^git@[A-Za-z0-9._-]+:[A-Za-z0-9._/-]+\.git$'
}
url_host() {
printf '%s' "$1" | sed -E 's/^git@([^:]+):.*/\1/'
}
prompt_repo_url() {
local url="${KEI_MEMORY_REPO_URL:-}"
if [ -n "$url" ]; then
say "using KEI_MEMORY_REPO_URL from environment"
elif [ -t 0 ]; then
printf '\nMemory repo SSH URL (e.g. git@github.com:you/kei-memory.git):\n > '
read -r url
else
err "no repo URL provided and no TTY to prompt; set KEI_MEMORY_REPO_URL"
exit 1
fi
if ! validate_ssh_url "$url"; then
err "invalid SSH URL format: $url"
err "expected: git@<host>:<org>/<repo>.git"
exit 1
fi
printf '%s' "$url"
}
ensure_ssh_key() {
mkdir -p "$(dirname "$SSH_KEY")"
chmod 700 "$(dirname "$SSH_KEY")" 2>/dev/null || true
if [ -f "$SSH_KEY" ] && [ -f "${SSH_KEY}.pub" ]; then
say "deploy key already exists at $SSH_KEY (skipping)"
return 0
fi
say "generating ed25519 deploy key at $SSH_KEY"
ssh-keygen -t ed25519 -f "$SSH_KEY" -N '' -C 'keisei-memory-sync' >/dev/null
chmod 600 "$SSH_KEY"
}
show_deploy_key_instructions() {
local url="$1"
printf '\n==============================================================\n'
printf ' ADD THIS AS A DEPLOY KEY (WRITE ACCESS) TO: %s\n' "$url"
printf '==============================================================\n\n'
cat "${SSH_KEY}.pub"
printf '\nFingerprint: '
ssh-keygen -lf "${SSH_KEY}.pub" 2>/dev/null || true
printf '\nGitHub: Settings -> Deploy keys -> Add ("Allow write access")\n'
printf 'GitLab: Settings -> Repository -> Deploy keys -> Enable write\n'
printf 'Bitbucket: Repository settings -> Access keys -> Add (write)\n'
printf 'Forgejo: Settings -> Deploy Keys -> Add (allow write)\n'
printf '==============================================================\n\n'
}
init_sync_repo() {
local url="$1"
mkdir -p "$REPO_PATH"
if [ -d "${REPO_PATH}/.git" ]; then
say "sync-repo already initialized at $REPO_PATH"
return 0
fi
say "cloning $url$REPO_PATH (shallow, may fail if repo empty — will init instead)"
if GIT_SSH_COMMAND="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new" \
git clone --depth 1 "$url" "$REPO_PATH" 2>/dev/null; then
say "cloned existing repo"
else
say "clone failed (empty repo?) — initializing local and setting remote"
rm -rf "$REPO_PATH"
mkdir -p "$REPO_PATH"
( cd "$REPO_PATH" && git init -q -b main && git remote add origin "$url" )
fi
}
scaffold_repo_structure() {
local url="$1"
cd "$REPO_PATH"
mkdir -p traces reports
[ -f README.md ] || write_readme
[ -f .gitignore ] || printf 'target/\nnode_modules/\n.DS_Store\n*.swp\n*.swo\n' > .gitignore
[ -f backlog.md ] || write_backlog
[ -f .keisei-sync.toml ] || write_sync_config "$url"
}
write_readme() {
cat > README.md <<'EOF'
# KeiSeiKit memory store
Append-only store for KeiSeiKit session traces + nightly REM reports.
Managed by kei-sleep-sync; do not hand-edit.
- traces/ — session JSONL pushed after each Claude Code session
- reports/ — nightly reports written by a cloud agent on /schedule
- backlog.md — recurring patterns flagged for your review
EOF
}
write_backlog() {
cat > backlog.md <<'EOF'
# REM backlog — recurring patterns
Nightly consolidation prepends dated blocks when >=3 patterns recur.
Pop entries manually after review.
<!-- populated by the cloud agent -->
EOF
}
write_sync_config() {
cat > .keisei-sync.toml <<EOF
# KeiSeiKit sleep-sync config (per-repo)
repo_url = "$1"
push_on_session_end = true
branch = "main"
commit_prefix = "memory"
EOF
}
write_env_refs() {
local url="$1"
mkdir -p "$(dirname "$SECRETS_FILE")"
chmod 700 "$(dirname "$SECRETS_FILE")" 2>/dev/null || true
touch "$SECRETS_FILE"
chmod 600 "$SECRETS_FILE"
# Remove any prior KEI_MEMORY_* lines (idempotent update).
local tmp
tmp="$(mktemp)"
grep -vE '^(KEI_MEMORY_REPO_URL|KEI_MEMORY_REPO_PATH|KEI_MEMORY_SSH_KEY)=' \
"$SECRETS_FILE" > "$tmp" 2>/dev/null || true
cat >> "$tmp" <<EOF
KEI_MEMORY_REPO_URL=$url
KEI_MEMORY_REPO_PATH=$REPO_PATH
KEI_MEMORY_SSH_KEY=$SSH_KEY
EOF
mv "$tmp" "$SECRETS_FILE"
chmod 600 "$SECRETS_FILE"
say "wrote env refs to $SECRETS_FILE"
}
test_ssh_auth() {
local url="$1"
local host
host="$(url_host "$url")"
say "testing SSH auth to $host"
# ssh -T on git hosts returns non-zero even on success; grep the banner.
local out
out="$(ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new \
-o BatchMode=yes -T "git@$host" 2>&1 || true)"
if printf '%s' "$out" | grep -Eiq '(successfully authenticated|welcome|you.?ve|does not provide shell access)'; then
say "SSH auth OK ($host)"
return 0
fi
warn "SSH auth to $host did not return a known success banner"
warn "server said: $(printf '%s' "$out" | head -n1)"
warn "if the deploy key was just added it may need 30-60s to propagate"
return 1
}
main() {
say "KeiSeiKit v0.11 sleep-sync setup"
local url
url="$(prompt_repo_url)"
ensure_ssh_key
show_deploy_key_instructions "$url"
if [ -t 0 ]; then
printf 'Deploy key added to the repo? Press ENTER to continue, Ctrl-C to abort.\n'
read -r _ || true
fi
init_sync_repo "$url"
scaffold_repo_structure "$url"
write_env_refs "$url"
test_ssh_auth "$url" || warn "continuing despite auth test warning"
echo
say "setup complete"
say "next: run /sleep-setup in Claude Code to register a nightly /schedule trigger"
}
main "$@"