Initial release: Claude Code × Obsidian Wiki framework
This commit is contained in:
commit
f8d084eae4
23 changed files with 833 additions and 0 deletions
332
README.md
Normal file
332
README.md
Normal file
|
|
@ -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の中身を自動同期、人間は読むだけ
|
||||
│ → <!-- SYNCED: DO NOT EDIT --> ヘッダー付き
|
||||
│
|
||||
├── 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
|
||||
7
vault-template/.gitignore
vendored
Normal file
7
vault-template/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.obsidian/workspace.json
|
||||
.obsidian/workspace-mobile.json
|
||||
.obsidian/graph.json
|
||||
.sync.log
|
||||
.DS_Store
|
||||
.trash/
|
||||
.git-sync.lock
|
||||
32
vault-template/CLAUDE.md
Normal file
32
vault-template/CLAUDE.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Obsidian Vault Schema
|
||||
|
||||
> このファイルはLLMがvaultを読み書きする際のルール定義。
|
||||
|
||||
## ファイル区分
|
||||
|
||||
| 区分 | 場所 | 編集権限 | ヘッダー |
|
||||
|------|------|---------|---------|
|
||||
| SYNCED | system/, skills/, memory/ | 自動同期のみ。手動編集禁止 | `<!-- SYNCED: DO NOT EDIT -->` |
|
||||
| editable | daily/, meetings/, clients/, decisions/, insights/ | Claude + 人間が自由に編集 | なし |
|
||||
|
||||
## 書き込みルール
|
||||
|
||||
1. **frontmatter必須** — 全ページに YAML frontmatter を付ける
|
||||
2. **日本語** — 本文は日本語。タグ・フォルダ名は英語kebab-case
|
||||
3. **日付フォーマット** — YYYY-MM-DD(ISO 8601)
|
||||
4. **SYNCEDファイルの冒頭** — `<!-- SYNCED: DO NOT EDIT -->` を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` |
|
||||
| 顧客 | `<kebab-case>.md` | `naoru.md` |
|
||||
22
vault-template/DREAMS.md
Normal file
22
vault-template/DREAMS.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
type: dreams
|
||||
updated:
|
||||
last_dreaming:
|
||||
---
|
||||
|
||||
# Dreams
|
||||
|
||||
## Current Insights
|
||||
<!-- weekly-dreaming が毎週自動更新 -->
|
||||
|
||||
## Emerging Patterns
|
||||
|
||||
| パターン | 観測回数 | 傾向 |
|
||||
|---------|---------|------|
|
||||
<!-- Dreamingが蓄積 -->
|
||||
|
||||
## Growth Trajectory
|
||||
<!-- 月次サマリーが追記される -->
|
||||
|
||||
## Open Questions
|
||||
<!-- Dreamingが検出した未解決課題 -->
|
||||
0
vault-template/clients/.gitkeep
Normal file
0
vault-template/clients/.gitkeep
Normal file
0
vault-template/daily/.gitkeep
Normal file
0
vault-template/daily/.gitkeep
Normal file
0
vault-template/decisions/.gitkeep
Normal file
0
vault-template/decisions/.gitkeep
Normal file
0
vault-template/insights/.gitkeep
Normal file
0
vault-template/insights/.gitkeep
Normal file
0
vault-template/meetings/.gitkeep
Normal file
0
vault-template/meetings/.gitkeep
Normal file
0
vault-template/memory/feedback/.gitkeep
Normal file
0
vault-template/memory/feedback/.gitkeep
Normal file
0
vault-template/memory/project/.gitkeep
Normal file
0
vault-template/memory/project/.gitkeep
Normal file
0
vault-template/memory/reference/.gitkeep
Normal file
0
vault-template/memory/reference/.gitkeep
Normal file
0
vault-template/memory/user/.gitkeep
Normal file
0
vault-template/memory/user/.gitkeep
Normal file
40
vault-template/scripts/git-pull-sync.sh
Executable file
40
vault-template/scripts/git-pull-sync.sh
Executable file
|
|
@ -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
|
||||
48
vault-template/scripts/on-file-change.sh
Executable file
48
vault-template/scripts/on-file-change.sh
Executable file
|
|
@ -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
|
||||
63
vault-template/scripts/on-session-end.sh
Executable file
63
vault-template/scripts/on-session-end.sh
Executable file
|
|
@ -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
|
||||
167
vault-template/scripts/sync-openclaw-to-vault.sh
Executable file
167
vault-template/scripts/sync-openclaw-to-vault.sh
Executable file
|
|
@ -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(
|
||||
'<!-- sync-openclaw-to-vault.sh evening が自動追記 -->',
|
||||
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(
|
||||
'<!-- sync-openclaw-to-vault.sh evening が自動生成 -->',
|
||||
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
|
||||
72
vault-template/scripts/weekly-sync.sh
Executable file
72
vault-template/scripts/weekly-sync.sh
Executable file
|
|
@ -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
|
||||
"<!-- SYNCED: DO NOT EDIT -->") ;;
|
||||
*) 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
|
||||
"---"|"<!-- SYNCED: DO NOT EDIT -->") ;;
|
||||
*) 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
|
||||
0
vault-template/skills/.gitkeep
Normal file
0
vault-template/skills/.gitkeep
Normal file
0
vault-template/system/.gitkeep
Normal file
0
vault-template/system/.gitkeep
Normal file
17
vault-template/templates/daily-note.md
Normal file
17
vault-template/templates/daily-note.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
date: {{date}}
|
||||
weekday: {{weekday}}
|
||||
---
|
||||
|
||||
## Schedule
|
||||
<!-- morning-briefing が自動記入 -->
|
||||
|
||||
## Log
|
||||
<!-- Claude Code hooks が自動追記 -->
|
||||
<!-- 手動で書き足してもOK -->
|
||||
|
||||
## Thoughts
|
||||
<!-- 自分の考え・感想を自由に書く場所 -->
|
||||
|
||||
## Links
|
||||
<!-- 今日作成/更新されたvaultページへの自動リンク -->
|
||||
17
vault-template/templates/decision.md
Normal file
17
vault-template/templates/decision.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
date: {{date}}
|
||||
category: {{category}}
|
||||
impact: {{impact}}
|
||||
---
|
||||
|
||||
## 判断
|
||||
-
|
||||
|
||||
## 背景
|
||||
-
|
||||
|
||||
## 代替案と却下理由
|
||||
-
|
||||
|
||||
## 結果
|
||||
<!-- 判断後の結果を追記 -->
|
||||
16
vault-template/templates/meeting.md
Normal file
16
vault-template/templates/meeting.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
date: {{date}}
|
||||
client: {{client}}
|
||||
source: {{source}}
|
||||
---
|
||||
|
||||
## 決定事項
|
||||
-
|
||||
|
||||
## 宿題
|
||||
-
|
||||
|
||||
## 気づき
|
||||
-
|
||||
|
||||
## 関連
|
||||
Loading…
Add table
Add a link
Reference in a new issue