Community写作与编辑github.com

timoncool/dream-skill

针对Claude Code的记忆整合技能——只读的dream pass、复选框/验证器门控、全自动模式、快照+单命令回滚。绝不rm。

dream-skill 是什么?

这是一个专为Claude Code设计的记忆整合项目(Memory Consolidation)技能,包含两个核心部分:`dream`(生成报告与选择)和`wake`(应用选择变更)。该技能通过一个两阶段机制,确保对Claude项目目录`MEMORY.md`及相关文件的修改安全可控。`dream`阶段读取当前状态,生成一个包含独立提议(proposals)的Markdown报告,每个提议有唯一ID和特定动作类型(如更新、合并、删除、提升技能等)。用户可以通过HTML报告中的复选框进行选择,或让内置验证器在`auto mode`下自动生成可信选择JSON。`wake`阶段读取该JSON或用户指定的ID列表,在应用任何变更前创建整个memory目录的完整快照,确保单命令回滚能力。它提供三级确认门控:手动模式需要用户二次确认摘要;`auto mode`跳过确认但自动排除不安全动作(如删除标记为`[UNVERIFIED]`的项);`full auto`则移除更多限制,但对删除操作强制进行预删除备份。该技能支持跨项目操作(通过`project: <slug>`字段)和全局模式,并严格遵守“绝不rm”的承诺——所有删除的文件都会被移动到项目级回收站或归档目录。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add timoncool/dream-skill

Installed? Explore more 写作与编辑 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Wake — 应用从 dream 中选择的结果

在 dream 生成报告并且用户勾选了复选框之后——wake 会接管选择并应用更改。

安全原则

wake 绝不自行决定应用什么内容。选择来源(按优先级):

  1. JSON 文件 DREAM-CHOICES-<date>.json(通过 HTML 中的 Save 按钮生成,或者在 auto mode 下由验证器创建——标记为 "auto": true,请参阅 dream SKILL.md 中的“Auto mode”部分)
  2. 技能参数——wake M1,M3,N2 或 wake all

如果两者都未指定——则询问用户选择什么,不执行任何操作。

工作流程

阶段 1 — 加锁 + 查找报告和选择文件

首先加锁防止竞争(与 dream/SKILL.md 阶段 0 相同)。如果两个会话同时在同一份报告上运行 wake——编辑操作会互相覆盖,MEMORY.md 会变成一团乱。

LOCK_DIR="$CWD_BASH/.wake-lock"
if mkdir "$LOCK_DIR" 2>/dev/null; then
  echo $$ > "$LOCK_DIR/pid"
else
  LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$LOCK_DIR" 2>/dev/null || stat -f %m "$LOCK_DIR") ))
  if [ "$LOCK_AGE" -gt 3600 ]; then
    rm -rf "$LOCK_DIR" && mkdir "$LOCK_DIR" && echo $$ > "$LOCK_DIR/pid"
  else
    echo "WAKE ALREADY RUNNING (lock age ${LOCK_AGE}s) — abort"
    exit 0
  fi
fi
# 在阶段 5 结束时执行:rm -rf "$LOCK_DIR"

首先计算路径(与 dream/SKILL.md 中相同——_BASH 用于 bash 命令,_WIN 用于 Python):

# 计算路径(Win11 Git Bash)
CWD_BASH=$(pwd)                                    # /d/Projects/TEMP
CWD_WIN=$(pwd -W 2>/dev/null || pwd)               # D:/Projects/TEMP(在非 Windows 系统上回退)
SLUG=$(echo "$CWD_WIN" | sed 's|[:/]|-|g')         # D--Projects-TEMP

MEMORY_DIR_BASH="$HOME/.claude/projects/$SLUG/memory"
MEMORY_DIR_WIN=$(cygpath -w "$MEMORY_DIR_BASH" 2>/dev/null | sed 's|\\|/|g' || echo "$MEMORY_DIR_BASH")

test -d "$MEMORY_DIR_BASH" && echo "OK $MEMORY_DIR_WIN" || { echo "MEMORY DIR NOT FOUND — wake cannot proceed without it"; }

# 1. 在 cwd 中查找最新的 dream 报告 MD 文件
LATEST_REPORT=$(ls "$CWD_BASH"/DREAM-REPORT-*.md 2>/dev/null | sort -r | head -1)
if [ -z "$LATEST_REPORT" ]; then
  echo "NO REPORT FOUND — run dream skill first to generate DREAM-REPORT-<date>.md"
  # 不退出——Claude 会读取此输出并自行停止工作流
else
  echo "Report: $LATEST_REPORT"
fi

# 从文件名提取日期:DREAM-REPORT-2026-05-02.md → 2026-05-02
REPORT_DATE=$(basename "$LATEST_REPORT" | sed 's|DREAM-REPORT-||; s|\.md||')

# 2. 查找选择 JSON 文件(优先级:cwd → ~/Downloads/ → ~/Desktop/)
CHOICES=""
for path in "$CWD_BASH/DREAM-CHOICES-$REPORT_DATE.json" "$HOME/Downloads/DREAM-CHOICES-$REPORT_DATE.json" "$HOME/Desktop/DREAM-CHOICES-$REPORT_DATE.json"; do
  if [ -f "$path" ]; then CHOICES="$path"; break; fi
done

# 回退:忽略日期查找最新的 CHOICES 文件
if [ -z "$CHOICES" ]; then
  CHOICES=$(ls -t "$HOME/Downloads/DREAM-CHOICES-"*.json "$CWD_BASH/DREAM-CHOICES-"*.json 2>/dev/null | head -1)
fi
echo "Choices: ${CHOICES:-NOT FOUND}"

**日期验证:**如果 JSON 中的 report_date ≠ MD 文件名中的日期 → 发出 WARN,询问用户:

⚠️ DREAM-CHOICES 中的日期 (2026-05-01) 与最新的 DREAM-REPORT (2026-05-02) 不匹配。仍然使用还是中止?(apply/cancel)

如果没有参数或 JSON 文件:

未找到 DREAM-CHOICES-*.json 且参数中没有 ID。选项:

  1. 打开 HTML 报告 DREAM-REPORT-<date>.html,勾选复选框,点击 💾 Save choices
  2. 或者在聊天中提供 ID:wake M1,M3,N2 或 wake all

阶段 2 — 读取报告 + 解析 JSON 块

完整读取 DREAM-REPORT-<date>.md。JSON 提议约定——请参阅 D:/Projects/TEMP/.claude/skills/dream/references/action_types.md。

**解析(可靠的约定):**查找带有 json 语言的围栏代码块。每个块都是一个单独的提议。

使用 Python 进行解析(比带有反引号的 awk 更可靠):

import re, json, sys
content = open(report_path, encoding='utf-8').read()
# 模式:三个反引号 + json + 换行 + 内容 + 换行 + 三个反引号
pattern = r'```json\n(.*?)\n```'
blocks = re.findall(pattern, content, re.DOTALL)
proposals = []
malformed_indices = []
for i, block in enumerate(blocks):
    try:
        proposals.append(json.loads(block))
    except json.JSONDecodeError as e:
        print(f"WARN: malformed JSON block #{i}: {e}", file=sys.stderr)
        malformed_indices.append(i)
        # 稍后添加到 failed[] 中——记录块索引以备应用日志

你将得到一个对象数组,包含必需字段:

  • id (string, 例如 "M1")
  • category ("memory" | "notes" | "index" | "other")
  • action (其一:update, merge, delete, soft_delete, create_new, extract, remove_links, shorten_lines, add_links, promote_skill, retire_skill, purge_trash)
  • 特定于 Action 的字段(请参阅 dream/SKILL.md 中的“Action types”部分)

构建 map {id: proposal}。

读取已选择项:

  • 如果有 JSON 文件 → selected = json.selected(ID 数组)
  • 如果 args = all → selected = map 中的所有 ID
  • 如果 args = M1,M3,N2 → 按逗号分割(去除空格)

验证:selected 中的每个 ID 都在 map 中。如果不在——发出警告,跳过,添加到“skipped”中。

阶段 3 — 确认摘要(单个门控)

在聊天中显示:

🌅 Wake 准备应用来自 dream 报告 <date> 的 N 项:

记忆整合:

  • M1. 合并 handoff_pikabu_* → project_pikabu_mcp.md(3 个源文件)
  • M3. 软删除 TRASH/*(超过一个月)

Notes 集成:

  • N2. 提取 runware 审计 → reference_runware_audit_2026_04.md

索引重建:

  • I1. 删除死链接:4 个链接

已跳过(不在报告中):<列表(如果有)>

应用?请说“是”/“go”/“apply”。拒绝时——说“停止”/“取消”。

如果所选项目的标题包含 [UNVERIFIED]——在摘要中用 ⚠️ 显示它,并提醒:dream 无法验证过时原因,应用与否取决于用户的信任。

等待确认。这是最终门控——之后将无问题地应用。

自动模式:如果 choices JSON 包含 "auto": true 并且 "validator_verdicts"——门控是信息性的:显示摘要 + 验证器判决,然后不等待确认直接应用。在应用之前,重新检查自动停止列表:从 auto-JSON 的 selected 中排除带有 [UNVERIFIED] 在 title 中且 action=delete、purge_trash 或 promote_skill 的项目,将其添加到 skipped[],reason 为 "auto mode: manual-only action"。如果 auto: true 但没有 validator_verdicts——认为 JSON 可疑,按手动模式运行(等待确认)。在 Wake 日志中添加一行 Gate: auto (validator)。

全自动模式(choices JSON 中的 "auto_level": "full"):取消对 actions 的自动停止列表——delete、purge_trash、promote_skill、retire_skill 等操作可以从 selected 中应用。对 delete 的强制补偿:在清除内容之前将文件复制到回收站——cp "$MEMORY_DIR_BASH/<target>" "$MEMORY_DIR_BASH/TRASH/<target>.pre-delete-$REPORT_DATE.md"——然后才编辑。对于 promote_skill:激活路径必须记录在 Wake 日志中(行 Promoted skills: <name> → <dest>)——回滚技能 = 将其文件夹从 dest 移回 ~/.claude/satori/staging/。即使在 full auto 模式下,标题中包含 [UNVERIFIED] 的项目也会被排除(验证器无权批准它们——出现在 selected 中意味着故障,reason "full auto: [UNVERIFIED] must not be auto-approved")。在 Wake 日志中——Gate: full-auto (validator)。

为什么是单个门控而不是每个项目一个:用户已经通过复选框(或自动模式下由独立验证器)思考过。额外的批准会令人厌烦。但摘要很重要——自 dream 以来可能发生了变化。

阶段 4 — 应用(确认后)

**跨项目解析(全局模式)。**如果提议具有 project: <slug> 字段——目标 memory 目录为 ~/.claude/projects/<slug>/memory/,而不是当前的 $MEMORY_DIR_BASH。如果字段不存在(默认模式)——使用 $MEMORY_DIR_BASH。

def resolve_memory_dir(proposal, default_dir):
    proj = proposal.get('project')
    if proj:
        return os.path.expanduser(f'~/.claude/projects/{proj}/memory')
    return default_dir

**验证跨项目访问:**对于 selected 中每个唯一的 project——在开始 apply 之前检查 os.path.isdir(target_dir)。如果不存在 → 使整个跨项目 item 失败,reason 为 "memory dir not found for project="。

在任何更改之前对 memory 进行完整快照(始终启用,所有模式下——保证单命令回滚):

SNAP="$CWD_BASH/_archive/wake-backup-$REPORT_DATE-$(date +%H%M%S)"
mkdir -p "$SNAP"
cp -r "$MEMORY_DIR_BASH/." "$SNAP/"
echo "Snapshot: $SNAP"
# 全局模式:对于 selected 中每个唯一的 project — cp -r 其 memory 目录到 "$SNAP/<slug>/"

Memory 目录很小(几百 KB)——快照比任何关于更改内容的争论都要便宜。快照路径必须在最终报告和 Wake 日志中显示。

在开始之前一次性地准备好目标目录:

mkdir -p "$MEMORY_DIR_BASH/TRASH"
mkdir -p "$CWD_BASH/_archive/dream-applied-$REPORT_DATE"
mkdir -p "$CWD_BASH/_archive/trash-purged-$REPORT_DATE"   # 用于 purge_trash items
# 按需创建每个项目的 TRASH 目录(并非所有项目都在 selected 中)

对于 selected 中的每个项目,按照 MD 报告中的“Apply order recommendation”顺序(默认为 M → N → I → S → O):

action: update

  • 读取 target 文件
  • diff_preview — 用自然语言描述更改(例如“将 '## Session 04-01' 部分附加到现有内容”)。这不是结构化的 diff。
  • **尽力而为的适用性检查:**如果 diff_preview 提到了特定的 substring('## Session 04-01'、'feedback_X.md' 等)——执行 substring 检查 if substring in current_content。如果提到的 substring 明确应存在于文件中但不存在 → 使失败,reason 为 "target content drifted: '' not found"
  • 如果确信更改适用——进行编辑。如果不清楚如何更改(模糊的 diff_preview)——使失败,reason 为 "diff_preview too vague to apply automatically",添加到 failed[]。

action: merge

  • 读取所有 files,读取 target(如果已存在)
  • 构建合并后的内容:现有 target + 来自源文件的 sections
  • 写入 target
  • 对于每个 source:mv "$MEMORY_DIR_BASH/<source>" "$MEMORY_DIR_BASH/TRASH/"

action: delete

  • 在全自动模式(auto_level: "full")下——首先进行预删除备份:cp target 到 TRASH/<target>.pre-delete-<date>.md(见阶段 3)
  • 编辑 target 以清除内容(保留带有注释 <!-- deleted YYYY-MM-DD by wake --> 的前置元数据)

action: soft_delete

  • 对于 files 中的每个文件:mv "$MEMORY_DIR_BASH/<file>" "$MEMORY_DIR_BASH/TRASH/"

action: create_new

  • 使用来自提议的前置元数据(type) + content_template 写入 target

action: extract (notes)

  • 从 cwd 读取 source_note
  • 读取 target_memory_file(如果已存在)或准备具有前置元数据的文件
  • 编辑/写入 target,添加 what_to_extract 部分
  • 源操作:
    • keep — 不对 source 做任何操作
    • delete — mv "$CWD_BASH/<source_note>" "$CWD_BASH/_archive/dream-applied-$REPORT_DATE/"
    • move-to-archive — 同上,mv

action: remove_links

  • 读取 MEMORY.md
  • 对于 links_to_remove 中的每个 substring:
    • 查找出现 substring 的行
    • 删除整行(不仅仅是 substring)
  • 使用更新后的内容编辑 MEMORY.md
  • 如果在任何行中都未找到 substring → 记录 warning "link not found in MEMORY.md: ",继续(不要使整个 item 失败)

action: shorten_lines

  • 读取 MEMORY.md
  • 对于 lines_to_shorten 中的每个对象 {original, replacement}:
    • Substring 检查:original 必须在 content 中
    • 编辑:将 original 替换为 replacement
  • 如果未找到 original → 发出警告,跳过此 item,继续

action: add_links

  • 读取 MEMORY.md
  • 对于 links_to_add 中的每个 {section, line}:
    • 查找包含 section(精确的 markdown 标题)的行
    • 在标题行之后直接插入 line
  • 编辑 MEMORY.md
  • 如果未找到 section → 发出警告,跳过,继续

action: promote_skill (satori)

  • 源:~/.claude/satori/staging/<name>/SKILL.md。不存在 → 失败。
  • 目标:提议中的 dest_dir;否则从 DB 查询的 pinned_project(python -c 只读:从 ~/.claude/satori/state.db 中的 SELECT pinned_project FROM skill_usage WHERE name=?)→ <pinned>/.claude/skills/<name>/;否则 ~/.claude/skills/<name>/。
  • 目标已存在 → 备份到 ~/.claude/satori/backups/(cp),然后将 source cp 到 dest。技能在重新启动 Claude Code 后激活。

action: retire_skill (satori)

  • mv ~/.claude/satori/staging/<name> ~/.claude/satori/archive/<name>-<date>(mkdir -p archive)。源不存在 → 发出警告,跳过,继续。

action: purge_trash(回收站第二级)

  • 对于 files 中的每个文件:
    • 源:$MEMORY_DIR_BASH/TRASH/<file>
    • 目标:$CWD_BASH/_archive/trash-purged-$REPORT_DATE/<file>
    • 如果源不存在(用户已经手动删除)→ 发出警告,跳过,继续
    • 如果目标中已存在同名文件 → 将目标重命名为 <file>.<unix_ts>(避免覆盖)
    • mv "$MEMORY_DIR_BASH/TRASH/<file>" "$CWD_BASH/_archive/trash-purged-$REPORT_DATE/<file>"
  • 永远不要使用 rm——即使在这里。文件被移动到 _archive/,用户自行决定何时最终删除。

每个项目之后——在聊天中简短记录:✓ M1 applied (3 files merged → project_pikabu_mcp.md)。

出错时(读取失败、编辑冲突、文件缺失)——不要停止,添加到 failed[] 并附上 reason,然后继续。

阶段 5 — 验证并报告

echo "=== Memory dir state ==="
wc -l "$MEMORY_DIR_BASH/MEMORY.md"
ls "$MEMORY_DIR_BASH"/*.md | wc -l
ls "$MEMORY_DIR_BASH/TRASH/" 2>/dev/null | wc -l

# 检查 MEMORY.md 中的断开链接(Python — 仅使用 _WIN 路径;bash-mount 的 /c/... Windows 上的 Python 无法理解)
python -c "
import re, os
content = open(r'$MEMORY_DIR_WIN/MEMORY.md', encoding='utf-8').read()
links = re.findall(r'\(([^)]+\.md)\)', content)
for link in links:
    full = os.path.join(r'$MEMORY_DIR_WIN', link)
    if not os.path.isfile(full):
        print(f'BROKEN: {link}')
"

如果存在 BROKEN——添加最终自动修复:编辑 MEMORY.md,删除断开的链接,报告更改。

...

[已截断以进行翻译]

相关技能