diff --git a/.claude/plugins/onebrain/.claude-plugin/plugin.json b/.claude/plugins/onebrain/.claude-plugin/plugin.json index 49e97039..9d58e747 100644 --- a/.claude/plugins/onebrain/.claude-plugin/plugin.json +++ b/.claude/plugins/onebrain/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "onebrain", - "version": "2.4.9", + "version": "2.4.10", "description": "OneBrain — Where human and AI thinking become one. A powerful thinking partner powered by AI synergy.", "author": { "name": "OneBrain Contributors" diff --git a/.claude/plugins/onebrain/INSTRUCTIONS.md b/.claude/plugins/onebrain/INSTRUCTIONS.md index 68babf5c..8b9a7a8e 100644 --- a/.claude/plugins/onebrain/INSTRUCTIONS.md +++ b/.claude/plugins/onebrain/INSTRUCTIONS.md @@ -58,6 +58,7 @@ If `[agent_folder]/MEMORY.md` has no `## Identity & Personality` section, onboar 07-logs/ Logs split by type: session/YYYY/MM/ Session logs (YYYY-MM-DD-session-NN.md) checkpoint/ Stop-hook scratch (flat; consumed by /wrapup) + pause/ Pause-thread snapshots (flat; _active.md pointer + YYYY-MM-DD-{slug}-pause-NN.md) update/ Release history (flat, version-named) log/YYYY/MM/ Skill log entries (recap, doctor, memory, ...) attachments/ Copied files from /import --attach (pdf/, images/, video/) @@ -73,6 +74,8 @@ TASKS.md Live task dashboard (created by /tasks, read-only query block - Archive items: `[archive_folder]/YYYY/MM/filename.md` (organized by date archived) - Session logs: `[logs_folder]/session/YYYY/MM/YYYY-MM-DD-session-NN.md` - Checkpoints: `[logs_folder]/checkpoint/YYYY-MM-DD-{session_token}-checkpoint-NN.md` (flat, auto-generated by hooks, not manual) +- Pause snapshots: `[logs_folder]/pause/YYYY-MM-DD-{slug}-pause-NN.md` (flat, NN scoped to slug across all dates) +- Pause pointer: `[logs_folder]/pause/_active.md` (single line containing the active slug) - Update logs: `[logs_folder]/update/YYYY-MM-DD-update-vX.Y.Z.md` (flat, written by /update) - Skill logs: `[logs_folder]/log/YYYY/MM/YYYY-MM-DD-{skill}.md` (append per day; some have discriminator e.g. `distill-{slug}`, `qmd-{subcommand}`) - Inbox items: `[inbox_folder]/YYYY-MM-DD-topic.md` (flat, no subfolders) @@ -149,6 +152,8 @@ These workflows are documented in `.claude/plugins/onebrain/skills/`: | `/tasks` | `tasks/SKILL.md` | Create or update live task dashboard (TASKS.md) and open in Obsidian | user asks to view the task dashboard, regenerate TASKS.md, or open it in Obsidian | | `/moc` | `moc/SKILL.md` | Create or update vault portal (MOC.md) and open in Obsidian | user asks to update the vault map | | `/wrapup` | `wrapup/SKILL.md` | Wrap up session → session log | explicit `/wrapup` command only — end-of-session signals are handled silently by Auto Session Summary | +| `/pause` | `pause/SKILL.md` | Save snapshot of long-running work to resume later | user signals pause of long task ("pause", "หยุดก่อน", "step away", "ทิ้งงานไว้") | +| `/resume` | `resume/SKILL.md` | Load active pause thread state | user signals resume of paused work ("resume", "กลับมาทำต่อ", "pick up", "ที่ค้างไว้") | | `/learn` | `learn/SKILL.md` | Teach the agent : facts or behavioral preferences | user tells the agent to remember or learn something | | `/memory-review` | `memory-review/SKILL.md` | Interactive memory pruning | (manual only) | | `/clone` | `clone/SKILL.md` | Package agent context for vault transfer | (manual only) | @@ -178,6 +183,8 @@ These agents live in `.claude/plugins/onebrain/agents/` and are dispatched autom When a user message clearly maps to a skill, invoke it directly — no `/command` needed. If intent is ambiguous, use AskUserQuestion to confirm before invoking. When trigger conditions overlap, prefer the lighter-weight skill (e.g. `/capture` over `/braindump`, `/bookmark` over `/summarize`). Skills marked "manual only" require explicit `/command` always. +**/pause vs /capture vs /wrapup:** Use `/pause` only when work in progress will continue across multiple sessions. `/capture` is for a single idea unrelated to ongoing work. `/wrapup` is terminal — when the work is done. If unsure between `/pause` and `/capture`, prefer `/capture` (lighter weight). Routing keywords for `/pause`: "pause", "step away", "ทิ้งงานไว้", "ค่อยมาทำต่อ", "long task". Routing keywords for `/resume`: "resume", "continue", "pick up", "ทำต่อ", "ที่ค้างไว้". + ## Search Strategy If qmd MCP tools are available (`mcp__plugin_onebrain_qmd__query` in tool list): @@ -240,11 +247,12 @@ On weekends: lighter, less task-focused tone. **No-repeat rule:** don't ask abou - Glob `[inbox_folder]/*.md` → count files as `inbox_count` - Run the Grep tool **twice** — once with `path: "[projects_folder]"` and once with `path: "[inbox_folder]"`, both with the pattern `- \[ \] .*📅 [0-9]{4}-[0-9]{2}-[0-9]{2}` and `output_mode: "content"`. Combine the two result sets. (Grep handles UTF-8 correctly on every platform; the previous Bash shell-out relied on `LC_ALL=en_US.UTF-8` and GNU grep BRE semantics, both Bash-only.) Keep only tasks where date ≤ today; group overdue first, then due today - Run `onebrain orphan-scan "[logs_folder]" "[session_token]"` (from vault root) → parse JSON output; read `orphan_count` field. JSON shape: `{"orphan_count":N}`. If the command fails or is unavailable, fall back to a structure-aware glob: if `[logs_folder]/checkpoint/` exists, glob `[logs_folder]/checkpoint/*-checkpoint-*.md` (post-v2.4.0 flat layout); else glob `[logs_folder]/**/*-checkpoint-*.md` (pre-v2.4.0 nested layout — multi-vault user on an unmigrated vault). Then discard files whose date has a non-auto-saved session log (look in `[logs_folder]/session/YYYY/MM/` for post-v2.4.0, or `[logs_folder]/YYYY/MM/` for pre-v2.4.0), and count distinct session tokens among remaining files. +- Read `[logs_folder]/pause/_active.md` if present → parse single-line content as `active_pause_slug`. If absent, set `active_pause_slug = null`. Then if non-null: glob `[logs_folder]/pause/*-{active_pause_slug}-pause-*.md` and count them as `active_pause_count`; read the latest file's `date` frontmatter as `active_pause_last_date`. - **Legacy structure detection (post-v2.4.0):** Check whether `[logs_folder]/session/` exists (any of the new top-level subfolders works as a sentinel; `session/` is the most representative). If it does NOT exist AND `[logs_folder]/YYYY/` does exist (legacy structure pre-v2.4.0), set `vault_structure_legacy = true`. If both `session/` and a legacy `YYYY/` exist (partial migration), still treat `vault_structure_legacy = false` — `/update` will resume cleanup on next run. If neither exists (fresh vault), `vault_structure_legacy = false`. **Step 4 — Send startup status (after Step 3 completes):** -If inbox_count = 0 and orphan_count = 0 and qmd_unembedded = 0 and vault_structure_legacy = false and no tasks found: show nothing after the greeting. +If inbox_count = 0 and orphan_count = 0 and qmd_unembedded = 0 and active_pause_slug is null and vault_structure_legacy = false and no tasks found: show nothing after the greeting. Otherwise, append after the greeting: @@ -252,6 +260,7 @@ Otherwise, append after the greeting: 📥 inbox [N] ← omit if inbox_count = 0 📋 [N] orphan session(s) — /wrapup? ← omit if orphan_count = 0 ⚠️ qmd: [N] doc(s) need embedding — /qmd embed ← omit if qmd_unembedded = 0 +📂 active pause: {active_pause_slug} ({active_pause_count} snapshots, last {active_pause_last_date}) — /resume? ← omit if active_pause_slug is null ⚠️ vault structure outdated — run /update to migrate ← omit if vault_structure_legacy = false Pending tasks: @@ -335,12 +344,12 @@ Different commands have different verbosity expectations. Match output to the pr | Profile | Commands | Behavior | |---------|----------|----------| -| **Capture** | `/capture`, `/braindump`, `/bookmark`, `/learn` | Write the note, confirm done in 1 line. No elaboration. | +| **Capture** | `/capture`, `/braindump`, `/bookmark`, `/learn`, `/pause` | Write the note/snapshot, confirm done. /pause adds the active-thread line. No elaboration. | | **Automated** | cron jobs, Auto Session Summary, `/wrapup` | Structured output only (bullets/sections). No commentary. Under 300 words. | -| **Interactive** | `/research`, `/connect`, `/consolidate`, `/reading-notes`, `/weekly`, `/distill`, `/recap` | Normal verbosity : depth matches task complexity. | +| **Interactive** | `/research`, `/connect`, `/consolidate`, `/reading-notes`, `/weekly`, `/distill`, `/recap`, `/resume` | Normal verbosity : depth matches task complexity. | | **Diagnostic** | `/doctor` | Structured report output. No meta-commentary. Lead with findings. | | **Config/Setup** | `/onboarding`, `/tasks`, `/moc`, `/qmd` | Confirm actions taken. No verbose explanation unless asked. | -> **Terminal rendering:** The Obsidian terminal plugin renders markdown natively (tables, bold, headers, code blocks). Interactive and Diagnostic profiles should use full markdown. Capture profile: 1-line plain-text confirm only. Automated profile: no headers — output is read async via Telegram where markdown rendering varies. +> **Terminal rendering:** The Obsidian terminal plugin renders markdown natively (tables, bold, headers, code blocks). Interactive and Diagnostic profiles should use full markdown. Capture profile: 1-line plain-text confirm only (exception: `/pause` emits a 2-line confirm + 2-line user instruction block per its SKILL.md Step 6). Automated profile: no headers — output is read async via Telegram where markdown rendering varies. For cron/automated agents specifically: output is read by the user async (often via Telegram) : lead with the content, skip all meta-commentary about what you're doing. diff --git a/.claude/plugins/onebrain/skills/doctor/SKILL.md b/.claude/plugins/onebrain/skills/doctor/SKILL.md index 2f4e44bb..63ad1a89 100644 --- a/.claude/plugins/onebrain/skills/doctor/SKILL.md +++ b/.claude/plugins/onebrain/skills/doctor/SKILL.md @@ -130,6 +130,52 @@ Only run when vault.yml contains a `schedule:` block. Skip entirely otherwise. - **Schedule drift** — Read `vault.yml` `schedule:` block. For each entry, check that the corresponding launchd plist exists at `~/Library/LaunchAgents/com.onebrain..plist` where `labelSafe` strips leading `/` from `entry.skill` and replaces non-`[a-zA-Z0-9-]` chars with `-`. If any entry's plist is missing → 🟡 drift — suggest `onebrain register-schedule`. If any installed plist no longer matches a vault.yml entry (stale orphan) → 🟡 stale plist — suggest `onebrain register-schedule --remove` then re-register. - **One-shot reachability** — For each entry with `at:` (one-shot), verify the timestamp has not already passed. If passed and the plist still exists → 🟡 expired one-shot not cleaned up — suggest `onebrain register-schedule --remove` to clear the stale plist (the self-delete shell may have failed to run). +### Pause: Orphan Pointer + +Check: `[logs_folder]/pause/_active.md` exists but no pause file matches its slug. + +How to detect: +1. Read `_active.md`. If absent → skip. +2. Parse slug. Glob `[logs_folder]/pause/*-{slug}-pause-*.md`. If empty match → orphan pointer. + +Report (if orphan): +``` +⚠️ Pause pointer references `{slug}` but no snapshot files exist. + Fix: rm 07-logs/pause/_active.md (or create initial /pause) +``` + +### Pause: Missing Pointer + +Check: pause files exist but `_active.md` is missing. + +How to detect: +1. Glob `[logs_folder]/pause/*-pause-*.md`. If empty → skip. +2. If `_active.md` exists → skip. +3. Otherwise: missing pointer. Identify slug(s) from filenames. + +Report (if missing): +``` +⚠️ Pause files exist but no active pointer: + - {slug-1} (N snapshots, latest YYYY-MM-DD) + - {slug-2} (M snapshots, latest YYYY-MM-DD) + Fix: echo "{chosen-slug}" > 07-logs/pause/_active.md +``` + +### Pause: Idle Thread + +Check: active pause thread has had no new activity (no new pause file, no `/resume`) for > 14 days. + +How to detect: +1. Read `_active.md`. If absent → skip. +2. Glob pause files for the slug. Get max date prefix from filenames. +3. If `(today - max_date).days > 14` → idle. + +Report (if idle): +``` +⚠️ Pause thread `{slug}` idle for {N} days (last snapshot YYYY-MM-DD). + Fix: /wrapup to consolidate, or /pause to refresh, or /resume to continue +``` + --- ## Step 3: Report Findings diff --git a/.claude/plugins/onebrain/skills/pause/SKILL.md b/.claude/plugins/onebrain/skills/pause/SKILL.md new file mode 100644 index 00000000..c1d58f21 --- /dev/null +++ b/.claude/plugins/onebrain/skills/pause/SKILL.md @@ -0,0 +1,118 @@ +--- +name: pause +description: "Save a snapshot of long-running work mid-flight to resume later in a future session. Use when user signals they need to pause a long task and continue another time — 'pause this work', 'หยุดก่อน', 'พักงานนี้ก่อน', 'พักไว้ก่อน', 'step away', 'ทิ้งงานไว้ก่อน', 'ค่อยมาทำต่อ'. Writes a snapshot to 07-logs/pause/ but does NOT end the session or clear context. Do NOT use for: ending a session (use /wrapup), capturing a single idea (use /capture), saving a memory (use /learn)." +schedulable: false +--- + +# /pause — Save Long-Task Snapshot (TL;DR) + +Writes a per-thread snapshot of the current work to `[logs_folder]/pause/` so the next session can `/resume` and pick up seamlessly. Multiple pause files accumulate across days for the same task; `/wrapup` later consolidates them all into one session log. + +**Mental model:** `/pause` = SAVE snapshot. It does NOT end the session and does NOT clear context. User runs `/clear` manually when ready to free context. + +--- + +## Scope + +- /pause writes pause snapshot files only. It does NOT touch checkpoints, session logs, MEMORY.md, or memory/ files. +- It DOES update the pointer file `[logs_folder]/pause/_active.md` to set/confirm the active thread slug. + +--- + +## Step 1: Determine Active Thread Slug + +1. Read `[logs_folder]/pause/_active.md` if present. The file is plain text — one line — containing a kebab-case slug. +2. If the file exists and is non-empty: use that slug as `active_slug`. +3. If absent / empty: + - Agent reviews session context → propose a kebab-case slug from the dominant topic (e.g. `cli-refactor`, `oracle-borrows`) + - Use `AskUserQuestion` to confirm. Question: "Active thread name? Suggested: ``" with options: (a) accept suggestion, (b) type a different slug + - On confirmation, set `active_slug = ` and continue +4. If `/pause --task=` was invoked with an explicit slug: + - If `_active.md` is empty or contains the same slug → set `active_slug = `, skip warning + - If `_active.md` contains a different slug → use `AskUserQuestion`: "⚠️ Active thread: `` (N unmerged snapshots). Switch to ``? (y/N)". On `y` → set `active_slug = `. On `n` → abort silently with message "Pause aborted — active thread unchanged." + +--- + +## Step 2: Compute Next NN + +1. Glob `[logs_folder]/pause/*-{active_slug}-pause-*.md` (no date filter — NN is scoped to slug across all dates). +2. Parse the `NN` segment (the two-digit number before `.md`) from each filename. +3. `next_nn` = `max(NN parsed) + 1`. If no files → `next_nn = 01`. Always zero-pad to 2 digits. + +--- + +## Step 3: Generate Snapshot Content + +Review the current conversation since the last pause file of `active_slug` (or since session start if none). Fill the body sections following `skills/startup/references/session-formats.md` → Pause File Format. + +**Required sections (in order):** + +- `## Where I Stopped` — 1–2 sentences: file being edited, decision pending, last test run, current focus +- `## Resume With` — one concrete next action: specific file path, command, decision point. Must be actionable as the FIRST thing on resume +- `## What We Worked On` — 2–3 sentences describing the pause-interval's focus +- `## Key Decisions` — bullet list (every unique decision since last pause, full detail) +- `## Insights & Learnings` — omit if none +- `## Action Items` — `- [ ] task 📅 YYYY-MM-DD` format +- `## Open Questions` — omit if none + +**Word cap:** 350 words total. Preservation rule: every unique decision/action/question since last pause MUST appear — deduplication only, no summarization. + +--- + +## Step 4: Write the Pause File + +1. Today's date as `YYYY-MM-DD`. +2. Ensure directory exists: `mkdir -p [logs_folder]/pause/` +3. Get `session_token` from agent context (run `onebrain session-init` to recover if missing). **If `session-init` fails or returns no token:** abort the write. Do NOT proceed to Step 5. Output: `⚠️ Could not determine session token. Snapshot not saved — try again or run /doctor.` +4. Write to `[logs_folder]/pause/YYYY-MM-DD-{active_slug}-pause-{next_nn}.md`: + +```yaml +--- +tags: [pause, session-log] +date: YYYY-MM-DD +session_token: +task_slug: +pause: +trigger: manual +--- +``` + +Followed by the body sections from Step 3. + +**If the file write fails** (disk full, permission denied, etc.): abort the skill. Do NOT proceed to Step 5 (do not update `_active.md` — that would create an instant orphan pointer). Output: `⚠️ Snapshot failed to save — check disk space or file permissions. Active thread unchanged.` + +--- + +## Step 5: Update Active Pointer + +Write `[logs_folder]/pause/_active.md` with a single line: the `active_slug` value (no trailing newline issues — overwrite is idempotent if slug already matches). + +--- + +## Step 6: Confirm + +Output exactly this format (Capture profile — 1 confirmation block, no elaboration): + +``` +💾 Snapshot saved: 07-logs/pause/YYYY-MM-DD-{slug}-pause-NN.md +📂 Active thread: {slug} (N snapshots) + +คุยต่อได้เลย — ยังไม่ตัด context. +พิมพ์ /clear เมื่อพร้อมพักงาน (snapshot จะรอ session ถัดไป) +``` + +Where `N` = `next_nn` from Step 2 (the total snapshot count for this thread including the one just written). + +--- + +## Auto-Finalize (Called by AUTO-SUMMARY and /wrapup "n" branch) + +When AUTO-SUMMARY or `/wrapup` "n" branch needs to finalize an active thread before writing a daily session log, it calls into this skill's auto-finalize path. Three skip conditions — if ANY is true, skip: + +1. **No-activity:** Glob `[logs_folder]/checkpoint/*-{current_session_token}-checkpoint-*.md` — if empty, the session produced no work; skip. +2. **Already-captured-this-session:** Find the latest pause file of `active_slug`. If its frontmatter `session_token` equals current session token AND no checkpoint file mtime > pause file mtime, the current session's work is already captured by an earlier `/pause`; skip. +3. **No-pause-files-and-untouched:** If no pause file exists yet for `active_slug` AND the newest checkpoint mtime is older than `_active.md` mtime, the slug was set but no thread activity occurred; skip. + +If not skipped: run Steps 2–5 above, but in Step 4 frontmatter use `trigger: auto-finalize` instead of `manual`, and in Step 3's `## Where I Stopped` prepend "Auto-finalized at session end. " to the human-readable text. + +No Step 6 confirm — auto-finalize is silent. diff --git a/.claude/plugins/onebrain/skills/resume/SKILL.md b/.claude/plugins/onebrain/skills/resume/SKILL.md new file mode 100644 index 00000000..a057f889 --- /dev/null +++ b/.claude/plugins/onebrain/skills/resume/SKILL.md @@ -0,0 +1,58 @@ +--- +name: resume +description: "Load the latest snapshot of an active pause thread and announce its state in chat. Use when user signals they want to continue work that was paused — 'resume', 'กลับมาทำต่อ', 'pick up', 'continue from where I left off', 'ที่ค้างไว้'. Reads from 07-logs/pause/. Idempotent — running in same session as the pause is a no-op. Do NOT use for: starting fresh work (just talk), recap of past sessions (use /distill or /search), promoting insights (use /recap)." +schedulable: false +--- + +# /resume — Load Active Pause Thread (TL;DR) + +Reads the latest snapshot of the active pause thread and announces its state in chat so the agent and user can pick up seamlessly. Has no effect if there is no active thread. + +--- + +## Step 1: Resolve Active Thread + +1. Read `[logs_folder]/pause/_active.md`. If absent or empty → output "No active pause. คุยใหม่ได้เลย" and stop. +2. Parse the single-line content as `active_slug`. +3. If `/resume --task=` was invoked with explicit slug → use `` and overwrite `_active.md` to match (this enables switching from one paused thread to another). + +--- + +## Step 2: Locate Latest Pause File + +1. Glob `[logs_folder]/pause/*-{active_slug}-pause-*.md`. +2. Sort by NN descending (tiebreak: date prefix descending). Pick the first. +3. If no file matches (orphan pointer): output "⚠️ Active pointer references `` but no pause file found. Run /doctor." and stop. + +--- + +## Step 3: Idempotency Check + +1. Read the file's frontmatter `session_token`. If frontmatter is absent, malformed, or `session_token` cannot be parsed → skip this idempotency check and proceed to Step 4 (treat as a fresh resume). +2. If `session_token` parsed cleanly AND equals the current session token → output "Still in active thread ``. Already resumed." and stop. Do NOT re-announce content. + +--- + +## Step 4: Load & Announce + +Read the file. Extract: `## Where I Stopped`, `## Resume With`, `## Key Decisions`, count of `## Action Items` items (lines matching `- [ ] `), count of `## Open Questions` items. + +Output (Interactive profile — full markdown rendering, agent treats this as loaded working context for the next user message): + +``` +🔄 Resumed from {slug}-pause-{NN} ({YYYY-MM-DD}) + +**Where I Stopped:** {text} + +**Resume With:** {text} + +**Key Decisions (last pause):** +- {bullet} +- {bullet} + +**Open Questions:** {Q_count} · **Action Items:** {A_count} + +พร้อมทำต่อ — เริ่มเลย +``` + +After announcing, the agent treats the loaded content as the active context for the upcoming user turn. Do NOT re-read the pause file on the next user message unless the user explicitly asks for details not announced. diff --git a/.claude/plugins/onebrain/skills/startup/AUTO-SUMMARY.md b/.claude/plugins/onebrain/skills/startup/AUTO-SUMMARY.md index 591b1908..127b6bc3 100644 --- a/.claude/plugins/onebrain/skills/startup/AUTO-SUMMARY.md +++ b/.claude/plugins/onebrain/skills/startup/AUTO-SUMMARY.md @@ -10,6 +10,13 @@ Run silently (no output) if ALL of these are true: If conditions are met: - Use `session_token` from context if already loaded (set by `onebrain session-init` at startup); if absent, run `onebrain session-init` and use the `SESSION_TOKEN` value. Glob checkpoint files (post-v2.4.0: checkpoints live in flat `[logs_folder]/checkpoint/` regardless of date): `[logs_folder]/checkpoint/YYYY-MM-DD-{session_token}-checkpoint-*.md`. Also yesterday's (handles cross-midnight sessions): compute yesterday's date (accounting for month/year rollover) and glob `[logs_folder]/checkpoint/YYYY-MM-DD_PREV-{session_token}-checkpoint-*.md`. **Read every file in the glob result** and fully incorporate all of their content into the session summary (not just as background context). Any checkpoint file that exists is unmerged by definition — there is no `merged:` filter. Every checkpoint must appear in the summary before it is deleted. - Determine NN: count existing `[logs_folder]/session/YYYY/MM/YYYY-MM-DD-session-*.md` files for today; NN = count + 1, zero-padded to 2 digits (01, 02, …). **Verify** `YYYY-MM-DD-session-NN.md` does not already exist before writing; if it does, increment NN until a free slot is found. + +- **Auto-finalize active pause thread (if any) — runs before the session log write.** Read `[logs_folder]/pause/_active.md`. If absent or empty, skip this step. If a slug is present, apply the three skip conditions from `skills/pause/SKILL.md` → Auto-Finalize section (canonical source — keep in sync): + - No-activity: no checkpoint file exists for current session_token → skip + - Already-captured-this-session: latest pause file's session_token == current AND no checkpoint mtime > pause file mtime → skip + - No-pause-files-and-untouched: no pause file for slug AND newest checkpoint mtime < `_active.md` mtime → skip + + If not skipped: invoke `/pause` auto-finalize path (Steps 2–5 of `/pause`, with `trigger: auto-finalize` in frontmatter and "Auto-finalized at session end. " prefix in `## Where I Stopped`). This preserves the active pause thread's continuity when a session ends without an explicit `/pause`. Silent — no user-visible output. - Write to `[logs_folder]/session/YYYY/MM/YYYY-MM-DD-session-NN.md` using the Session Log Format from `references/session-formats.md`: - Checkpoints found and incorporated → case: **Auto-saved (auto-summary) — checkpoints incorporated** - No checkpoints → case: **Auto-saved (auto-summary) — no checkpoints** diff --git a/.claude/plugins/onebrain/skills/startup/references/session-formats.md b/.claude/plugins/onebrain/skills/startup/references/session-formats.md index 77014b03..56daa6c4 100644 --- a/.claude/plugins/onebrain/skills/startup/references/session-formats.md +++ b/.claude/plugins/onebrain/skills/startup/references/session-formats.md @@ -70,6 +70,65 @@ trigger: stop --- +## Pause File Format + +Written by `/pause` (user-driven snapshot) or auto-finalize before AUTO-SUMMARY / `/wrapup` "n" branch when an active pause thread exists. Filename pattern: `YYYY-MM-DD-{slug}-pause-NN.md` (NN scoped to slug across all dates, not reset per day). + +**Frontmatter:** +```yaml +--- +tags: [pause, session-log] +date: YYYY-MM-DD +session_token: +task_slug: +pause: NN +trigger: manual +--- +``` + +- `trigger: manual` — written by `/pause` (default) +- `trigger: auto-finalize` — written automatically before session end when `_active.md` exists and skip conditions don't apply (see `skills/pause/SKILL.md` §Auto-Finalize) + +`task_slug` and `pause` mirror the filename so frontmatter-only filters work without parsing filenames. + +**Body:** use Shared Body Sections above, PLUS two additional sections at the top: + +```markdown +## Where I Stopped + +1–2 sentences describing the current state of the work: file being edited, decision pending, last test run. + +## Resume With + +One concrete action to perform first on resume — specific file path, command, or decision point. + +## What We Worked On + +[checkpoint section] + +## Key Decisions + +[bullet list] + +## Insights & Learnings + +[omit if none] + +## Action Items + +- [ ] task 📅 YYYY-MM-DD + +## Open Questions + +[bullet list] +``` + +**Word cap:** 350 words total (100 more than checkpoint, to accommodate Where I Stopped + Resume With). + +**Dataview compatibility:** same rule as Checkpoint Format — never write `` `=… `` anywhere in the file. + +--- + ## Session Log Format **Header line** (before body sections): @@ -139,6 +198,20 @@ auto-recovered: true --- ``` +**Thread wrapup — pause snapshots incorporated** (used by: `/wrapup` Thread Wrapup Branch when user confirms `y` on the active-thread prompt): +```yaml +--- +tags: [session-log] +date: YYYY-MM-DD +session_token: +session: NN +synthesized_from_pause: true +pause_slug: +--- +``` + +`pause_slug` records which thread this log consolidates. Body content merges all pause files of the slug + checkpoint files of the current session into one log, following the Preservation rule from /wrapup Step 4. + `session_token` mirrors the token embedded in the filename so cross-references (orphan recovery's `recovery-of:` marker, /distill source-log filtering, /doctor checks) can match by frontmatter without parsing filenames. Source the token from the `session_token` already in agent context (set by `onebrain session-init` at startup); for **Recovered from checkpoints**, source it from the orphan group's parsed token (the same one embedded in the body marker), not the live session's token. **Body marker (required for this case):** the very first body line — placed before `# Session Summary :` — must be the recovery-of marker: diff --git a/.claude/plugins/onebrain/skills/wrapup/SKILL.md b/.claude/plugins/onebrain/skills/wrapup/SKILL.md index 5bcbc970..422d440b 100644 --- a/.claude/plugins/onebrain/skills/wrapup/SKILL.md +++ b/.claude/plugins/onebrain/skills/wrapup/SKILL.md @@ -23,6 +23,40 @@ See `skills/startup/references/session-formats.md` → Session Log Format for fr --- +## Step 0: Active Pause Thread Detection + +Run this BEFORE Step 1. + +1. Read `[logs_folder]/pause/_active.md`. If absent or empty → set `wrapup_mode = "session"`; skip to Step 1 (zero-overhead path for non-pause sessions). +2. If a slug is present: parse the file's single-line content as `slug`. Glob `[logs_folder]/pause/*-{slug}-pause-*.md` → `pause_count` (file count) and derive `first_date` = earliest `YYYY-MM-DD` date prefix among matched files (used in Step 3's question text). +3. Use `AskUserQuestion`: + + Question: "Active pause thread: `{slug}` ({pause_count} snapshots since {first_date}). Wrap up this thread now?" + Options: + - "Yes — consolidate into one session log" (sets `wrapup_mode = "thread"`) + - "No — wrapup today's work only; keep pause thread active" (sets `wrapup_mode = "session"`) + +4. If `wrapup_mode = "session"` AND active pause exists → fall through to **Step 0b: Auto-Finalize Pause** (below) BEFORE proceeding to Step 1. +5. If `wrapup_mode = "thread"` → proceed to Step 1 normally, then branch in Step 4 (see Step 4 modifications below). + +--- + +## Step 0b: Auto-Finalize Pause (Session-mode wrapup with active thread) + +Runs only when `wrapup_mode = "session"` AND `_active.md` exists. + +Apply the three skip conditions from `skills/pause/SKILL.md` → Auto-Finalize section: + +1. **No-activity:** if no checkpoint file exists for current `session_token`, skip Auto-Finalize. +2. **Already-captured-this-session:** if latest pause file's frontmatter `session_token` matches current AND no checkpoint mtime > pause file mtime, skip. +3. **No-pause-files-and-untouched:** if no pause file exists for slug AND newest checkpoint mtime < `_active.md` mtime, skip. + +If not skipped: invoke `/pause` auto-finalize path (Steps 2–5 of `/pause`, with `trigger: auto-finalize` in frontmatter and "Auto-finalized at session end. " prefix in `## Where I Stopped`). + +After Step 0b, continue to Step 1. + +--- + ## Step 1: Gather Checkpoint Context 1. Get today's date as `YYYY-MM-DD`. Extract `YYYY` and `MM`. @@ -156,6 +190,35 @@ Reflect on the conversation that just occurred. Identify: ## Step 4: Write the Session Log +**Branch on wrapup_mode (set in Step 0):** + +- If `wrapup_mode = "session"` → follow the existing flow below (no changes). +- If `wrapup_mode = "thread"` → use the **Thread Wrapup Branch** below instead of the existing flow: + +### Thread Wrapup Branch + +1. Glob `[logs_folder]/pause/*-{slug}-pause-*.md` → read every file in chronological order (date prefix ascending, then NN ascending). Store as `pause_files`. Also derive `first_date` = date prefix of `pause_files[0]` and `last_date` = date prefix of `pause_files[-1]` (used in Step 7 confirm). +2. Combine `pause_files` content + checkpoint content from Step 1 (today's session). Apply the Preservation rule (deduplication only, no summarization) across all of them. +3. Determine session file name per existing Step 2 logic. +4. Write `[logs_folder]/session/YYYY/MM/YYYY-MM-DD-session-NN.md` using the **Thread wrapup — pause snapshots incorporated** frontmatter case from `skills/startup/references/session-formats.md`: + ```yaml + --- + tags: [session-log] + date: YYYY-MM-DD + session_token: + session: NN + synthesized_from_pause: true + pause_slug: + --- + ``` +5. Body: merged content from step 2, using the Shared Body Sections. +6. After successful write, run `onebrain checkpoint reset`. +7. Proceed to Step 4b (action item routing) and onward as normal. + +After Thread Wrapup writes the session log, the existing Step 5 (Checkpoint Cleanup) still runs — checkpoints from Step 1 are deleted. **Plus, in the new Step 5b (below), pause files and `_active.md` are deleted.** + +--- + > **Preservation rule (critical when checkpoints exist):** the session log must preserve **every unique detail** from every checkpoint file read in Step 1. Your job is **deduplication, not summarization**. Two pieces of content are duplicates only if they describe the same fact, decision, learning, action item, or question. When in doubt, keep both — the session log is the long-term archive of the session, and missing a unique decision or insight cannot be recovered later. > > Specifically: @@ -242,6 +305,19 @@ Guard: only delete AFTER confirming the session log write succeeded. Never delet --- +## Step 5b: Pause Cleanup (Thread Wrapup only) + +Runs only when `wrapup_mode = "thread"`. + +After the session log from Step 4 (Thread Wrapup Branch) is written successfully: + +1. Delete every file in `pause_files` from Step 4 Thread Wrapup Branch step 1. +2. Delete `[logs_folder]/pause/_active.md`. + +Guard: only delete AFTER confirming the session log write succeeded. If an individual delete fails, skip silently — `/doctor` will catch stragglers. + +--- + ## Step 6: Recap Reminder At the end of every /wrapup, compute `unrecapped_count` and `last_recapped`: @@ -289,6 +365,31 @@ Skipped routing (no match / tie): · [task text] (omit this block if skipped_tasks is empty; list one line per skipped task) +**If `wrapup_mode = "thread"`:** replace the standard header with: + +``` +────────────────────────────────────────────────────────────── +💾 Thread Wrapped Up — `{slug}` +────────────────────────────────────────────────────────────── +`[logs_folder]/session/YYYY/MM/YYYY-MM-DD-session-NN.md` + +Consolidated {P} pause snapshots from {first_date} to {last_date} + {C} checkpoint(s) from today. +``` + +Where `{P}` is `len(pause_files)`, `{C}` is the checkpoint count from Step 1. Then continue with action-item routing summary, orphan recovery summary, recap reminder as normal. + +**If `wrapup_mode = "session"` AND Step 0b Auto-Finalize ran (not skipped):** add a line after `💾 Session Saved`: + +``` +📂 Auto-finalized pause thread `{slug}` (snapshot {NN}). Thread still active — /resume to continue. +``` + +**If `wrapup_mode = "session"` AND Step 0b Auto-Finalize was skipped due to skip-condition (1, 2, or 3):** add a line after `💾 Session Saved`: + +``` +📂 Pause thread `{slug}` still active ({pause_count} snapshots) — /resume to continue. +``` + Auto-recovered {S} orphan session(s): {YYYY-MM-DD} → `session-NN.md` ({C} checkpoints) (omit this block if none recovered) diff --git a/PLUGIN-CHANGELOG.md b/PLUGIN-CHANGELOG.md index 524529ee..4a801fa0 100644 --- a/PLUGIN-CHANGELOG.md +++ b/PLUGIN-CHANGELOG.md @@ -1,5 +1,5 @@ --- -latest_version: 2.4.9 +latest_version: 2.4.10 released: 2026-05-12 --- @@ -11,6 +11,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). > **Versioning:** Plugin version is tracked in `plugin.json`. Bump when ANY harness config changes — skills, agents, hooks, INSTRUCTIONS, Gemini settings, slash commands, etc. > For CLI binary (`@onebrain-ai/cli`) changes, see [CHANGELOG.md](CHANGELOG.md). +## v2.4.10 — 2026-05-12 + +- feat(pause): new `/pause` skill saves snapshot of long-running work to `07-logs/pause/`; non-terminal — does not clear context +- feat(resume): new `/resume` skill loads active pause-thread state in fresh sessions; idempotent in same-session +- feat(wrapup): Step 0 detects active pause thread; "Yes" branch consolidates all pause files of slug + current checkpoints into one session log +- feat(wrapup, auto-summary): auto-finalize active pause thread before daily session log write (3 skip conditions prevent noise) +- feat(startup): banner shows `📂 active pause: {slug} ({N} snapshots)` when `_active.md` present +- feat(doctor): 3 new pause health checks (orphan pointer, missing pointer, idle > 14d) +- docs(instructions): wire `/pause` `/resume` into skills table, vault structure, file naming, routing, response profiles +- docs(session-formats): add Pause File Format + `synthesized_from_pause` session-log frontmatter case + ## 2.4.9 — 2026-05-12 - `/schedule-add` Step 0 first-run preset selector — Minimal / Essentials / Maintenance Plus / Custom (E15-B)