From f8d084eae477365007c474b4e88371fe246d26c7 Mon Sep 17 00:00:00 2001 From: Masahiro Chaen Date: Wed, 8 Apr 2026 00:39:33 +0900 Subject: [PATCH] =?UTF-8?q?Initial=20release:=20Claude=20Code=20=C3=97=20O?= =?UTF-8?q?bsidian=20Wiki=20framework?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 332 ++++++++++++++++++ vault-template/.gitignore | 7 + vault-template/CLAUDE.md | 32 ++ vault-template/DREAMS.md | 22 ++ vault-template/clients/.gitkeep | 0 vault-template/daily/.gitkeep | 0 vault-template/decisions/.gitkeep | 0 vault-template/insights/.gitkeep | 0 vault-template/meetings/.gitkeep | 0 vault-template/memory/feedback/.gitkeep | 0 vault-template/memory/project/.gitkeep | 0 vault-template/memory/reference/.gitkeep | 0 vault-template/memory/user/.gitkeep | 0 vault-template/scripts/git-pull-sync.sh | 40 +++ vault-template/scripts/on-file-change.sh | 48 +++ vault-template/scripts/on-session-end.sh | 63 ++++ .../scripts/sync-openclaw-to-vault.sh | 167 +++++++++ vault-template/scripts/weekly-sync.sh | 72 ++++ vault-template/skills/.gitkeep | 0 vault-template/system/.gitkeep | 0 vault-template/templates/daily-note.md | 17 + vault-template/templates/decision.md | 17 + vault-template/templates/meeting.md | 16 + 23 files changed, 833 insertions(+) create mode 100644 README.md create mode 100644 vault-template/.gitignore create mode 100644 vault-template/CLAUDE.md create mode 100644 vault-template/DREAMS.md create mode 100644 vault-template/clients/.gitkeep create mode 100644 vault-template/daily/.gitkeep create mode 100644 vault-template/decisions/.gitkeep create mode 100644 vault-template/insights/.gitkeep create mode 100644 vault-template/meetings/.gitkeep create mode 100644 vault-template/memory/feedback/.gitkeep create mode 100644 vault-template/memory/project/.gitkeep create mode 100644 vault-template/memory/reference/.gitkeep create mode 100644 vault-template/memory/user/.gitkeep create mode 100755 vault-template/scripts/git-pull-sync.sh create mode 100755 vault-template/scripts/on-file-change.sh create mode 100755 vault-template/scripts/on-session-end.sh create mode 100755 vault-template/scripts/sync-openclaw-to-vault.sh create mode 100755 vault-template/scripts/weekly-sync.sh create mode 100644 vault-template/skills/.gitkeep create mode 100644 vault-template/system/.gitkeep create mode 100644 vault-template/templates/daily-note.md create mode 100644 vault-template/templates/decision.md create mode 100644 vault-template/templates/meeting.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..45dcfbb --- /dev/null +++ b/README.md @@ -0,0 +1,332 @@ +# Claude Code × Obsidian Wiki — Your AI Remembers, Reflects, and Evolves + +> AIが勝手に記憶し、整理し、毎朝振り返ってくれるナレッジシステム +> +> Inspired by [Karpathy's LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) + OpenClaw's Dreaming pattern + +Claude Codeの中に眠っている記憶(Memory)、設定ファイル(CLAUDE.md)、スキル(Skills)を +Obsidianで可視化し、毎朝・毎夕のDreamingで自動振り返りを行う仕組み。 + +PC閉じてても動く。iPhoneからも見える。人間はObsidianを開いて読むだけ。 + +## コンセプト + +``` +┌─ Claude Code の世界(普段は .claude/ に隠れている)───────────┐ +│ │ +│ CLAUDE.md ×N ←──┐ │ +│ CC Memory ×N ←──┤── 自動同期 ──→ ~/vault/ │ +│ Skills ×N ←──┤ (Obsidian) │ +│ Cron jobs ←──┘ 人間が見る窓 │ +│ │ +│ + 日記(daily/)は Obsidian にだけ存在 │ +│ + Dreaming(朝夕の自動振り返り)で知見が複利で増える │ +│ │ +└──────────────────────────────────────────────────────────────┘ +``` + +### 3つの性格を持つハイブリッド設計 + +``` +vault/ +├── system/, skills/, memory/ +│ → 静的ミラー(ダッシュボード) +│ → Claude Codeの中身を自動同期、人間は読むだけ +│ → ヘッダー付き +│ +├── daily/ +│ → 自動ログ + 手書き日記 +│ → Calendar + Slack + Gmail + AI Analysis +│ → 朝夕2回のDreaming(パターン検出・振り返り) +│ +└── meetings/, clients/, insights/ + → Karpathyパターン(知識が複利で増える) + → 議事録を処理するたびに顧客ページに自動蓄積 + → 12回の議事録を読み返す必要がない +``` + +## アーキテクチャ + +``` +┌─ Layer 1: Cloud Scheduled Tasks(PC不要)──────────────────┐ +│ │ +│ 毎朝 07:00 vault-daily-morning │ +│ ├── Google Calendar → 今日の予定 │ +│ ├── Slack → 昨夜〜今朝のハイライト │ +│ ├── Gmail → 未読・重要メール │ +│ ├── Morning Dreaming(昨日の振り返り→今日の注目) │ +│ └── GitHub push │ +│ │ +│ 毎夕 18:30 vault-daily-evening │ +│ ├── Evening Dreaming(今日+7日分→パターン検出) │ +│ ├── 日曜は週次Dreaming + Lint + Slack通知 │ +│ └── GitHub push │ +│ │ +└──────────────────────┬──────────────────────────────────────┘ + │ push + ▼ +┌─ GitHub (private repo) ─────────────────────────────────────┐ +│ vault/ の全ファイル │ +└──────────────────────┬──────────────────────────────────────┘ + │ pull (launchd 毎時) + ▼ +┌─ Layer 2: ローカル自動化 ───────────────────────────────────┐ +│ │ +│ Claude Code Hooks (async: true) │ +│ ├── PostToolUse → ファイル変更をログ記録 │ +│ └── Stop → セッション終了をdaily noteに自動追記 │ +│ │ +│ OpenClaw Cron(PCオン時の追加データ) │ +│ ├── SF/Stripe/HERP/YouTube等の専門データ追記 │ +│ └── PCオフなら単にスキップ(Layer 1だけで完成) │ +│ │ +└──────────────────────┬──────────────────────────────────────┘ + │ iCloud + ▼ + Obsidian (Mac + iPhone) +``` + +## AIの記憶システム(Memory) + +Claude Codeは `.claude/projects/*/memory/` に記憶を保存する。 +この記憶がObsidianに自動ミラーされ、人間が読める形になる。 + +``` +memory/ +├── feedback/ (21件) ← AIへの行動指針 +│ ├── never-send-email.md 「メール送信は絶対禁止。下書きのみ」 +│ ├── gas-version-control.md 「GAS編集後は毎回git commit」 +│ └── minutes-include-sf.md 「議事録にはSF+Slack報告も含める」 +│ +├── reference/ (7件) ← 外部システムへのポインタ +│ ├── wordpress-api.md 「APIの認証情報はここ」 +│ └── typefully-api.md 「X投稿はTypefully経由」 +│ +├── project/ (4件) ← プロジェクト状況 +│ └── personal-budget.md 「月間支出目標¥3,000,000」 +│ +└── user/ (1件) ← ユーザープロファイル + └── user-profile.md 「シェル環境にまだ詳しくない」 +``` + +AIが過去の失敗や指示を覚えていて、次から同じミスをしない。 +その記憶が全部Obsidianで見える。「何を覚えてるの?」が一目瞭然。 + +## Dreaming(朝夕の自動振り返り) + +OpenClawのSOUL/MEMORY/DREAMSパターンを参考に設計。 + +``` +毎朝 07:00 — Morning Dreaming +├── 昨日のdaily noteを読み返す +├── 決定事項・未解決タスクを抽出 +└── 「今日の注目ポイント」を3行で生成 + +毎夕 18:30 — Evening Dreaming +├── 今日のSlack/Gmail/Calendarを振り返り +├── 直近7日とのパターン比較 +│ 例: 「火曜は会議密度が高い(3週連続)」 +│ 例: 「メール返信が午後に集中」 +├── 未解決の問いを抽出 +└── パターンが見つかったら insights/ にページ作成 + +毎週日曜 — Weekly Dreaming +├── 1週間分のdailyから洞察を抽出 +├── DREAMS.md に成長軌跡を記録 +├── Lint(壊れたリンク・orphanページ検出) +└── Slackでサマリー通知 +``` + +DREAMS.mdに蓄積される内容: +- Current Insights(最新の内省結果) +- Emerging Patterns(浮かび上がるパターン) +- Growth Trajectory(成長の軌跡) +- Open Questions(未解決の問い) + +## Vault構造 + +``` +~/vault/ +├── CLAUDE.md ← Schema(LLM向けルール定義) +├── DREAMS.md ← Dreaming蓄積ファイル +│ +├── daily/ ← デイリーノート +│ └── 2026-04-07.md   Schedule / Gmail / Slack / AI Analysis / +│ Morning Reflection / Evening Reflection / +│ Claude Code Session / Thoughts +│ +├── system/ ← Claude Codeシステムのミラー(SYNCED) +│ ├── claude-md-tree.md   全CLAUDE.mdの階層ツリー +│ ├── global-rules.md   ルール・禁止事項の要約 +│ ├── api-inventory.md   保有API一覧(キーは除外) +│ ├── tech-stack.md   技術スタック +│ └── cron-jobs.md   稼働中ジョブ一覧 +│ +├── skills/ ← 全スキル一覧 + 個別ページ(SYNCED) +│ ├── _index.md   カテゴリ別テーブル +│ └── auto-minutes.md   各スキルの説明・コマンド +│ +├── memory/ ← CC Memory完全ミラー(SYNCED) +│ ├── _index.md   全メモリ一覧(タイプ別) +│ ├── feedback/   行動指針 +│ ├── reference/   外部参照 +│ ├── project/   プロジェクト状況 +│ └── user/   ユーザープロファイル +│ +├── clients/ ← 顧客ナレッジ蓄積(Karpathyパターン) +│ ├── _index.md   全顧客一覧 +│ └── naoru.md   議事録のたびに自動蓄積 +│ +├── meetings/ ← 議事録要点(/auto-minutes連携) +├── decisions/ ← 経営判断ログ +├── insights/ ← 学び・パターン + 週次Dreaming +├── templates/ ← daily-note, meeting, decision +└── scripts/ ← hookスクリプト + 同期スクリプト +``` + +## セットアップ手順 + +### 前提条件 +- Claude Code(Pro or Max) +- Obsidian(無料) +- GitHub アカウント +- (オプション)Slack / Google Calendar / Gmail の Connector + +### Step 1: Vault作成 + +```bash +# フォルダ作成 +mkdir -p ~/vault/{daily,system,skills,memory/{feedback,reference,project,user},clients,meetings,decisions,insights,templates,scripts} + +# iCloud同期(iPhone対応する場合) +mv ~/vault ~/Library/Mobile\ Documents/iCloud~md~obsidian/Documents/claude-code +ln -s ~/Library/Mobile\ Documents/iCloud~md~obsidian/Documents/claude-code ~/vault +``` + +### Step 2: テンプレートファイルをコピー + +このリポジトリの `vault-template/` をコピー: + +```bash +cp -r vault-template/* ~/vault/ +``` + +### Step 3: Claude Code Hooks 設定 + +`~/.claude/settings.json` に追加: + +```json +{ + "hooks": { + "PostToolUse": [{ + "matcher": "Write|Edit", + "hooks": [{ + "type": "command", + "command": "bash ~/vault/scripts/on-file-change.sh", + "async": true + }] + }], + "Stop": [{ + "hooks": [{ + "type": "command", + "command": "bash ~/vault/scripts/on-session-end.sh", + "async": true + }] + }] + } +} +``` + +### Step 4: 初回同期 + +Claude Codeで実行: + +``` +/wiki-sync-init +``` + +または手動: + +```bash +# Skills同期 +for d in ~/.claude/skills/*/; do + name=$(basename "$d") + # SKILL.md を読んで vault/skills/ にページ作成 +done + +# Memory同期 +find ~/.claude/projects -name "*.md" -path "*/memory/*" ! -name "MEMORY.md" | while read src; do + # vault/memory/ にミラー作成 +done +``` + +### Step 5: GitHub + 自動pull + +```bash +cd ~/vault +git init && git add -A && git commit -m "Initial vault" +gh repo create my-vault --private --source=. --push +``` + +### Step 6: Cloud Scheduled Tasks(オプション、PC不要にする場合) + +claude.ai/code/scheduled で: +- **vault-daily-morning**: 毎朝07:00、Calendar+Slack+Gmail→daily note生成 +- **vault-daily-evening**: 毎夕18:30、Evening Dreaming+パターン検出 + +## デイリーノートの完成形 + +```markdown +--- +date: 2026-04-07 +weekday: Monday +type: daily +score: 74 +--- + +## Schedule +| 時間 | 予定 | 備考 | +|------|------|------| +| 09:00 | 経営管理部 定例 | | +| 10:00 | 開発営業 定例 | | +| 14:00 | デジライズ定例 | | + +## Gmail +| From | Subject | Action | +|------|---------|--------| +| freee 上野 | セミナー開催打合せ | 要返信 | + +## Slack Highlights +- **#経営**: 人事部長アサイン議論 +- **#開発営業**: チャットbot進捗、SF議事録デモ依頼 +- **#日報_柴田**: エイジス様研修225万円受注ほぼ確定 + +## AI Analysis +- 生産性: B — 会議完了率100% +- 対応力: B — 未読メール6件 +- 営業: B- — 商談0件 + +## Morning Reflection +- 昨日の決定: スクール料金改定を決定 +- 今日の注目: freeeセミナー返信、金成さんMTG + +## Evening Reflection +- 今日のハイライト: エイジス225万円ほぼ確定 +- パターン: 月曜は会議が10件超で最多(3週連続) +- 未解決: freeeセミナー返信 + +## Thoughts + +``` + +## 参考 + +- [Karpathy's LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) — 設計思想の原点 +- [obsidian-wiki (Ar9av)](https://github.com/Ar9av/obsidian-wiki) — Karpathyパターンのフレームワーク +- [QMD](https://github.com/tobi/qmd) — Markdownセマンティック検索(100ページ超で導入検討) +- [Claude Code Hooks](https://code.claude.com/docs/en/hooks-guide) — async hookの公式ドキュメント +- [Cloud Scheduled Tasks](https://code.claude.com/docs/en/web-scheduled-tasks) — PC不要の自動化 + +## License + +MIT diff --git a/vault-template/.gitignore b/vault-template/.gitignore new file mode 100644 index 0000000..1073949 --- /dev/null +++ b/vault-template/.gitignore @@ -0,0 +1,7 @@ +.obsidian/workspace.json +.obsidian/workspace-mobile.json +.obsidian/graph.json +.sync.log +.DS_Store +.trash/ +.git-sync.lock diff --git a/vault-template/CLAUDE.md b/vault-template/CLAUDE.md new file mode 100644 index 0000000..4c3d83e --- /dev/null +++ b/vault-template/CLAUDE.md @@ -0,0 +1,32 @@ +# Obsidian Vault Schema + +> このファイルはLLMがvaultを読み書きする際のルール定義。 + +## ファイル区分 + +| 区分 | 場所 | 編集権限 | ヘッダー | +|------|------|---------|---------| +| SYNCED | system/, skills/, memory/ | 自動同期のみ。手動編集禁止 | `` | +| editable | daily/, meetings/, clients/, decisions/, insights/ | Claude + 人間が自由に編集 | なし | + +## 書き込みルール + +1. **frontmatter必須** — 全ページに YAML frontmatter を付ける +2. **日本語** — 本文は日本語。タグ・フォルダ名は英語kebab-case +3. **日付フォーマット** — YYYY-MM-DD(ISO 8601) +4. **SYNCEDファイルの冒頭** — `` を1行目に + +## リンク規約 + +- 内部リンク: `[[フォルダ/ファイル名]]` 形式 +- 外部ソース: frontmatterの `source:` フィールドにパス記載 +- 双方向リンク推奨 + +## 命名規則 + +| コンテンツ | パターン | 例 | +|-----------|---------|-----| +| デイリーノート | `YYYY-MM-DD.md` | `2026-04-07.md` | +| 議事録 | `YYYY-MM-DD_<顧客>_<種別>.md` | `2026-04-07_naoru_定例.md` | +| 判断ログ | `YYYY-MM_<テーマ>.md` | `2026-04_料金改定.md` | +| 顧客 | `.md` | `naoru.md` | diff --git a/vault-template/DREAMS.md b/vault-template/DREAMS.md new file mode 100644 index 0000000..5a12b5d --- /dev/null +++ b/vault-template/DREAMS.md @@ -0,0 +1,22 @@ +--- +type: dreams +updated: +last_dreaming: +--- + +# Dreams + +## Current Insights + + +## Emerging Patterns + +| パターン | 観測回数 | 傾向 | +|---------|---------|------| + + +## Growth Trajectory + + +## Open Questions + diff --git a/vault-template/clients/.gitkeep b/vault-template/clients/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/daily/.gitkeep b/vault-template/daily/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/decisions/.gitkeep b/vault-template/decisions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/insights/.gitkeep b/vault-template/insights/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/meetings/.gitkeep b/vault-template/meetings/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/memory/feedback/.gitkeep b/vault-template/memory/feedback/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/memory/project/.gitkeep b/vault-template/memory/project/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/memory/reference/.gitkeep b/vault-template/memory/reference/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/memory/user/.gitkeep b/vault-template/memory/user/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/scripts/git-pull-sync.sh b/vault-template/scripts/git-pull-sync.sh new file mode 100755 index 0000000..2b3f5c0 --- /dev/null +++ b/vault-template/scripts/git-pull-sync.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# git-pull-sync.sh — GitHub から最新を取得して iCloud に反映 +# launchd で毎時実行。Cloud Taskがpushした変更をローカルに取り込む。 + +set +e + +VAULT_DIR="$HOME/vault" +LOG_FILE="$VAULT_DIR/.sync.log" +LOCKFILE="$VAULT_DIR/.git-sync.lock" + +cd "$VAULT_DIR" || exit 0 + +# ロックファイルで同時実行を防止 +if [ -f "$LOCKFILE" ] && kill -0 "$(cat "$LOCKFILE" 2>/dev/null)" 2>/dev/null; then + echo "[$(date '+%F %T')] git-pull: locked, skipping" >> "$LOG_FILE"; exit 0 +fi +echo $$ > "$LOCKFILE" +trap 'rm -f "$LOCKFILE"' EXIT + +DIRTY=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ') +if [ "$DIRTY" -gt "0" ]; then + git stash 2>/dev/null + if ! git pull --rebase origin main 2>/dev/null; then + git rebase --abort 2>/dev/null + echo "[$(date '+%F %T')] git-pull: rebase failed, aborted" >> "$LOG_FILE" + fi + if ! git stash pop 2>/dev/null; then + echo "[$(date '+%F %T')] git-pull: stash pop FAILED — manual resolution needed" >> "$LOG_FILE" + git checkout -- . 2>/dev/null + git stash drop 2>/dev/null + fi + echo "[$(date '+%F %T')] git-pull: pulled with stash (dirty=$DIRTY)" >> "$LOG_FILE" +else + RESULT=$(git pull --rebase origin main 2>&1) + if ! echo "$RESULT" | grep -q "Already up to date"; then + echo "[$(date '+%F %T')] git-pull: $RESULT" >> "$LOG_FILE" + fi +fi + +exit 0 diff --git a/vault-template/scripts/on-file-change.sh b/vault-template/scripts/on-file-change.sh new file mode 100755 index 0000000..837ec01 --- /dev/null +++ b/vault-template/scripts/on-file-change.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# on-file-change.sh — Claude Code PostToolUse hook (Write|Edit) +# ファイル変更時にvaultの関連ページを更新する +# 呼び出し元: ~/.claude/settings.json → hooks.PostToolUse (async: true) +set +e + +LOG="$HOME/vault/.sync.log" + +# stdinからJSON(tool_input)を読み取る +INPUT=$(cat 2>/dev/null || true) +[ -z "$INPUT" ] && exit 0 + +# 変更されたファイルパスを抽出 +FP=$(echo "$INPUT" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + ti = d.get('tool_input', d) + print(ti.get('file_path', ti.get('path', ''))) +except: + print('') +" 2>/dev/null || echo "") + +[ -z "$FP" ] && exit 0 + +# パスに基づいて同期対象を判定(case文でgrepを避ける) +case "$FP" in + */vault/*) + # vault内のファイル変更は無視(再帰防止) + ;; + */CLAUDE.md) + echo "[$(date '+%F %T')] CLAUDE.md changed: $FP" >> "$LOG" + ;; + */.claude/projects/*/memory/*|*/memory/*) + echo "[$(date '+%F %T')] Memory changed: $FP" >> "$LOG" + ;; + */.claude/skills/*|*/skills/*) + echo "[$(date '+%F %T')] Skill changed: $FP" >> "$LOG" + ;; + */clients/*/minutes/*) + echo "[$(date '+%F %T')] Minutes changed: $FP" >> "$LOG" + ;; + *) + # 同期対象外 + ;; +esac + +exit 0 diff --git a/vault-template/scripts/on-session-end.sh b/vault-template/scripts/on-session-end.sh new file mode 100755 index 0000000..994d710 --- /dev/null +++ b/vault-template/scripts/on-session-end.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# on-session-end.sh — Claude Code Stop hook +# セッション終了時にdaily noteにサマリーを追記する +# 呼び出し元: ~/.claude/settings.json → hooks.Stop (async: true) +set +e + +VAULT_DIR="$HOME/vault" +DAILY_DIR="$VAULT_DIR/daily" +LOG="$VAULT_DIR/.sync.log" +TODAY=$(date +%Y-%m-%d) +DAILY_FILE="$DAILY_DIR/$TODAY.md" +NOW=$(date +%H:%M) + +echo "[$(date '+%Y-%m-%d %H:%M:%S')] on-session-end" >> "$LOG" + +# daily noteが存在しない場合は作成 +if [ ! -f "$DAILY_FILE" ]; then + WEEKDAY=$(TODAY="$TODAY" python3 -c " +import os, datetime +d = datetime.date.fromisoformat(os.environ['TODAY']) +print(d.strftime('%A')) +") + printf -- "---\ndate: %s\nweekday: %s\n---\n\n## Schedule\n\n## Log\n\n## Thoughts\n\n## Links\n" "$TODAY" "$WEEKDAY" > "$DAILY_FILE" + echo "[$(date '+%Y-%m-%d %H:%M:%S')] Created daily note: $DAILY_FILE" >> "$LOG" +fi + +# stdinからJSONを読み取る(hookから渡される) +INPUT=$(cat 2>/dev/null || true) +CWD=$(echo "${INPUT:-{}}" | python3 -c "import sys,json;print(json.load(sys.stdin).get('cwd',''))" 2>/dev/null || echo "") +LABEL=$([ -n "$CWD" ] && basename "$CWD" || echo "unknown") + +ENTRY="- $NOW セッション終了 (cwd: $LABEL)" + +# ## Log セクションに追記(環境変数経由でPythonに渡す — シェル補間を避ける) +export DAILY_FILE ENTRY +python3 -c ' +import os + +f = os.environ["DAILY_FILE"] +e = os.environ["ENTRY"] + +lines = open(f).readlines() +out = [] +in_log = False +done = False + +for l in lines: + if l.strip() == "## Log": + in_log = True + out.append(l) + continue + if in_log and not done and l.startswith("## "): + out.append(e + "\n\n") + done = True + out.append(l) + +if in_log and not done: + out.append(e + "\n\n") + +open(f, "w").writelines(out) +' 2>/dev/null || echo "$ENTRY" >> "$DAILY_FILE" + +exit 0 diff --git a/vault-template/scripts/sync-openclaw-to-vault.sh b/vault-template/scripts/sync-openclaw-to-vault.sh new file mode 100755 index 0000000..3a1b05b --- /dev/null +++ b/vault-template/scripts/sync-openclaw-to-vault.sh @@ -0,0 +1,167 @@ +#!/bin/bash +# sync-openclaw-to-vault.sh — OpenClawのJSON → vault/daily/ に追記 +# Usage: bash sync-openclaw-to-vault.sh [morning|evening] +# OpenClaw cron: 07:30 (morning), 18:30 (evening) +# Cloud TaskがLayer 1でdaily noteを作成済み。このスクリプトはLayer 2でデータ追記。 + +set +e + +MODE="${1:-morning}" +TODAY=$(date +%Y-%m-%d) +WEEKDAY=$(date +%A) +VAULT_DIR="$HOME/vault" +DAILY_FILE="$VAULT_DIR/daily/$TODAY.md" +JSON_FILE="$HOME/clawd/reports/data/daily/$TODAY.json" +LOG_FILE="$VAULT_DIR/.sync.log" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] sync-vault($MODE): $1" >> "$LOG_FILE" +} + +# ロックファイルで同時実行を防止 +LOCKFILE="$VAULT_DIR/.git-sync.lock" +if [ -f "$LOCKFILE" ] && kill -0 "$(cat "$LOCKFILE" 2>/dev/null)" 2>/dev/null; then + log "locked, skipping"; exit 0 +fi +echo $$ > "$LOCKFILE" +trap 'rm -f "$LOCKFILE"' EXIT + +# まずGitHub最新を取得(Cloud Taskがpush済みの可能性) +cd "$VAULT_DIR" && git pull --rebase origin main 2>/dev/null + +if [ ! -f "$JSON_FILE" ]; then + log "JSON not found: $JSON_FILE (OpenClaw may not have generated yet)" + exit 0 +fi + +log "Starting $MODE sync" + +export DAILY_FILE JSON_FILE MODE TODAY WEEKDAY +python3 << 'PYEOF' +import os, json + +mode = os.environ['MODE'] +today = os.environ['TODAY'] +weekday = os.environ['WEEKDAY'] +daily_file = os.environ['DAILY_FILE'] +json_file = os.environ['JSON_FILE'] + +with open(json_file) as f: + d = json.load(f) + +score = d.get('score', 0) +meetings = d.get('meetings', {}) +emails = d.get('emails', {}) +slack = d.get('slack', {}) +deals = d.get('deals', {}) +ai = d.get('aiAnalysis', {}) +tomorrow = d.get('tomorrow', {}) +highlights = d.get('highlights', []) + +# daily noteが存在しない場合(Cloud Taskが未実行)→ 作成 +if not os.path.exists(daily_file): + lines = [ + '---', f'date: {today}', f'weekday: {weekday}', 'type: daily', + f'score: {score}', '---', '', + ] + # 最低限のセクション + for section in ['Schedule', 'Gmail', 'Slack Highlights', 'Salesforce', + 'AI Analysis', 'Morning Reflection', 'Claude Code Session', + 'Evening Update', 'Evening Reflection', 'Tomorrow', + 'Thoughts', 'Links']: + lines.append(f'## {section}') + lines.append('') + with open(daily_file, 'w') as f: + f.write('\n'.join(lines)) + +content = open(daily_file).read() + +# OpenClaw専用データを追記セクションとして構築 +enrich_lines = [] + +# Salesforce +deal_items = deals.get('items', []) +if isinstance(deal_items, list) and deal_items: + sf_lines = [] + for dl in deal_items: + if isinstance(dl, dict): + name = dl.get('name', dl.get('title', '')) + stage = dl.get('stage', dl.get('status', '')) + sf_lines.append(f'- {name}: {stage}') + active = deals.get('active', 0) + sf_lines.append(f'- アクティブ案件: {active}件') + sf_text = '\n'.join(sf_lines) + if '## Salesforce' in content and content.split('## Salesforce')[1].split('##')[0].strip() == '': + content = content.replace('## Salesforce\n', f'## Salesforce\n{sf_text}\n', 1) + +# AI Analysis +if isinstance(ai, dict) and ai: + ai_lines = [] + for key, label in [('productivity','生産性'), ('responsiveness','対応力'), ('salesProgress','営業')]: + info = ai.get(key, {}) + if isinstance(info, dict) and info: + ai_lines.append(f'- {label}: {info.get("grade","")} — {info.get("comment","")}') + recs = ai.get('recommendations', []) + if recs: + ai_lines.append('') + for r in recs: + ai_lines.append(f'- {r}') + ai_text = '\n'.join(ai_lines) + if '## AI Analysis' in content and content.split('## AI Analysis')[1].split('##')[0].strip() == '': + content = content.replace('## AI Analysis\n', f'## AI Analysis\n{ai_text}\n', 1) + +if mode == 'evening': + # Evening Update + completed = meetings.get('completed', 0) + total = meetings.get('total', 0) + sent = emails.get('sent', 0) + pending = emails.get('pending', 0) + ev_lines = [ + f'- 会議: {completed}/{total} 完了', + f'- スコア: {score}', + f'- メール送信: {sent}件 / 未対応: {pending}件', + ] + if highlights: + for h in highlights: + if isinstance(h, dict): + ev_lines.append(f'- {h.get("emoji","")} {h.get("text","")}') + ev_text = '\n'.join(ev_lines) + content = content.replace( + '', + ev_text + ) + + # Tomorrow + t_lines = [] + t_items = tomorrow.get('items', []) + if isinstance(t_items, list): + for t in t_items: + if isinstance(t, dict): + time = t.get('time', '') + title = t.get('title', '') + warn = ' !!!' if t.get('warning') else '' + t_lines.append(f'- {time} {title}{warn}') + t_count = tomorrow.get('meetings', 0) + t_lines.append(f'') + t_lines.append(f'明日の会議: {t_count}件') + advice = ai.get('tomorrowAdvice', '') if isinstance(ai, dict) else '' + if advice: + t_lines.append(f'') + t_lines.append(advice) + t_text = '\n'.join(t_lines) + content = content.replace( + '', + t_text + ) + +with open(daily_file, 'w') as f: + f.write(content) + +print(f'{mode} enrichment done: {daily_file}') +PYEOF + +# git commit & push +cd "$VAULT_DIR" && git add "daily/$TODAY.md" && git commit -m "enrich: $TODAY $MODE (OpenClaw data)" 2>/dev/null && git push 2>/dev/null + +log "Completed $MODE sync" +exit 0 diff --git a/vault-template/scripts/weekly-sync.sh b/vault-template/scripts/weekly-sync.sh new file mode 100755 index 0000000..59d91e9 --- /dev/null +++ b/vault-template/scripts/weekly-sync.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# weekly-sync.sh — 週次Lintスクリプト +# 毎週日曜 03:00 に実行(cron or manual) +# vault内の整合性チェック・壊れたリンク検出・orphanページ検出 +set +e + +VAULT="$HOME/vault" +LOG="$VAULT/.sync.log" +ISSUES="" +IC=0 + +add() { + ISSUES="${ISSUES}"$'\n'"- $1" + IC=$((IC + 1)) +} + +echo "[$(date '+%F %T')] weekly-sync start" >> "$LOG" + +# 1. 壊れたwikilinksを検出(grep -oE はmacOS互換) +echo "--- Checking broken wikilinks ---" +while IFS= read -r -d '' f; do + # grep -oE で [[...]] リンクを抽出(-oP は macOS 非対応なので使わない) + while IFS= read -r link; do + [ -z "$link" ] && continue + [ ! -f "$VAULT/${link}.md" ] && add "Broken: [[${link}]] in $(basename "$f")" + done < <(grep -oE '\[\[[^]|]+' "$f" 2>/dev/null | sed 's/\[\[//') +done < <(find "$VAULT" -name "*.md" -not -path "*/.obsidian/*" -not -path "*/templates/*" -print0) + +# 2. 過去7日のdaily noteが存在するかチェック +echo "--- Checking daily notes ---" +for i in $(seq 1 7); do + d=$(date -v-"${i}d" +%Y-%m-%d 2>/dev/null || continue) + [ ! -f "$VAULT/daily/${d}.md" ] && add "Missing daily: ${d}" +done + +# 3. SYNCEDファイルのヘッダーチェック +echo "--- Checking SYNCED headers ---" +while IFS= read -r -d '' f; do + FIRST_LINE=$(head -1 "$f") + case "$FIRST_LINE" in + "") ;; + *) add "Missing SYNCED header: $(basename "$f")" ;; + esac +done < <(find "$VAULT/system" -name "*.md" -print0 2>/dev/null) + +# 4. frontmatterのないファイルを検出 +echo "--- Checking frontmatter ---" +while IFS= read -r -d '' f; do + FIRST_LINE=$(head -1 "$f") + case "$FIRST_LINE" in + "---"|"") ;; + *) add "Missing frontmatter: $(basename "$f")" ;; + esac +done < <(find "$VAULT" -name "*.md" -not -path "*/templates/*" -not -path "*/.obsidian/*" -print0 2>/dev/null) + +# 5. CLAUDE.mdの総数をカウント +echo "--- Counting CLAUDE.md files ---" +CLAUDE_COUNT=0 +while IFS= read -r -d '' _f; do + CLAUDE_COUNT=$((CLAUDE_COUNT + 1)) +done < <(find "$HOME/work" "$HOME/dev" "$HOME/content" -name "CLAUDE.md" -print0 2>/dev/null) + +echo "[$(date '+%F %T')] weekly-sync done: $IC issues, $CLAUDE_COUNT CLAUDE.md files" >> "$LOG" + +# サマリー出力 +if [ "$IC" -gt 0 ]; then + printf "Lint: %s issues%s\n" "$IC" "$ISSUES" +else + echo "Lint: All clear! ($CLAUDE_COUNT CLAUDE.md files)" +fi + +exit 0 diff --git a/vault-template/skills/.gitkeep b/vault-template/skills/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/system/.gitkeep b/vault-template/system/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vault-template/templates/daily-note.md b/vault-template/templates/daily-note.md new file mode 100644 index 0000000..66f54ae --- /dev/null +++ b/vault-template/templates/daily-note.md @@ -0,0 +1,17 @@ +--- +date: {{date}} +weekday: {{weekday}} +--- + +## Schedule + + +## Log + + + +## Thoughts + + +## Links + diff --git a/vault-template/templates/decision.md b/vault-template/templates/decision.md new file mode 100644 index 0000000..ea9cd4b --- /dev/null +++ b/vault-template/templates/decision.md @@ -0,0 +1,17 @@ +--- +date: {{date}} +category: {{category}} +impact: {{impact}} +--- + +## 判断 +- + +## 背景 +- + +## 代替案と却下理由 +- + +## 結果 + diff --git a/vault-template/templates/meeting.md b/vault-template/templates/meeting.md new file mode 100644 index 0000000..76e403f --- /dev/null +++ b/vault-template/templates/meeting.md @@ -0,0 +1,16 @@ +--- +date: {{date}} +client: {{client}} +source: {{source}} +--- + +## 決定事項 +- + +## 宿題 +- + +## 気づき +- + +## 関連