Merge feat/v0.17-sleep-setup-hybrid — local/remote/hybrid mode wizard
This commit is contained in:
commit
4c77b9a79b
5 changed files with 355 additions and 60 deletions
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
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.
|
||||
description: One-time wizard (RULE 0.15) that configures KeiSeiKit sleep layer. Phase 0 picks mode (local-only / remote-only / hybrid); Phase 0b picks local trigger time (6 options incl. Custom HH:MM). Remote/hybrid generate an SSH deploy key, init the memory-repo, write env refs (RULE 0.8), and emit a `/schedule create` command. Local-only / hybrid emit a CronCreate snippet. Pure-click except 2 free-text fields (repo URL in Phase 2, Custom time in Phase 0b).
|
||||
argument-hint: (no arguments)
|
||||
---
|
||||
|
||||
|
|
@ -18,19 +18,24 @@ executed in order. Never skip a phase. Never re-order phases.
|
|||
|
||||
---
|
||||
|
||||
## Pipeline overview (6 phases, 9+ AskUserQuestion since v0.13.0)
|
||||
## Pipeline overview (8 phases, 11+ AskUserQuestion since v0.14.0)
|
||||
|
||||
| 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) |
|
||||
| 0 | [phase-0-mode.md](phase-0-mode.md) | Pick sleep mode: local-only / remote-only / hybrid | 1 (click-only) |
|
||||
| 0b | [phase-0b-time.md](phase-0b-time.md) | Pick local trigger time (6 options; Custom adds 1 free-text) | 1-2 (click; +freeText if Custom) |
|
||||
| 1 | [phase-1-repo-pick.md](phase-1-repo-pick.md) | Pick repo provider + visibility (skipped if local-only) | 2 (click-only) |
|
||||
| 2 | [phase-2-repo-url.md](phase-2-repo-url.md) | Collect SSH URL (skipped if local-only) | 1 (AskUserQuestion `freeText`) |
|
||||
| 3 | [phase-3-deploy-key.md](phase-3-deploy-key.md) | Run `kei-sleep-setup.sh`, confirm deploy-key added (skipped if local-only) | 1 (click) |
|
||||
| 3b | [phase-3b-deep-sleep.md](phase-3b-deep-sleep.md) | v0.13.0 — deep-sleep cadence + fork mode + store backend | 3 (click; +1 free-text if Custom cadence) |
|
||||
| 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) |
|
||||
| 4 | [phase-4-test-push.md](phase-4-test-push.md) | Dry-run a test commit (skipped if local-only) | 1 (click) |
|
||||
| 5 | [phase-5-trigger.md](phase-5-trigger.md) | Render CronCreate and/or `/schedule create` per mode | 1-2 (click; hybrid asks twice) |
|
||||
|
||||
**Minimum AskUserQuestion count: 9.** All clicks except the single repo-URL
|
||||
free-text in Phase 2 (plus optional Custom cadence / S3 fields in Phase 3b).
|
||||
**Minimum AskUserQuestion count: 11** (remote-only / hybrid full pipeline).
|
||||
**Local-only mode: 6 minimum** (Phases 0, 0b, 3b, 5; Phases 1-4 skipped).
|
||||
All clicks except the repo-URL free-text in Phase 2 (skipped in local-only),
|
||||
the Custom-time free-text in Phase 0b (optional), and the Custom-cadence /
|
||||
S3 free-text fields in Phase 3b (optional).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -38,22 +43,28 @@ free-text in Phase 2 (plus optional Custom cadence / S3 fields in Phase 3b).
|
|||
|
||||
| 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? |
|
||||
| `SLEEP_MODE` | Phase 0 | `local-only` / `remote-only` / `hybrid` |
|
||||
| `SLEEP_TIME_LOCAL` | Phase 0b | `HH:MM` 24h format (e.g. `03:00`); user's local time |
|
||||
| `PROVIDER` | Phase 1 (remote/hybrid only) | `github` / `gitlab` / `bitbucket` / `self-hosted` |
|
||||
| `VISIBILITY` | Phase 1 (remote/hybrid only) | `private` (recommended) / `public` (explicit user choice) |
|
||||
| `REPO_URL` | Phase 2 (remote/hybrid only) | Validated SSH URL (`git@host:org/repo.git`) |
|
||||
| `KEY_ADDED` | Phase 3 (remote/hybrid only) | boolean; was deploy key confirmed added? |
|
||||
| `DEEP_SLEEP_CRON_DAYS` | Phase 3b | integer ≥0; 0 disables Phase C; default 7 |
|
||||
| `DEEP_SLEEP_WITH_FORK` | Phase 3b | 0 (plan only) / 1 (plan + fork branch) |
|
||||
| `DEEP_SLEEP_WITH_FORK` | Phase 3b | 0 (plan only) / 1 (plan + fork branch) / 2 (plan + local-patch; local-only mode) |
|
||||
| `STORE_BACKEND` | Phase 3b | `github` / `forgejo` / `gitea` / `filesystem` / `s3` |
|
||||
| `TEST_VERIFIED` | Phase 4 | boolean; did the user see the test commit in the remote? |
|
||||
| `SCHEDULE_ACTION` | Phase 5 | `run-now` / `copy-later` / `skip` |
|
||||
| `TEST_VERIFIED` | Phase 4 (remote/hybrid only) | boolean; did the user see the test commit in the remote? |
|
||||
| `SLEEP_CRON_UTC` | Phase 5 (remote/hybrid only) | `m h * * *` cron expression derived from `SLEEP_TIME_LOCAL` + local TZ |
|
||||
| `SCHEDULE_ACTION` | Phase 5 | mode-dependent; see phase-5-trigger.md §5d |
|
||||
|
||||
---
|
||||
|
||||
## Final report (emit after Phase 5)
|
||||
|
||||
Remote-only / hybrid (full pipeline):
|
||||
```
|
||||
=== SLEEP-SETUP REPORT ===
|
||||
Mode: <SLEEP_MODE>
|
||||
Time (local): <SLEEP_TIME_LOCAL>
|
||||
Provider: <PROVIDER> (visibility: <VISIBILITY>)
|
||||
Repo URL: <REPO_URL>
|
||||
Deploy key: ~/.ssh/keisei-memory-sync(.pub)
|
||||
|
|
@ -63,19 +74,40 @@ Test push: <PASS/FAIL> (Phase 4)
|
|||
Schedule: <SCHEDULE_ACTION>
|
||||
```
|
||||
|
||||
If `SCHEDULE_ACTION == skip`, add:
|
||||
Local-only (Phases 1-4 skipped):
|
||||
```
|
||||
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.
|
||||
=== SLEEP-SETUP REPORT ===
|
||||
Mode: local-only
|
||||
Time (local): <SLEEP_TIME_LOCAL>
|
||||
Provider: (skipped)
|
||||
Repo URL: (skipped)
|
||||
Deploy key: (not generated — local-only needs no git)
|
||||
Sync repo path: (skipped)
|
||||
Env refs: ~/.claude/secrets/.env (no sleep-sync keys written)
|
||||
Test push: (skipped)
|
||||
Schedule: <SCHEDULE_ACTION> # local-cron-created / -copy-later / -skipped
|
||||
```
|
||||
|
||||
If `SCHEDULE_ACTION` contains `skipped` (remote) or `local-cron-skipped`,
|
||||
add:
|
||||
```
|
||||
No nightly consolidation. Traces still land locally in
|
||||
~/.claude/memory/traces/ (and push to the repo on every session end if
|
||||
mode != local-only). Re-run /sleep-setup any time to register a trigger.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
- **Pure-click contract.** Only Phase 2 (repo URL) and Phase 0b (Custom
|
||||
time) ask for free text; every other decision is an `AskUserQuestion`.
|
||||
No `freeText` outside those two points (plus optional Custom cadence /
|
||||
S3 fields in Phase 3b).
|
||||
- **Local-only skips Phases 1-4 entirely** — no git operations, no SSH
|
||||
key, no repo URL collection, no deploy-key walkthrough, no test push.
|
||||
The skill jumps phase-0 → phase-0b → phase-3b → phase-5 and never
|
||||
touches git.
|
||||
- **Idempotent.** Re-running the wizard must NOT clobber existing
|
||||
`~/.ssh/keisei-memory-sync` or `~/.claude/memory/sync-repo/`; the
|
||||
helper script handles re-use.
|
||||
|
|
@ -91,8 +123,10 @@ To enable nightly consolidation later: paste the prompt from Phase 5 into
|
|||
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).
|
||||
- **Constructor Pattern (RULE ZERO).** Every phase file < 200 LOC
|
||||
(RULE ZERO hard limit). New lightweight phases (0, 0b) target < 100
|
||||
LOC; phase-3b and phase-5 are heavier branch-heavy router files and
|
||||
sit between 100-200.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
64
skills/sleep-setup/phase-0-mode.md
Normal file
64
skills/sleep-setup/phase-0-mode.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Phase 0 — Sleep mode pick
|
||||
|
||||
Ask the user to pick the execution mode for nightly sleep-layer
|
||||
consolidation. This is the FIRST phase of the wizard — it runs BEFORE
|
||||
Phase 1 and branches the entire pipeline.
|
||||
|
||||
## 0a — Mode click
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "How should sleep-layer consolidation run?",
|
||||
"header": "Sleep mode",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{"label": "Local-only", "description": "macOS CronCreate on this Mac; full access to ~/.claude/memory/ and /self-audit. No git repo needed."},
|
||||
{"label": "Remote-only", "description": "Cloud agent via /schedule; git-repo based; morning git pull to read the report. Mac can sleep."},
|
||||
{"label": "Hybrid", "description": "Both. Local cron does the deep analysis; remote is redundancy when Mac is asleep. Both paths are idempotent."}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Store the pick as `SLEEP_MODE` ∈ {`local-only`, `remote-only`, `hybrid`}.
|
||||
|
||||
## 0b — Branching
|
||||
|
||||
Branch the remainder of the pipeline based on `SLEEP_MODE`:
|
||||
|
||||
- **`local-only`** — skip Phases 1, 2, 3, 4 entirely. No git provider,
|
||||
no SSH key, no repo URL, no deploy-key walkthrough, no test push.
|
||||
Jump directly to Phase 0b (time) → Phase 3b (deep-sleep cadence,
|
||||
adapted for local per phase-3b-deep-sleep.md) → Phase 5 (trigger,
|
||||
emits only CronCreate).
|
||||
- **`remote-only`** — proceed through Phase 0b (time) → Phase 1 →
|
||||
Phase 2 → Phase 3 → Phase 3b → Phase 4 → Phase 5 (emits only
|
||||
`/schedule create`).
|
||||
- **`hybrid`** — same full pipeline as `remote-only`, but Phase 5
|
||||
emits BOTH a CronCreate block AND a `/schedule create` block, with
|
||||
two sequential AskUserQuestions (one per trigger path).
|
||||
|
||||
## 0c — Implication note
|
||||
|
||||
Print a one-line reminder before continuing:
|
||||
|
||||
```
|
||||
SLEEP_MODE = <pick>. Local cron uses this Mac; remote trigger uses a
|
||||
cloud Claude Code agent. Hybrid runs both; the two paths write to
|
||||
different paths and are idempotent.
|
||||
```
|
||||
|
||||
No second AskUserQuestion — the pick is final. Re-running the wizard
|
||||
lets the user change modes later.
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- Exactly ONE `AskUserQuestion` in this phase.
|
||||
- `SLEEP_MODE ∈ {local-only, remote-only, hybrid}`.
|
||||
- If `local-only`, Phases 1-4 MUST be skipped by the caller.
|
||||
- If `remote-only` or `hybrid`, the full pipeline continues at Phase 0b.
|
||||
79
skills/sleep-setup/phase-0b-time.md
Normal file
79
skills/sleep-setup/phase-0b-time.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Phase 0b — Sleep time picker
|
||||
|
||||
Ask the user to pick the local-time trigger for nightly consolidation.
|
||||
Runs immediately after Phase 0 (mode pick), before any git setup.
|
||||
|
||||
## 0b.1 — Time click
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "When should nightly consolidation run (local time)?",
|
||||
"header": "Sleep time",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{"label": "03:00", "description": "Classical REM peak — default"},
|
||||
{"label": "00:00", "description": "Midnight — end of calendar day"},
|
||||
{"label": "05:00", "description": "Pre-dawn — fresh morning report"},
|
||||
{"label": "23:00", "description": "Late evening — before overnight sync"},
|
||||
{"label": "21:00", "description": "Early evening — for early sleepers"},
|
||||
{"label": "Custom", "description": "Pick exact HH:MM (24h format) on next prompt"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 0b.2 — Store or branch
|
||||
|
||||
- Non-custom pick → store as `SLEEP_TIME_LOCAL` verbatim (e.g. `03:00`).
|
||||
- `Custom` → emit follow-up `AskUserQuestion` with `freeText`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "Enter the local trigger time in HH:MM (24h). Example: 04:15 for 4:15 AM.",
|
||||
"header": "Custom time",
|
||||
"freeText": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 0b.3 — Validation
|
||||
|
||||
Validate with regex `^([01][0-9]|2[0-3]):[0-5][0-9]$`:
|
||||
|
||||
```bash
|
||||
if ! echo "$SLEEP_TIME_LOCAL" | grep -qE '^([01][0-9]|2[0-3]):[0-5][0-9]$'; then
|
||||
# invalid — re-ask with the same freeText prompt
|
||||
# accept any leading zeros; reject "3:00" (must be "03:00"), "24:00", "12:60"
|
||||
echo "Invalid time '$SLEEP_TIME_LOCAL'. Expected HH:MM with leading zeros (e.g. 03:00, 23:59)."
|
||||
# loop back to 0b.2 Custom freeText prompt
|
||||
fi
|
||||
```
|
||||
|
||||
Retry loop: if invalid, re-emit the freeText prompt up to 3 times. After
|
||||
3 failures, fall back to `03:00` and log `SLEEP_TIME_LOCAL defaulted to
|
||||
03:00 after 3 invalid inputs`.
|
||||
|
||||
## 0b.4 — Confirmation line
|
||||
|
||||
Once validated, print:
|
||||
|
||||
```
|
||||
SLEEP_TIME_LOCAL = <HH:MM> (this Mac's local time). Phase 5 will use
|
||||
this value for the CronCreate expression and/or the UTC conversion for
|
||||
the remote `/schedule` trigger.
|
||||
```
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- At least ONE `AskUserQuestion` (two if Custom picked).
|
||||
- `SLEEP_TIME_LOCAL` matches `^([01][0-9]|2[0-3]):[0-5][0-9]$`.
|
||||
- No unclamped / unvalidated values stored. Invalid input either
|
||||
re-prompts or falls back to `03:00` with an audit line.
|
||||
|
|
@ -4,6 +4,19 @@ Collect three pure-click decisions for Phase C (system consolidation):
|
|||
cadence, fork mode, store backend. All three are `AskUserQuestion`
|
||||
batches — zero free text (frequency "custom" is the single exception).
|
||||
|
||||
## Mode-dependent behaviour
|
||||
|
||||
If `SLEEP_MODE == local-only` (set in Phase 0), the fork mode question
|
||||
in §3b.2 gets an EXTRA third option `plan+local-patch` that applies
|
||||
auto-resolvable changes directly to `~/.claude/` files (after user
|
||||
confirm at morning) instead of committing to a git branch. Cadence
|
||||
(§3b.1) and store backend (§3b.3) are still asked, but the store
|
||||
backend defaults to `filesystem` for local-only — the user can still
|
||||
pick another if they want a secondary backup path.
|
||||
|
||||
For `remote-only` / `hybrid` the fork mode stays as the original 2
|
||||
options (plan only / plan + fork branch).
|
||||
|
||||
## 3b.1 — Deep-sleep cadence
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
|
@ -39,7 +52,8 @@ Store as `DEEP_SLEEP_CRON_DAYS`:
|
|||
|
||||
## 3b.2 — Fork output mode
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
For `SLEEP_MODE ∈ {remote-only, hybrid}`, emit ONE `AskUserQuestion`
|
||||
with 2 options:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -57,7 +71,30 @@ Emit ONE `AskUserQuestion`:
|
|||
}
|
||||
```
|
||||
|
||||
Store as `DEEP_SLEEP_WITH_FORK` ∈ {0, 1}.
|
||||
For `SLEEP_MODE == local-only`, emit ONE `AskUserQuestion` with 3
|
||||
options (adds `plan+local-patch`):
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "Fork output with applied changes?",
|
||||
"header": "Deep-sleep fork (local mode)",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{"label": "Plan only (Recommended)", "description": "Read markdown in the morning; decide by hand"},
|
||||
{"label": "Plan + fork branch", "description": "Also generate deep-sleep/YYYY-MM-DD branch (needs a local git repo under ~/.claude/)"},
|
||||
{"label": "Plan + local-patch", "description": "Auto-resolvable changes applied directly to ~/.claude/ after morning confirm — no git branch needed"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Store as `DEEP_SLEEP_WITH_FORK` ∈ {0, 1, 2}:
|
||||
- `0` — Plan only
|
||||
- `1` — Plan + fork branch
|
||||
- `2` — Plan + local-patch (local-only mode only)
|
||||
|
||||
## 3b.3 — Memory-repo backend
|
||||
|
||||
|
|
@ -110,7 +147,8 @@ free-text fields (one-off — unavoidable; S3 has no SSH-like default).
|
|||
## 3b.5 — Verify-criterion
|
||||
|
||||
- `DEEP_SLEEP_CRON_DAYS ∈ {0,1,3,7,14, or 1..=90}` for custom.
|
||||
- `DEEP_SLEEP_WITH_FORK ∈ {0, 1}`.
|
||||
- `DEEP_SLEEP_WITH_FORK ∈ {0, 1}` for `remote-only` / `hybrid`;
|
||||
`∈ {0, 1, 2}` for `local-only` (2 = plan+local-patch).
|
||||
- `STORE_BACKEND ∈ {github, forgejo, gitea, filesystem, s3}`.
|
||||
- `~/.claude/agents/_primitives/store-config.toml` exists and has
|
||||
the active backend set.
|
||||
|
|
|
|||
|
|
@ -1,47 +1,100 @@
|
|||
# Phase 5 — Emit `/schedule create` command
|
||||
# Phase 5 — Emit trigger (CronCreate and/or `/schedule create`)
|
||||
|
||||
Render the ready-to-paste nightly trigger and ask whether to run it now,
|
||||
copy and do later, or skip to local-only mode.
|
||||
Render the ready-to-paste nightly trigger(s) and ask how to register
|
||||
them. Branches on `SLEEP_MODE` (set in Phase 0) and uses
|
||||
`SLEEP_TIME_LOCAL` (set in Phase 0b).
|
||||
|
||||
## 5a — Load template
|
||||
## 5a — Load template (remote path only)
|
||||
|
||||
Read `_primitives/templates/sleep-trigger-prompt.md` from the kit install
|
||||
path:
|
||||
If `SLEEP_MODE ∈ {remote-only, hybrid}`, read the cloud-agent template
|
||||
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).
|
||||
template in this phase file (see 5f).
|
||||
|
||||
## 5b — Compute UTC cron
|
||||
For `SLEEP_MODE == local-only` skip 5a entirely — no remote template is
|
||||
rendered.
|
||||
|
||||
Local target: `03:00` user-local time, every day.
|
||||
## 5b — Parse local time
|
||||
|
||||
`SLEEP_TIME_LOCAL` has format `HH:MM` (validated in Phase 0b). Convert
|
||||
to minutes-past-midnight. The `10#` prefix prevents bash from
|
||||
interpreting `08` / `09` as invalid octal:
|
||||
|
||||
```bash
|
||||
hh=${SLEEP_TIME_LOCAL%:*}
|
||||
mm=${SLEEP_TIME_LOCAL#*:}
|
||||
local_minutes=$((10#$hh * 60 + 10#$mm))
|
||||
```
|
||||
|
||||
## 5c — Compute UTC cron (remote path only)
|
||||
|
||||
Only needed if `SLEEP_MODE ∈ {remote-only, hybrid}`. CronCreate on the
|
||||
Mac uses local time directly, so `local-only` skips this block.
|
||||
|
||||
```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")
|
||||
SLEEP_CRON_UTC=$(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.
|
||||
## 5d — Render blocks per mode
|
||||
|
||||
## 5c — Render placeholders
|
||||
### Mode: `local-only`
|
||||
|
||||
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.
|
||||
Render ONE fenced `CronCreate` snippet (no `/schedule`). The cron
|
||||
expression uses the user's local time directly — CronCreate runs on
|
||||
this Mac, not in UTC:
|
||||
|
||||
## 5d — Click
|
||||
```
|
||||
CronCreate expression: <mm> <hh> * * * (local time on this Mac)
|
||||
Prompt body:
|
||||
Run /self-audit --cross-session on ~/.claude/memory/traces/.
|
||||
Duration budget: 60 min max.
|
||||
Always write summary to ~/.claude/memory/sleep-report-YYYY-MM-DD.md.
|
||||
If >=3 recurring patterns detected, append a dated block to
|
||||
~/.claude/memory/audit-backlog.md (section per RULE 0.14).
|
||||
Invariants: append-only traces; no fabricated findings; skip
|
||||
analysis if CWD was under a banned-project path.
|
||||
```
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
Where `<mm>` and `<hh>` are the values from 5b. Emit ONE
|
||||
`AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "Register the local CronCreate now?",
|
||||
"header": "Local cron",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{"label": "Create CronCreate now", "description": "Invoke CronCreate with the rendered body"},
|
||||
{"label": "Copy, create later", "description": "I'll register the cron myself with the snippet above"},
|
||||
{"label": "Skip (no local cron)", "description": "No scheduled analysis — manual /self-audit only"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Store `SCHEDULE_ACTION`:
|
||||
- `Create CronCreate now` → `local-cron-created`
|
||||
- `Copy, create later` → `local-cron-copy-later`
|
||||
- `Skip` → `local-cron-skipped`
|
||||
|
||||
### Mode: `remote-only`
|
||||
|
||||
Render ONE fenced `/schedule create` block using the template from 5a.
|
||||
Replace `{REPO_URL}` with `REPO_URL` and `{UTC_CRON}` with
|
||||
`SLEEP_CRON_UTC`. Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -52,7 +105,7 @@ Emit ONE `AskUserQuestion`:
|
|||
"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": "Copy, run later", "description": "I'll paste into /schedule create myself"},
|
||||
{"label": "Skip (local-only)", "description": "Just push traces; no nightly consolidation"}
|
||||
]
|
||||
}
|
||||
|
|
@ -60,22 +113,40 @@ Emit ONE `AskUserQuestion`:
|
|||
}
|
||||
```
|
||||
|
||||
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".
|
||||
Store `SCHEDULE_ACTION`:
|
||||
- `Run /schedule now` → `remote-run-now`
|
||||
- `Copy, run later` → `remote-copy-later`
|
||||
- `Skip` → `remote-skipped`
|
||||
|
||||
## 5e — Fallback inline template (if kit missing the file)
|
||||
### Mode: `hybrid`
|
||||
|
||||
Render BOTH blocks (CronCreate first, then `/schedule create`). Emit
|
||||
TWO sequential `AskUserQuestion` batches — first the local question
|
||||
from mode `local-only` (section 5d.local), then the remote question
|
||||
from mode `remote-only` (section 5d.remote).
|
||||
|
||||
Store `SCHEDULE_ACTION` as a composite, e.g.
|
||||
`local-cron-created+remote-run-now`,
|
||||
`local-cron-copy-later+remote-skipped`,
|
||||
`local-cron-skipped+remote-copy-later`, etc.
|
||||
|
||||
## 5e — Render placeholders (remote path only)
|
||||
|
||||
For `remote-only` / `hybrid`: replace `{REPO_URL}` and `{UTC_CRON}` in
|
||||
the template. Print the rendered prompt inside a fenced code block so
|
||||
the user can one-click-copy.
|
||||
|
||||
For `local-only`: no placeholders to render — the CronCreate body is
|
||||
self-contained in 5d.
|
||||
|
||||
## 5f — Fallback inline template (remote path, if kit missing file)
|
||||
|
||||
If `~/.claude/agents/_primitives/templates/sleep-trigger-prompt.md` is
|
||||
absent, use this minimal inline prompt:
|
||||
|
||||
```
|
||||
Clone: <REPO_URL>
|
||||
At UTC <utc_cron>:
|
||||
At UTC <SLEEP_CRON_UTC>:
|
||||
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
|
||||
|
|
@ -89,7 +160,16 @@ missing from kit install; using fallback" so the user can re-install.
|
|||
|
||||
## Verify-criterion
|
||||
|
||||
- Exactly ONE `AskUserQuestion`.
|
||||
- `local-only`: exactly ONE `AskUserQuestion`; exactly ONE fenced
|
||||
CronCreate block; no `/schedule` rendered.
|
||||
- `remote-only`: exactly ONE `AskUserQuestion`; exactly ONE fenced
|
||||
`/schedule create` block; no CronCreate rendered.
|
||||
- `hybrid`: exactly TWO `AskUserQuestion` batches (local first,
|
||||
remote second); both blocks rendered.
|
||||
- Cron expression on local path uses `SLEEP_TIME_LOCAL` directly (no
|
||||
UTC conversion).
|
||||
- Cron expression on remote path uses `SLEEP_CRON_UTC` from 5c.
|
||||
- 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.
|
||||
- `SCHEDULE_ACTION` set per mode rules above.
|
||||
- The final report block from SKILL.md is emitted with real values,
|
||||
including `Mode: <SLEEP_MODE>` and `Time (local): <SLEEP_TIME_LOCAL>`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue