diff --git a/skills/sleep-setup/SKILL.md b/skills/sleep-setup/SKILL.md new file mode 100644 index 0000000..fa76a93 --- /dev/null +++ b/skills/sleep-setup/SKILL.md @@ -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: (visibility: ) +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: (Phase 4) +Schedule: +``` + +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 diff --git a/skills/sleep-setup/phase-1-repo-pick.md b/skills/sleep-setup/phase-1-repo-pick.md new file mode 100644 index 0000000..296f1d1 --- /dev/null +++ b/skills/sleep-setup/phase-1-repo-pick.md @@ -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. diff --git a/skills/sleep-setup/phase-2-repo-url.md b/skills/sleep-setup/phase-2-repo-url.md new file mode 100644 index 0000000..8b83858 --- /dev/null +++ b/skills/sleep-setup/phase-2-repo-url.md @@ -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@:/.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 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@`). diff --git a/skills/sleep-setup/phase-3-deploy-key.md b/skills/sleep-setup/phase-3-deploy-key.md new file mode 100644 index 0000000..c60f237 --- /dev/null +++ b/skills/sleep-setup/phase-3-deploy-key.md @@ -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="" \ + ~/.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 . +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"). diff --git a/skills/sleep-setup/phase-4-test-push.md b/skills/sleep-setup/phase-4-test-push.md new file mode 100644 index 0000000..970a2a2 --- /dev/null +++ b/skills/sleep-setup/phase-4-test-push.md @@ -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@ + 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). diff --git a/skills/sleep-setup/phase-5-trigger.md b/skills/sleep-setup/phase-5-trigger.md new file mode 100644 index 0000000..e8b801b --- /dev/null +++ b/skills/sleep-setup/phase-5-trigger.md @@ -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: +At UTC : + 1. Clone shallow, read traces/ since reports/last-run.txt + 2. Write reports/sleep-.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.