Merge branch 'feat/v0.11-sleep-sync' — v0.11.0 cloud REM sync for public users

This commit is contained in:
Parfii-bot 2026-04-22 01:34:51 +08:00
commit c6c572dcf4
12 changed files with 921 additions and 5 deletions

View file

@ -1,6 +1,6 @@
# KeiSeiKit — Constructor-Pattern Agent Kit for Claude Code
KeiSeiKit is a comprehensive drop-in toolkit for [Claude Code](https://claude.com/claude-code). It ships a curated set of composable behavioral blocks, a Rust assembler that builds agent `.md` files from TOML manifests deterministically, nine pre-wired hooks (three of them dedicated to RULE 0.14 session self-audit), 35 portable skills (including an interactive `/new-agent` wizard, 10 hub-and-spoke pipelines, and the `/self-audit` retrospective skill), **10 Rust primitive crates** (including `genesis-scan` patent-IP leak detector), 13 shell primitives, and 11 cross-tool bridge templates. Everything follows a Constructor Pattern: one file per concern, manifests as single source of truth, and the generated agent files are regenerated on every relevant edit.
KeiSeiKit is a comprehensive drop-in toolkit for [Claude Code](https://claude.com/claude-code). It ships a curated set of composable behavioral blocks, a Rust assembler that builds agent `.md` files from TOML manifests deterministically, nine pre-wired hooks (three of them dedicated to RULE 0.14 session self-audit), 35 portable skills (including an interactive `/new-agent` wizard, 10 hub-and-spoke pipelines, and the `/self-audit` retrospective skill), **9 Rust primitive crates**, 13 shell primitives, and 11 cross-tool bridge templates. Everything follows a Constructor Pattern: one file per concern, manifests as single source of truth, and the generated agent files are regenerated on every relevant edit.
The kit is MIT-licensed and fully generic — install it on a fresh machine and you get a sane 12-agent fleet (implementers, critics, researchers, cost-guardians, and more — all namespaced under `kei-*` so they won't collide with your own same-named agents), a wizard for spinning up new project specialists, 10 pipeline skills that combine primitives end-to-end (`/compose-solution`, `/site-create`, `/schema-design`, `/observability-setup`, `/auth-setup`, `/api-design`, `/ci-scaffold`, `/test-matrix`, `/docs-scaffold`, `/new-project`, `/vm-provision`), and a build pipeline that keeps every agent derivable from its manifest.
@ -59,7 +59,7 @@ By default `./install.sh` is **minimal** — agents + hooks + skills + bridges,
| Profile | Primitives added | Install time | Disk (approx) |
|---|---|---|---|
| `minimal` (default) | none | ~5s | ~2 MB |
| `core` | `tomd`, `genesis-scan` | ~10s | ~5 MB |
| `core` | `tomd` | ~5s | ~3 MB |
| `frontend` | 8 site tools: `mock-render`, `visual-diff`, `tokens-sync`, `design-scrape`, `live-preview`, `figma-tokens`, `frontend-inspect`, `screenshot-decode` | ~60s | ~80 MB |
| `ops` | 8 infra tools: `kei-ledger`, `ssh-check`, `firewall-diff`, `provision-hetzner`, `provision-vultr`, `harden-base`, `metrics-scrape`, `log-ship` | ~90s | ~50 MB |
| `dev` | 4 dev tools: `kei-migrate`, `kei-changelog`, `kei-ci-lint`, `kei-docs-scaffold` | ~60s | ~40 MB |
@ -152,6 +152,28 @@ Manual trigger: `/self-audit` skill (same flow, invoked on demand).
Requires the `kei-memory` primitive. Included in the `dev` and `full` profiles; otherwise add via `./install.sh --add=kei-memory`.
## Cloud REM sync (sleep layer, v0.11.0)
Run a nightly "sleep" cycle on Anthropic's cloud — no laptop, no infra, no DevOps.
**How it works:**
- Each session: your Mac pushes trace JSONL to a private git repo you control
- 03:00 local time: a remote Claude Code agent clones the repo, analyzes the last 24h of traces, writes `reports/sleep-YYYY-MM-DD.md`, and commits back
- Next morning: `git pull` and read the consolidated findings
**Setup (one-time, ~5 min):**
1. Create an empty private repo on GitHub / GitLab / Bitbucket / self-hosted Forgejo
2. In Claude Code run `/sleep-setup`
3. The wizard generates an SSH deploy key → you paste it into the repo's deploy-key settings with WRITE access
4. The wizard emits a ready-to-paste `/schedule create` command, converted to your local 03:00 in UTC
After that, the sleep cycle runs every night automatically. The morning report is yours to read — nothing is auto-injected back into any session.
**Requires** the `kei-memory` primitive (shipped in the `dev` and `full` profiles; add via `./install.sh --add=kei-memory` otherwise). Sleep-sync scripts themselves are installed unconditionally and stay dormant until you opt in via `/sleep-setup`.
Opt in at install time with `./install.sh --with-sleep-sync` (TTY-only). Governed by RULE 0.15 in `~/.claude/rules/sleep-layer.md`.
## Primitives (Rust)
`_primitives/_rust/` is a Cargo workspace with 9 single-binary crates. `install.sh` builds `--release` and drops binaries at `~/.claude/agents/_primitives/_rust/target/release/<name>`.
@ -167,7 +189,6 @@ Requires the `kei-memory` primitive. Included in the `dev` and `full` profiles;
| `visual-diff` | Pixel diff with tolerance — used in `/site-create` screenshot-regression loop |
| `tokens-sync` | Design tokens JSON → Tailwind config extend + CSS variables under `:root` |
| `kei-memory` | Session retrospective + recurring pattern detector; offline-first analyzer powering RULE 0.14 self-audit |
| `genesis-scan` | Patent-IP leak scanner — runs as a git pre-commit or CI gate (complements the `genesis-leak-guard` runtime hook) |
## Primitives (shell)

192
_primitives/kei-sleep-setup.sh Executable file
View file

@ -0,0 +1,192 @@
#!/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 "$@"

77
_primitives/kei-sleep-sync.sh Executable file
View file

@ -0,0 +1,77 @@
#!/bin/sh
# kei-sleep-sync.sh — POSIX-sh helper called at session end.
#
# Stages any new session traces + backlog in the user's memory-repo and
# pushes via a dedicated deploy key. NEVER blocks the session: every
# failure path logs to ~/.claude/memory/sync-errors.log and exits 0.
#
# Config resolution order:
# 1. env var KEI_MEMORY_REPO_PATH / KEI_MEMORY_SSH_KEY
# 2. ~/.claude/secrets/.env (sourced if present)
# 3. sync-repo's .keisei-sync.toml (informational only)
#
# Emergency bypass: `KEI_SLEEP_SYNC_BYPASS=1 ...` — silent exit 0.
set -u
ERR_LOG="${HOME}/.claude/memory/sync-errors.log"
log_err() {
mkdir -p "$(dirname "$ERR_LOG")" 2>/dev/null || return 0
printf '[%s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*" >> "$ERR_LOG" 2>/dev/null || true
}
# ---- bypass + env -----------------------------------------------------------
[ "${KEI_SLEEP_SYNC_BYPASS:-0}" = "1" ] && exit 0
SECRETS_FILE="${HOME}/.claude/secrets/.env"
if [ -f "$SECRETS_FILE" ] && [ -z "${KEI_MEMORY_REPO_PATH:-}" ]; then
# shellcheck disable=SC1090
. "$SECRETS_FILE" 2>/dev/null || true
fi
REPO_PATH="${KEI_MEMORY_REPO_PATH:-}"
SSH_KEY="${KEI_MEMORY_SSH_KEY:-}"
# Silent no-op when sync isn't configured yet (most users).
[ -z "$REPO_PATH" ] && exit 0
[ -d "${REPO_PATH}/.git" ] || exit 0
# ---- stage, commit, push ---------------------------------------------------
# cd may fail (permissions / path vanished) — silent exit.
cd "$REPO_PATH" 2>/dev/null || exit 0
# Mirror traces from the canonical local dump dir into the repo.
TRACES_SRC="${HOME}/.claude/memory/traces"
if [ -d "$TRACES_SRC" ]; then
mkdir -p traces 2>/dev/null || true
# -n = never overwrite; append-only semantics.
cp -n "$TRACES_SRC"/*.jsonl traces/ 2>/dev/null || true
fi
git add traces/ backlog.md 2>/dev/null || { log_err "git add failed"; exit 0; }
# Nothing staged — silent exit.
if git diff --cached --quiet 2>/dev/null; then
exit 0
fi
COMMIT_MSG="memory: session traces $(date +%Y-%m-%dT%H:%M:%S%z)"
if ! git commit -q -m "$COMMIT_MSG" 2>/dev/null; then
log_err "git commit failed"
exit 0
fi
# Push via the dedicated deploy key so we don't clobber the user's default SSH.
if [ -n "$SSH_KEY" ] && [ -f "$SSH_KEY" ]; then
GIT_SSH_COMMAND="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new" \
git push -q origin HEAD 2>/dev/null \
|| { log_err "git push failed via $SSH_KEY"; exit 0; }
else
git push -q origin HEAD 2>/dev/null \
|| { log_err "git push failed (no SSH_KEY set)"; exit 0; }
fi
exit 0

View file

@ -0,0 +1,94 @@
# Nightly REM consolidation (KeiSeiKit v0.11 sleep-sync)
<!--
Template prompt. Render placeholders before pasting into Claude Code
`/schedule create`. The sleep-setup skill does this for you; this file
exists so power-users can customise the prompt before scheduling.
Placeholders:
{REPO_URL} — your memory-repo SSH URL (git@host:org/repo.git)
{UTC_CRON} — cron expression in UTC (sleep-setup converts local 03:00)
-->
Clone: {REPO_URL}
Branch: main
Time: 03:00 local (UTC cron: {UTC_CRON})
## Task
1. Clone the memory repo shallow.
2. Identify NEW traces in `traces/` since the last consolidation by
comparing filenames against `reports/last-run.txt` (if the file is
missing, treat ALL traces as new on the first run).
3. For each new trace (JSONL, one event per line), extract:
- user prompts (role = "user", type = "message")
- tool calls (type = "tool_use", name + input summary)
- tool errors (is_error = true)
- session duration (first vs last timestamp)
4. Group events into topics via simple keyword matching on user prompts
(no ML, no embeddings — keyword co-occurrence ≥ 2 is enough).
5. Count recurring patterns: any tool-call sequence OR error class that
appears in ≥ 2 distinct sessions is a "cross-session pattern".
6. Write `reports/sleep-YYYY-MM-DD.md` with this structure:
```
# REM report — YYYY-MM-DD
Sessions analyzed: <count>
Total duration: <hh:mm>
## Top tool-call sequences (cross-session)
1. <seq> ×<count>
...
## Top error classes
1. <class> ×<count>
...
## Suggested rule/hook candidates (dry-run only)
- [ ] <name> — why (<E-grade>)
...
```
7. If there are ≥ 3 cross-session patterns, prepend a timestamped block
to `backlog.md`:
```
## YYYY-MM-DD — REM consolidation
- <pattern 1>
- <pattern 2>
- <pattern 3>
```
8. Write a single line to `reports/last-run.txt` with this run's
ISO-8601 UTC timestamp (overwrite, no append).
9. Stage, commit, push:
```
git add reports/ backlog.md
git commit -m "REM: consolidation $(date -I)"
git push
```
## Invariants
- Traces are append-only. Never delete or modify `traces/*.jsonl`.
- If nothing recurred this cycle, the report MUST still be written —
with body "no patterns this cycle" — so you can tell "ran and found
nothing" apart from "did not run".
- Never fabricate findings. If the analyzer outputs an empty list,
emit an empty report.
- Never paraphrase patent-sensitive content from the traces into the
report body. Install a project-local pre-commit gate on the
memory-repo if you want hard enforcement of that boundary.
- Success signal = commit pushed cleanly. Anything else is a failure
that surfaces to the user on the next `git pull`.
## Failure handling
- Clone fails → post an issue to the repo if possible; otherwise exit 1.
- Commit hook blocks → do NOT force-push. Write the failure reason to
`reports/sleep-YYYY-MM-DD.md` body and attempt a commit excluding the
offending file.
- Push fails → retry once with exponential backoff; on second failure,
leave local commit in place and exit 1 (next run will push).

View file

@ -41,4 +41,12 @@ if command -v kei-memory >/dev/null 2>&1 && [ -f "$dest" ]; then
>/dev/null 2>&1 || true
fi
# v0.11 sleep-sync (RULE 0.15) — push traces to the user's memory-repo so a
# cloud agent can consolidate them overnight. Silent no-op when the primitive
# is absent or the user hasn't opted in via /sleep-setup.
sleep_sync="${HOME}/.claude/agents/_primitives/kei-sleep-sync.sh"
if [ -x "$sleep_sync" ]; then
"$sleep_sync" >/dev/null 2>&1 || true
fi
exit 0

View file

@ -26,6 +26,7 @@ INSTALLED_FILE="$AGENTS_DIR/_primitives/.installed"
# --- flag parsing ----------------------------------------------------------
ACTIVATE_HOOKS=0
WITH_BRIDGES=0
WITH_SLEEP_SYNC=0
PROFILE=""
ADD_LIST=""
REMOVE_NAME=""
@ -37,6 +38,7 @@ for arg in "$@"; do
case "$arg" in
--activate-hooks) ACTIVATE_HOOKS=1 ;;
--with-bridges) WITH_BRIDGES=1 ;;
--with-sleep-sync) WITH_SLEEP_SYNC=1 ;;
--profile=*) PROFILE="${arg#--profile=}" ;;
--add=*) ADD_LIST="${arg#--add=}" ;;
--remove=*) REMOVE_NAME="${arg#--remove=}" ;;
@ -71,6 +73,11 @@ Usage: ./install.sh [flags]
Aider / Replit / Antigravity / Warp / Zed).
Skipped if invoked inside the KeiSeiKit repo itself.
--with-sleep-sync after core install, run the v0.11 sleep-layer
setup helper (kei-sleep-setup.sh). TTY-only — no-op
on CI / non-interactive invocations. Print a
reminder to finish via /sleep-setup either way.
--activate-hooks jq-merge settings-snippet.json into ~/.claude/settings.json
non-interactively. Without this flag, a TTY prompt asks
at the end; non-TTY runs print manual instructions.
@ -289,7 +296,6 @@ primitive_time_secs() {
case "$name" in
mock-render|kei-migrate|kei-ledger) echo 20 ;;
kei-changelog|firewall-diff) echo 15 ;;
genesis-scan) echo 10 ;;
visual-diff|tokens-sync|ssh-check) echo 5 ;;
*) echo 10 ;;
esac
@ -307,7 +313,6 @@ primitive_disk_kb() {
case "$name" in
mock-render|kei-migrate|kei-ledger) echo 30000 ;;
kei-changelog|firewall-diff) echo 10000 ;;
genesis-scan) echo 6000 ;;
visual-diff|tokens-sync|ssh-check) echo 5000 ;;
*) echo 8000 ;;
esac
@ -1000,6 +1005,21 @@ mkdir -p "$AGENTS_DIR/_primitives"
cp -f "$KIT_DIR/_primitives/MANIFEST.toml" "$AGENTS_DIR/_primitives/MANIFEST.toml" 2>/dev/null || true
cp -f "$KIT_DIR/_primitives/README.md" "$AGENTS_DIR/_primitives/" 2>/dev/null || true
# v0.11 sleep-sync scripts — NOT listed in MANIFEST because they're always
# available regardless of profile (zero binary deps; enabled only when the
# user opts in via /sleep-setup). Copy them every install.
for sleep_sh in kei-sleep-setup.sh kei-sleep-sync.sh; do
src="$KIT_DIR/_primitives/$sleep_sh"
if [ -f "$src" ]; then
cp -f "$src" "$AGENTS_DIR/_primitives/$sleep_sh"
chmod +x "$AGENTS_DIR/_primitives/$sleep_sh"
fi
done
if [ -d "$KIT_DIR/_primitives/templates" ]; then
mkdir -p "$AGENTS_DIR/_primitives/templates"
cp -f "$KIT_DIR/_primitives/templates/"*.md "$AGENTS_DIR/_primitives/templates/" 2>/dev/null || true
fi
say "resolving primitives for profile=$PROFILE"
# Clean slate: drop every shell .sh + rust crate dir from the installed set
# FAST (no per-rust rebuild). A single regenerate_rust_workspace at the end
@ -1148,6 +1168,21 @@ if [[ "$WITH_BRIDGES" == "1" ]]; then
fi
fi
# --- optional: run sleep-sync setup helper (v0.11) -----------------------
# The helper has its own TTY prompts + validation. We only kick it off when
# stdin+stdout are TTY; otherwise print the reminder so the user can finish
# later via /sleep-setup inside a Claude Code session.
if [[ "$WITH_SLEEP_SYNC" == "1" ]]; then
SLEEP_HELPER="$AGENTS_DIR/_primitives/kei-sleep-setup.sh"
if [[ -x "$SLEEP_HELPER" ]] && [ -t 0 ] && [ -t 1 ]; then
say "running sleep-sync setup helper"
"$SLEEP_HELPER" || warn "sleep-sync setup did not complete — re-run via /sleep-setup"
else
say "sleep-sync setup deferred (non-TTY or helper missing)"
say " run /sleep-setup inside Claude Code to finish configuration"
fi
fi
# --- done ----------------------------------------------------------------
echo
say "install complete (profile=$PROFILE)"

102
skills/sleep-setup/SKILL.md Normal file
View file

@ -0,0 +1,102 @@
---
name: sleep-setup
description: One-time wizard (RULE 0.15) that configures KeiSeiKit v0.11 cloud REM sync. Generates an SSH deploy key, initializes the user's memory-repo, writes env refs (RULE 0.8), and emits a ready-to-paste `/schedule create` command for nightly consolidation. Pure-click except the 1 free-text field for repo URL.
argument-hint: (no arguments)
---
# Sleep Setup — Cloud REM Sync Wizard (index)
You are running the one-time configuration wizard for the KeiSeiKit v0.11
sleep layer. Each session-end dump pushes to a private git repo; a cloud
Claude Code agent on `/schedule` clones the repo nightly, analyzes traces,
and commits a consolidation report back. In the morning the user runs
`git pull` and reads the report. Nothing in this pipeline blocks session
close, and report content is for the user to READ — never auto-injected.
This `SKILL.md` is the INDEX. Each phase lives in its own file and is
executed in order. Never skip a phase. Never re-order phases.
---
## Pipeline overview (5 phases, 5+ AskUserQuestion)
| Phase | File | Purpose | AskUserQuestion |
|---|---|---|---|
| 1 | [phase-1-repo-pick.md](phase-1-repo-pick.md) | Pick repo provider + visibility | 2 (click-only) |
| 2 | [phase-2-repo-url.md](phase-2-repo-url.md) | Collect SSH URL (1 free-text field) | 1 (AskUserQuestion `freeText`) |
| 3 | [phase-3-deploy-key.md](phase-3-deploy-key.md) | Run `kei-sleep-setup.sh`, show pubkey, confirm deploy-key added | 1 (click) |
| 4 | [phase-4-test-push.md](phase-4-test-push.md) | Dry-run a test commit via `kei-sleep-sync.sh` | 1 (click) |
| 5 | [phase-5-trigger.md](phase-5-trigger.md) | Render `/schedule create` command, offer to run now | 1 (click) |
**Minimum AskUserQuestion count: 6.** All clicks except the single repo-URL
free-text in Phase 2.
---
## Variables the pipeline produces
| Name | Set in | Meaning |
|---|---|---|
| `PROVIDER` | Phase 1 | `github` / `gitlab` / `bitbucket` / `self-hosted` |
| `VISIBILITY` | Phase 1 | `private` (recommended) / `public` (explicit user choice) |
| `REPO_URL` | Phase 2 | Validated SSH URL (`git@host:org/repo.git`) |
| `KEY_ADDED` | Phase 3 | boolean; was deploy key confirmed added? |
| `TEST_VERIFIED` | Phase 4 | boolean; did the user see the test commit in the remote? |
| `SCHEDULE_ACTION` | Phase 5 | `run-now` / `copy-later` / `skip` |
---
## Final report (emit after Phase 5)
```
=== SLEEP-SETUP REPORT ===
Provider: <PROVIDER> (visibility: <VISIBILITY>)
Repo URL: <REPO_URL>
Deploy key: ~/.ssh/keisei-memory-sync(.pub)
Sync repo path: ~/.claude/memory/sync-repo/
Env refs: ~/.claude/secrets/.env (KEI_MEMORY_REPO_URL, _PATH, _SSH_KEY)
Test push: <PASS/FAIL> (Phase 4)
Schedule: <SCHEDULE_ACTION>
```
If `SCHEDULE_ACTION == skip`, add:
```
Local-only mode. Traces will be pushed to the repo on every session end.
To enable nightly consolidation later: paste the prompt from Phase 5 into
`/schedule create` any time.
```
---
## Rules (apply throughout — enforced at every phase)
- **Pure-click contract.** Only Phase 2 asks for free text; every
decision is an `AskUserQuestion`. No `freeText` outside Phase 2.
- **Idempotent.** Re-running the wizard must NOT clobber existing
`~/.ssh/keisei-memory-sync` or `~/.claude/memory/sync-repo/`; the
helper script handles re-use.
- **NO DOWNGRADE (RULE -1).** If SSH auth fails, return 2-3 constructive
paths (re-check deploy key, check `sshd` host, fall back to HTTPS with
PAT — with warning) — never "cannot set up".
- **NO HALLUCINATION (RULE 0.4).** Never fabricate repo URLs, key
fingerprints, or commit hashes. Show the real output of the script.
- **RULE 0.8 secrets.** The wizard writes env-var REFERENCES to
`~/.claude/secrets/.env`, never inline tokens in any generated file.
- **RULE 0.1 private remotes.** Recommend private visibility in Phase 1.
If the user explicitly picks `public`, warn once: "a public memory
repo leaks your session prompts and tool usage — confirm?".
- **Silent failure (RULE 0.15).** Nothing in the session-end path may
block the session from closing. The wizard itself may fail loudly.
- **Constructor Pattern (RULE ZERO).** Every phase file < 100 LOC
(well under the 200-LOC file limit).
---
## References
- `~/.claude/rules/sleep-layer.md` — RULE 0.15 full text
- `~/.claude/rules/secrets-single-source.md` — RULE 0.8 enforcement
- `_primitives/kei-sleep-setup.sh` — the imperative setup helper
- `_primitives/kei-sleep-sync.sh` — the session-end-dump callback
- `_primitives/templates/sleep-trigger-prompt.md` — cloud agent prompt
- `hooks/session-end-dump.sh` — where `kei-sleep-sync.sh` is invoked

View file

@ -0,0 +1,71 @@
# Phase 1 — Repo provider + visibility
Ask the user to pick where the memory-repo lives. Purely click-based —
two `AskUserQuestion` batches, zero free text.
## 1a — Provider click
Emit ONE `AskUserQuestion`:
```json
{
"questions": [
{
"question": "Where does your memory-repo live?",
"header": "Provider",
"multiSelect": false,
"options": [
{"label": "GitHub", "description": "github.com — easiest; private repo recommended"},
{"label": "GitLab", "description": "gitlab.com or self-managed GitLab"},
{"label": "Bitbucket", "description": "bitbucket.org (Atlassian)"},
{"label": "Self-hosted", "description": "Forgejo / Gitea / custom; requires SSH access"}
]
}
]
}
```
Store the pick as `PROVIDER`.
## 1b — Visibility click
Emit ONE `AskUserQuestion`:
```json
{
"questions": [
{
"question": "Repo visibility — private is strongly recommended. Your traces contain prompts and tool calls.",
"header": "Visibility",
"multiSelect": false,
"options": [
{"label": "Private (recommended)", "description": "Only you + the deploy key can read"},
{"label": "Public (I accept the risk)", "description": "Traces visible to anyone — confirm below"}
]
}
]
}
```
Store the pick as `VISIBILITY`.
## 1c — Public-visibility warning
If `VISIBILITY == "Public (I accept the risk)"`, print the warning block
below to stdout BEFORE proceeding to Phase 2:
```
WARNING: a public memory repo leaks your session prompts, tool usage, and
file paths to anyone who finds the repo. This is rarely what you want.
If you proceed, the rest of the wizard will continue unchanged — there is
no second confirmation.
```
Do NOT emit a third AskUserQuestion for re-confirm — the user already
picked "I accept the risk".
## Verify-criterion
- `PROVIDER ∈ {GitHub, GitLab, Bitbucket, Self-hosted}`.
- `VISIBILITY ∈ {Private, Public}`.
- Exactly TWO `AskUserQuestion` calls were emitted in this phase.

View file

@ -0,0 +1,62 @@
# Phase 2 — Collect SSH repo URL
The one and only free-text field in the wizard. Everything else is a
click.
## 2a — Free-text prompt
Emit ONE `AskUserQuestion` with a `freeText` field:
```json
{
"questions": [
{
"question": "Paste the SSH URL of the memory repo you created.",
"header": "Repo URL",
"multiSelect": false,
"freeText": true,
"placeholder": "git@github.com:you/kei-memory.git"
}
]
}
```
## 2b — Validate
Regex: `^git@[A-Za-z0-9._-]+:[A-Za-z0-9._/-]+\.git$`
If the user's input does NOT match, print:
```
Invalid SSH URL. Expected shape: git@<host>:<org>/<repo>.git
Examples:
git@github.com:alice/kei-memory.git
git@gitlab.com:alice/devops/kei-memory.git
git@forgejo.keisei.app:alice/kei-memory.git
```
Re-emit the same `AskUserQuestion`. Up to 3 attempts; on the 3rd failure
abort the wizard with a short "try again later with `/sleep-setup`"
message. Do not loop silently.
## 2c — Cross-check against Phase 1
Extract the host from the URL:
```
host = url.match(/^git@([^:]+):/)[1]
```
If `PROVIDER == "GitHub"` and `host != "github.com"`, print a soft
warning: "host <host> doesn't look like github.com — continuing anyway".
Same for GitLab → `gitlab.com`, Bitbucket → `bitbucket.org`. For
`Self-hosted`, skip this check.
Store the URL as `REPO_URL`.
## Verify-criterion
- `REPO_URL` matches the validation regex.
- Exactly ONE `AskUserQuestion` (or up to 3 if the user mistyped).
- No secret-like token accidentally pasted into the URL
(regex rejects `@` outside the leading `git@`).

View file

@ -0,0 +1,70 @@
# Phase 3 — Run setup script, hand off deploy key
Run the imperative helper and hand the public-key material to the user.
## 3a — Invoke `kei-sleep-setup.sh`
Run the primitive non-interactively with `REPO_URL` pre-supplied:
```bash
KEI_MEMORY_REPO_URL="<REPO_URL>" \
~/.claude/agents/_primitives/kei-sleep-setup.sh
```
Capture stdout + stderr. The script:
1. Generates `~/.ssh/keisei-memory-sync` if missing.
2. Prints the `.pub` contents and fingerprint.
3. Scaffolds `~/.claude/memory/sync-repo/` and writes config + env refs.
4. Tests SSH auth against the host (advisory).
If the script exits non-zero, surface its stderr directly to chat and
abort the wizard. Do NOT retry silently.
## 3b — Render deploy-key block to chat
The script already printed the key + fingerprint to its stdout. Echo
that block back to the user verbatim, prefaced with:
```
Add this key as a DEPLOY KEY with WRITE access to <REPO_URL>.
GitHub: Settings → Deploy keys → Add deploy key ("Allow write access")
GitLab: Settings → Repository → Deploy keys → Enable with write access
Bitbucket: Repository settings → Access keys → Add key (write)
Self-host: check your provider's "deploy key" or "access key" feature
```
NEVER show the private key. The `.pub` file is safe to display.
## 3c — Confirm click
Emit ONE `AskUserQuestion`:
```json
{
"questions": [
{
"question": "Have you added the deploy key to the repo with WRITE access?",
"header": "Deploy key",
"multiSelect": false,
"options": [
{"label": "Yes, it's added", "description": "Proceed to a test push"},
{"label": "Show me the key again", "description": "Re-print the public key + fingerprint"},
{"label": "Abort", "description": "Cancel — re-run /sleep-setup later"}
]
}
]
}
```
Handle each option:
- `Yes` → set `KEY_ADDED = true`, proceed to Phase 4.
- `Show again` → re-print the block from 3b, re-emit this click.
- `Abort` → print "aborted — re-run /sleep-setup later"; exit.
## Verify-criterion
- `~/.ssh/keisei-memory-sync(.pub)` exist.
- `~/.claude/memory/sync-repo/.git/` exists.
- `~/.claude/secrets/.env` contains all three `KEI_MEMORY_*` refs.
- `KEY_ADDED == true`.
- Exactly ONE `AskUserQuestion` (plus loops on "Show me again").

View file

@ -0,0 +1,89 @@
# Phase 4 — Test push (verify write access)
Write a tiny marker file, call `kei-sleep-sync.sh`, let the user confirm
the commit landed in the remote.
## 4a — Write a test marker
```bash
touch ~/.claude/memory/sync-repo/traces/.sleep-setup-test
```
The marker file is tracked the same way real traces are (so it tests the
real `git add traces/` path used at session end).
## 4b — Invoke the sync helper
```bash
~/.claude/agents/_primitives/kei-sleep-sync.sh
```
Capture exit code. The helper is designed to be silent on success; capture
`~/.claude/memory/sync-errors.log` as well — if it gained a new line in
the last 60s, surface that line to chat.
## 4c — Show expected commit to user
Read `HEAD`'s commit message from the local mirror:
```bash
( cd ~/.claude/memory/sync-repo && git log -1 --pretty=format:'%h %s' )
```
Print this commit to chat as "expected to appear on your remote:".
## 4d — Confirm click
Emit ONE `AskUserQuestion`:
```json
{
"questions": [
{
"question": "Do you see this commit on the remote (refresh the repo page)?",
"header": "Test push",
"multiSelect": false,
"options": [
{"label": "Yes, commit is there", "description": "Proceed to schedule"},
{"label": "No, not showing up", "description": "Show diagnostics + 2-3 fix paths"},
{"label": "Skip — I'll check later", "description": "Mark as UNVERIFIED, continue"}
]
}
]
}
```
Handle each option:
- `Yes` → set `TEST_VERIFIED = true`, clean up marker, proceed to Phase 5.
- `No` → print the diagnostic block below; re-emit the click.
- `Skip` → set `TEST_VERIFIED = false`, proceed to Phase 5.
## 4e — Diagnostic block (when user says "not showing up")
Render constructively per RULE -1:
```
Three things to check:
1. Deploy key write-access — GitHub/GitLab/Bitbucket default to READ,
you must tick the write box explicitly.
2. Default branch — your repo must have a 'main' branch; if it has
'master' or nothing at all the push target is missing.
3. SSH reachability — run:
ssh -i ~/.ssh/keisei-memory-sync -T git@<host>
and confirm the auth banner shows your repo account.
If all three look correct, check ~/.claude/memory/sync-errors.log.
```
## 4f — Cleanup marker
Regardless of branch:
```bash
rm -f ~/.claude/memory/sync-repo/traces/.sleep-setup-test
```
## Verify-criterion
- Exactly ONE `AskUserQuestion` (plus loops on the "No" branch).
- `TEST_VERIFIED` is either `true` or `false` (both acceptable; only
"Abort" terminates the wizard, and that option doesn't exist here).

View file

@ -0,0 +1,95 @@
# Phase 5 — Emit `/schedule create` command
Render the ready-to-paste nightly trigger and ask whether to run it now,
copy and do later, or skip to local-only mode.
## 5a — Load template
Read `_primitives/templates/sleep-trigger-prompt.md` from the kit install
path:
```
~/.claude/agents/_primitives/templates/sleep-trigger-prompt.md
```
If the file is missing (older kit version), fall back to the inline
template in this phase file (see 5e).
## 5b — Compute UTC cron
Local target: `03:00` user-local time, every day.
```bash
# macOS / GNU date — detect local TZ offset in minutes
offset_min=$(date +%z | awk '{ s=substr($0,1,1); h=substr($0,2,2); m=substr($0,4,2); print (s=="-" ? 1 : -1) * (h*60+m) }')
# local 03:00 = 180 minutes past midnight local
local_minutes=180
utc_minutes=$(( (local_minutes + offset_min + 1440) % 1440 ))
utc_hour=$(( utc_minutes / 60 ))
utc_min=$(( utc_minutes % 60 ))
utc_cron=$(printf '%d %d * * *' "$utc_min" "$utc_hour")
```
Keep arithmetic in the skill prompt if Claude Code executes shell;
otherwise have Claude compute the offset directly.
## 5c — Render placeholders
Replace `{REPO_URL}` with `REPO_URL` and `{UTC_CRON}` with `utc_cron` in
the template. Print the rendered prompt to chat inside a fenced code
block so the user can one-click-copy.
## 5d — Click
Emit ONE `AskUserQuestion`:
```json
{
"questions": [
{
"question": "How should we register the nightly REM trigger?",
"header": "Schedule",
"multiSelect": false,
"options": [
{"label": "Run /schedule now", "description": "Invoke /schedule create with the rendered prompt"},
{"label": "Copy, run later", "description": "Leave it to me — I'll paste into /schedule create myself"},
{"label": "Skip (local-only)", "description": "Just push traces; no nightly consolidation"}
]
}
]
}
```
Handle:
- `Run now` → set `SCHEDULE_ACTION = run-now`; invoke
`/schedule create` with the rendered body.
- `Copy later` → set `SCHEDULE_ACTION = copy-later`; print the body
again and a one-line reminder.
- `Skip` → set `SCHEDULE_ACTION = skip`; print the local-only
footer from SKILL.md's "Final report".
## 5e — Fallback inline template (if kit missing the file)
If `~/.claude/agents/_primitives/templates/sleep-trigger-prompt.md` is
absent, use this minimal inline prompt:
```
Clone: <REPO_URL>
At UTC <utc_cron>:
1. Clone shallow, read traces/ since reports/last-run.txt
2. Write reports/sleep-<date>.md with session + tool + error summary
3. If >=3 cross-session patterns, prepend to backlog.md
4. Commit + push to main
Invariants: append-only traces; no fabricated findings; never
paraphrase patent-sensitive content into report bodies.
```
Note the fallback is strictly less capable — loudly log "template file
missing from kit install; using fallback" so the user can re-install.
## Verify-criterion
- Exactly ONE `AskUserQuestion`.
- Rendered prompt contains no placeholder (`{REPO_URL}` / `{UTC_CRON}`).
- `SCHEDULE_ACTION` is one of `run-now` / `copy-later` / `skip`.
- The final report block from SKILL.md is emitted with real values.