dream
夜間メモリ統合 — 古いエントリを削除し、重複を統合し、矛盾を解決し、MEMORY.mdインデックスを再構築します。複数のセッションにわたってメモリファイルが蓄積され、クリーンアップが必要な場合に使用してください。新しい判断の保存(rememberを使用)やメモリの検索(memoryを使用)には使用しないでください。
description の原文を見る
Nightly memory consolidation — prunes stale entries, merges duplicates, resolves contradictions, rebuilds MEMORY.md index. Use when memory files have accumulated over many sessions and need cleanup. Do NOT use for storing new decisions (use remember) or searching memory (use memory).
SKILL.md 本文
Dream - メモリ統合
決定的なメモリメンテナンス:古い項目を検出し、重複をマージし、矛盾を解決し、MEMORY.md インデックスを再構築します。すべてのプルーニング判定は、LLMの判断ではなく、検証可能なチェック(ファイルが存在するか?関数が存在するか?重複したコンテンツがあるか?)に基づいています。
引数の解決
DRY_RUN = "--dry-run" in "$ARGUMENTS" # Preview changes without writing
概要
メモリファイルはセッション間で蓄積されます。時間が経つと、次のような問題が発生します:
- 古い参照 — もう存在しないファイル、関数、またはクラスを指すメモリ
- 重複 — 同じトピックをカバーする複数のメモリがあり、内容が重複している
- 矛盾 — 新しいメモリが古いメモリを上書きしているが、古いメモリの削除がない
- インデックスずれ — MEMORY.md インデックスが実際のメモリファイルと同期していない
このスキルは、決定的なチェックのみを使用してこれら4つの問題すべてを修正します。
ステップ 1:メモリファイルの検出
# メモリディレクトリを検出(エージェント固有またはプロジェクトレベル)
# エージェントメモリは以下に存在:.claude/agent-memory/<agent-id>/
# プロジェクトメモリは以下に存在:.claude/projects/<hash>/memory/
# また確認:.claude/memory/
memory_dirs = []
Glob(pattern=".claude/agent-memory/*/MEMORY.md")
Glob(pattern=".claude/projects/*/memory/MEMORY.md")
Glob(pattern=".claude/memory/MEMORY.md")
# 検出された各 MEMORY.md について、そのディレクトリ内のすべての *.md ファイルをグロブ
for dir in memory_dirs:
Glob(pattern=f"{dir}/../*.md") # MEMORY.md と同じディレクトリ内のすべてのメモリファイル
検出されたすべてのメモリファイルを読み込みます。フロントマター(name、description、type)と本文を解析します。メモリ内のインベントリを構築します:
inventory = [{
"path": "/abs/path/to/file.md",
"name": frontmatter.name,
"type": frontmatter.type, # user, feedback, project, reference
"description": frontmatter.description,
"body": body_text,
"file_refs": [], # 抽出されたファイルパス
"symbol_refs": [], # 抽出された関数/クラス名
"topics": [], # 重複検出のためのキーフレーズ
}]
ステップ 2:古さの検出
各メモリファイルについて、参照を抽出し、それらがまだ存在することを確認します。
2a:ファイルパスの参照
ファイル参照のように見えるパスを抽出(パターン:/ を含むパスと拡張子、バッククォートで囲まれたパス):
# 本文テキストからの正規表現のような抽出:
# - 一般的な拡張子を含む / を含むパス:.py、.ts、.tsx、.js、.json、.md、.yaml、.yml、.sh
# - バッククォートで囲まれたパス:`src/something/file.ts`
# - フロントマターの説明の引用パス
for ref in file_refs:
Glob(pattern=ref) # ファイルが存在するかをチェック
# マッチがない → STALE_FILE_REF としてマーク
2b:シンボルの参照
関数/クラス名を抽出(パターン:function_name()、ClassName、def function_name):
for symbol in symbol_refs:
Grep(pattern=symbol, path=".", output_mode="files_with_matches", head_limit=1)
# マッチがない → STALE_SYMBOL_REF としてマーク
2c:古さの分類
| 検出結果 | 分類 | アクション |
|---|---|---|
| すべてのファイル参照が有効、すべてのシンボルが見つかった | FRESH | 保持 |
| 一部のファイル参照が見つからない | PARTIALLY_STALE | レビュー用にフラグを立てる |
| すべてのファイル参照が見つからない かつ すべてのシンボルが見つからない | FULLY_STALE | プルーニング候補 |
| 外部参照がない(純粋な決定/環境設定) | EVERGREEN | 保持 |
FULLY_STALE に分類されたメモリのみが自動的にプルーニングされます。PARTIALLY_STALE メモリはレポートされますが保持されます — ユーザーが判断します。
ステップ 3:重複の検出
同じディレクトリ内のメモリを成対で比較します。2つのメモリが重複している場合:
- 同じタイプ(両方が
feedback、両方がprojectなど) - トピックの重複 — 有意な単語(ストップワードを除外)の60%以上が両方の本文に出現
- 同じ主題 —
nameまたはdescriptionフィールドが同じコンセプトを参照
stopwords = {"the", "a", "an", "is", "are", "was", "were", "be", "been",
"have", "has", "had", "do", "does", "did", "will", "would",
"could", "should", "may", "might", "can", "shall", "to", "of",
"in", "for", "on", "with", "at", "by", "from", "as", "into",
"through", "during", "before", "after", "this", "that", "it",
"not", "no", "but", "or", "and", "if", "then", "than", "so"}
def significant_words(text):
words = set(text.lower().split()) - stopwords
return {w for w in words if len(w) > 2}
def overlap_ratio(words_a, words_b):
if not words_a or not words_b:
return 0.0
intersection = words_a & words_b
smaller = min(len(words_a), len(words_b))
return len(intersection) / smaller if smaller > 0 else 0.0
# 同じタイプの各ペアについて:
# overlap_ratio >= 0.6 の場合 → DUPLICATE ペア
# 新しいファイルを保持(ファイルシステムのmtimeで判定)、古いファイルをプルーニング
ステップ 4:矛盾の解決
矛盾は、同じタイプの2つのメモリが同じ主題について相反する主張をする場合に発生します。検出:
- 同じタイプ + 同じトピック(重複 >= 0.4 だが < 0.6 — 関連しているが完全には重複していない)
- 否定シグナル — 一方の本文に他方の主張の否定が含まれている:
- 「X を実行する」対「X を実行しない」/「X をしない」/「決して X をしない」
- 「X を使用する」対「X を避ける」/「X の使用を中止する」
- 「X を優先する」対「Y を優先する」(同じ決定ドメインの場合)
negation_pairs = [
("do ", "do not "), ("do ", "don't "),
("use ", "avoid "), ("use ", "stop using "),
("prefer ", "don't prefer "), ("always ", "never "),
]
# 矛盾としてフラグが立てられた各ペアについて:
# 新しいファイルを保持(より最近の決定が古い決定に優先)
# 古いファイルをプルーニング
ステップ 5:変更の実行(またはドライラン)
ドライランモード(--dry-run)
--dry-run フラグが存在する場合、すべての書き込みをスキップします。完全なレポート(ステップ 6)を [DRY RUN] プレフィックス付きで出力し、変更される内容をリストします:
[DRY RUN] Would delete: .claude/agent-memory/foo/stale_old_path.md (FULLY_STALE)
[DRY RUN] Would delete: .claude/agent-memory/foo/duplicate_auth.md (DUPLICATE of auth_patterns.md)
[DRY RUN] Would delete: .claude/agent-memory/foo/old_preference.md (CONTRADICTED by new_preference.md)
[DRY RUN] Would rebuild: .claude/agent-memory/foo/MEMORY.md (3 entries removed, 12 remaining)
ライブモード
# 1. FULLY_STALE ファイルを削除
for stale in fully_stale_files:
Bash(command=f"rm '{stale['path']}'")
# 2. 重複ファイルを削除(新しいファイルは保持)
for dup in duplicate_pairs:
older = dup["older"]
Bash(command=f"rm '{older['path']}'")
# 3. 矛盾のあるファイルを削除(新しいファイルは保持)
for contradiction in contradiction_pairs:
older = contradiction["older"]
Bash(command=f"rm '{older['path']}'")
# 4. 生き残ったファイルから MEMORY.md インデックスを再構築
MEMORY.md の再構築
生き残ったすべての .md ファイル(MEMORY.md 自体を除く)を読み込みます。インデックスを生成します:
# <ディレクトリ名> Memory
- [Name](filename.md) -- フロントマターからの1行の説明
再構築されたインデックスのルール:
- メモリファイルごと1行、150文字未満
- ファイル名でアルファベット順にソート
- 合計インデックスは200行以下のままにする
- 再構築後に200行を超える場合、ユーザーに警告(コンテンツメモリを自動切り詰めしない)
# 再構築された MEMORY.md を書き込む
Write(path="<memory_dir>/MEMORY.md", content=rebuilt_index)
ステップ 6:レポート
統合後に概要テーブルを出力します:
## Dream 統合レポート
| メトリクス | 件数 |
|-----------|------|
| スキャンされたメモリディレクトリ | N |
| スキャンされた合計メモリファイル | N |
| プルーニングされた古い項目 | N |
| マージされた重複 | N |
| 解決された矛盾 | N |
| 部分的に古い(保持、フラグを立てた) | N |
| エバーグリーン(外部参照なし) | N |
| 生き残ったメモリ | N |
| 再構築された MEMORY.md インデックス | N |
### 実行された変更
| ファイル | アクション | 理由 |
|---------|-----------|------|
| `path/to/file.md` | 削除 | 完全に古い:すべての参照ファイルが削除済み |
| `path/to/old.md` | 削除 | `path/to/new.md` の重複 |
| `path/to/outdated.md` | 削除 | `path/to/current.md` で矛盾が解決 |
### レビュー用にフラグを立てた(PARTIALLY_STALE)
| ファイル | 見つからない参照 |
|---------|----------------|
| `path/to/file.md` | `src/old/path.ts` はもう存在しません |
--dry-run の場合、レポート全体に次のプレフィックスを付けます:
[DRY RUN] ファイルは変更されていません。--dry-run なしで実行して変更を適用します。
エラーハンドリング
| 条件 | 応答 |
|---|---|
| メモリディレクトリが見つからない | 「メモリディレクトリが見つかりません」と報告して終了 |
| ディレクトリにメモリファイルがない | 「ディレクトリが空です、統合するものがありません」と報告 |
| すべてのメモリが FRESH | 「すべての N メモリは現在です、プルーニングするものがありません」と報告 |
| 再構築後 MEMORY.md が200行を超える | ユーザーに警告、自動切り詰めしない |
| ファイル削除に失敗 | エラーを報告、残りのファイルで続行 |
| メモリファイルにフロントマターがない | EVERGREEN として扱う(メタデータなしで参照を検証できない) |
ステップ 7:プラグインハウスキーピング(CC 2.1.121+、#1544)
メモリ統合後、孤立した自動インストールプラグイン依存関係をチェックし、プルーニングを提案します:
# 孤立を検出
claude plugin list --json | jq '[.[] | select(.auto_installed == true and .reason_kept == "orphaned")] | length'
# > 0 かつ最後のプルーニング > 7 日前(.claude/state/last-prune.txt で追跡)の場合:
claude plugin prune # インタラクティブ — 削除前に確認
CC < 2.1.121 ではこのステップをスキップします。ステートファイル .claude/state/last-prune.txt は最後のプルーニング成功日を記録するため、dream を呼び出すたびに実行しません。
ステップ 8:古いプロジェクト状態ヒント(CC 2.1.126+、#1582、#1587 で修正)
プラグインハウスキーピング後、古いプロジェクト状態が存在する場合、非ブロッキング提案を表示します。パージを実行しないでください — プレビューのみです。
# CC < 2.1.126 ではスキップ(`claude project purge` が利用不可)
# 権威あるソース経由で古いプロジェクトを検出:`claude project purge --dry-run --all`
# は ~/.claude.json から直接来た `config: projects["<canonical-path>"]` 行を出力します。
# これらの解析はロスレス;~/.claude/projects/ の下のディレクトリ名エンコーディングは違う
# (`/` と `.` 両方が `-` に変換されるため、確定的に逆転できません)。
stale_count=$(claude project purge --dry-run --all 2>/dev/null \
| grep -oE 'projects\["[^"]+"\]' \
| sed -E 's/^projects\["//; s/"\]$//' \
| while IFS= read -r p; do
[ -n "$p" ] && [ ! -d "$p" ] && echo "$p"
done | wc -l)
# > 0 の場合、dream サマリーにヒントを表示(自動実行しない)
if [ "$stale_count" -gt 0 ]; then
echo "ℹ $stale_count 個の古いプロジェクト状態エントリが検出されました。"
echo " クリーンアップをプレビュー:claude project purge --dry-run --all"
fi
厳密なルール:常に --dry-run、--yes は使用しません。プロジェクトを(削除ではなく)移動したユーザーはディレクトリを保持する必要があります;パージは不可逆です。提案を表示し、ユーザーが決定できるようにします。
~/.claude.projects/ ではなく claude project purge --dry-run --all を解析する理由:~/.claude.projects/ の下のディレクトリ命名は元のパスの非可逆な変換です(/ と . 両方が - になります)。単純な sed 's|-|/|g' デコードは - を含むパス(例:my-project → /my/project)を誤認識します。CLI のドライラン出力は ~/.claude.json から正規パスを読み取り、唯一の信頼できるソースです。
使用しないケース
- 新しい決定を保存するため --
/ork:rememberを使用 - 過去の決定を検索するため --
/ork:memory searchを使用 - セッション開始時にコンテキストをロードするため --
/ork:memory loadを使用 - 5セッション未満の後 -- メモリファイルが古さを十分に蓄積していない可能性があります
関連スキル
ork:remember-- 決定とパターンを保存(書き込み側)ork:memory-- 検索、ロード、同期、可視化(読み込み側)
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- yonatangross
- ライセンス
- MIT
- 最終更新
- 2026/5/12
Source: https://github.com/yonatangross/orchestkit / ライセンス: MIT