Skip to content

Auto-Dreaming

sb brain dream synthesizes cross-session insights from episodic memory. By default it runs only when you invoke it. Two automation hooks turn dreaming into a background process so the Insight store stays fresh without manual nudges.

Path 1 — Session-stop hook

brain/mcp/hooks/session_stop.py runs at the end of every Claude Code session. After logging the session_end event it will, by default, kick off a short-lookback dream cycle:

  • Lookback: 1 day — just the last batch of activity.
  • Throttle: 6 hours — a timestamp in state_dir/.last_dream_at blocks back-to-back invocations across rapidly-closed sessions.
  • Failure mode: silent. The hook always lets the session exit.

The hook is registered with async: true in .claude/settings.json, so the dream cycle never blocks the Claude Code UI.

Env flags

Variable Default Effect
SB_HOOK_DREAM on Set to 0, false, no, or off to skip dream.
SB_HOOK_DREAM_LOOKBACK_DAYS 1 Days of episodic history to scan.
SB_HOOK_DREAM_MIN_INTERVAL_HOURS 6 Minimum gap between cycles. Set to 0 to disable throttling.
SB_HOOK_DREAM_INSTRUCTIONS unset Free-text steering (e.g. "focus on coding-style preferences; ignore one-off debugging notes"). Mirrors Anthropic Dreams' instructions field; capped at 4096 chars.

Drop SB_HOOK_DREAM=0 into your shell rc to opt out without editing the project settings.

Path 2 — Cron / launchd

scripts/sb-dream-cron.sh runs sb brain dream --lookback-days 7 --json and appends results to logs/dream.log. The 7-day window catches medium-term patterns that a 1-day session hook would miss.

Example crontab

# Dream daily at 03:00 local
0 3 * * * /opt/SecondBrain/scripts/sb-dream-cron.sh

# Custom lookback window
0 3 * * 0 /opt/SecondBrain/scripts/sb-dream-cron.sh 30   # weekly deep dream

# Steered cycle (lookback + free-text instructions)
0 3 * * * /opt/SecondBrain/scripts/sb-dream-cron.sh 7 "Focus on coding-style preferences; ignore one-off debugging notes"

Example launchd plist (macOS)

Drop into ~/Library/LaunchAgents/com.secondbrain.dream.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key><string>com.secondbrain.dream</string>
    <key>ProgramArguments</key>
    <array>
      <string>/opt/SecondBrain/scripts/sb-dream-cron.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
      <key>Hour</key><integer>3</integer>
      <key>Minute</key><integer>0</integer>
    </dict>
    <key>RunAtLoad</key><false/>
  </dict>
</plist>

Then launchctl load ~/Library/LaunchAgents/com.secondbrain.dream.plist.

How they compose

The two paths are intentionally non-overlapping:

  • The hook keeps short-term patterns hot — what happened today.
  • The cron sweeps medium-term patterns — what's recurred this week.
  • The manual sb brain dream --lookback-days 30 (or higher) remains the right tool for deep retrospectives.

Both paths write through the same Dreamer.dream() code, so insights deduplicate against each other automatically (cosine threshold from intelligence.dedup_threshold, default 0.85).

Steering a cycle

sb brain dream, the hook, and the cron wrapper all accept free-text instructions that mirror Anthropic Dreams' instructions field. Use it to bias what the synthesis pays attention to:

sb brain dream --instructions "Focus on coding-style preferences; ignore one-off debugging notes."

The string is normalized (stripped + truncated to 4096 chars) and folded into the LLM synthesis prompt; the heuristic path records it on the DreamReport and event log for auditability even without a provider.

Reorganizing the LTM (non-destructive rebuild)

sb brain dream surfaces cross-session insights. The companion sb brain reorganize command rewrites the LTM itself, mirroring Anthropic Dreams' core semantic: each cycle produces a reviewable proposal of mutations (merge similar entries, deprecate stale ones, supersede contradicted ones). The live store is never touched until you explicitly accept the proposal.

sb brain reorganize                          # propose merges (stricter threshold)
sb brain reorganize --threshold 0.85         # cast a wider net
sb brain reorganize --instructions "focus on infra notes"
sb brain reorganize --dry-run                # preview, do not save

sb brain reorganize list                     # pending proposals
sb brain reorganize show <proposal_id>       # full mutation list
sb brain reorganize accept <proposal_id>     # apply atomically
sb brain reorganize reject <proposal_id> --reason "false positives"

What the heuristic catches

  • Merge clusters: groups of active LTMs with bag-of-words cosine similarity ≥ --threshold (default 0.92). All members of a cluster collapse into one new active LTM; originals become deprecated with merged_into links pointing at the replacement.
  • Pinned + static LTMs are excluded unconditionally — they're treated as canonical and never proposed for merge.

Safety contract

  • A new cycle always lands in status='pending'. No mutation hits the live store until you accept.
  • accept applies mutations independently; a mutation whose sources were deprecated since the proposal was created is recorded as skipped rather than failing the batch.
  • An accepted or rejected proposal can't be re-applied — generate a fresh proposal instead.
  • Every proposal create / accept / reject lands in the event log with the proposal id and outcome.

The reorganize flow is not wired into the cron / hook automation yet. Accepting mutations is a deliberate human decision; the session-stop hook and cron continue to run insight-surfacing sb brain dream cycles only.

Inspecting results

sb brain insights --status active --limit 20
sb brain insights --type gap                 # unresolved/blocked patterns
sb brain insights --query "<topic>"          # search by text
tail -f logs/dream.log                       # watch cron output

The dream_after_session_end event in the event log records every hook-triggered cycle (look it up via sb or query the event log table directly).