SLOPSHOPPER

vault-guard

Obsidian保管庫のルールを機械的に守らせる: 公開領域から非公開ノートへのリンク、カレンダーの色ルール

guard
A shopper browsing a rack in a slop shop
README

obsidian-ai-knowledge-lake

Obsidian の保管庫を「AI と共有する自分のナレッジレイク」にして、作業の多くを AI(Claude Code / Codex)に任せられるようにするための一式。 作者が自分の保管庫で実際に運用している仕組みから、個人の中身を抜いて汎用化したもの。

中身

フォルダ何か最初に読むもの
knowledge-lake-kit/保管庫の器。 フォルダ構成、AI への指示書(AGENTS.md)、各フォルダの README、テンプレート、運用の考え方START_HERE.md(人間)/ SETUP_FOR_AI.md(AI)
agent-tooling/AI に持たせる道具。 スキル(/inbox・/prioritize など)、MCP(Gmail 横断・Google Tasks)、Claude Code の mod、フック、CLI、定期実行README.md(人間)/ FOR_AI.md(AI)

使い方

git clone https://github.com/KaiMiyazawa/obsidian-ai-knowledge-lake.git
  1. 保管庫を作る: knowledge-lake-kit/START_HERE.md を上から順に進める。最後は AI がインタビューしながら保管庫を組み立てる。
  2. 道具を入れる(任意): 保管庫ができて慣れてきたら、保管庫で AI に次のように頼む。 > <クローンした場所>/agent-tooling/FOR_AI.md を読んで、この道具を私の保管庫に入れるのを手伝って

どちらも「AI が読んで、利用者に確認しながら進める」前提で書いてある。全部を一度に入れなくてよい。

注意

  • 秘密の値(トークン・パスワード・Webhook など)は入っていない。作者の個人情報は伏せ字(you@gmail.com、<MAIN_LIST_ID> など)にしてある。
  • 自分の保管庫は private リポジトリで管理する。 このリポジトリを fork して中身を書き足し、公開したままにしない。
  • 道具は作者の環境(macOS、Claude Code・Codex の特定の版)で作ったもの。そのまま動く保証はなく、利用者の環境に合わせて AI と直しながら使う。

ライセンス

MIT。ただし agent-tooling/outside-vault/gtasks-mcp/ は alvinjchoi/gtasks-mcp(MIT)を改造したもので、同フォルダの LICENSE に従う。

Source 1 files
hooks/register.ts 77 lines
1import type { Register } from 'claude-code'
2
3// 公開前提の領域(AGENTS.md「編集時の注意」)。ここへの書き込みに、名前で中身が分かる非公開ノートへの参照を入れさせない。
4const PUBLIC_AREA = /\/Obsidian\/(99_public|89_profile)\//
5const PRIVATE_REF = /(08_people\/|06_health\/|relationship-end)/
6
7// 02_note/calendar-color-rules.md の要約。拒否理由としてモデルに渡すので、mdを読み直さなくて済む量に絞る。
8const CALENDAR_RULES = [
9  '色ルール(02_note/calendar-color-rules.md): 赤→オレンジ→仮→時間だけ→ジャンル、の順に決める。',
10  '11=重要(取り返しがつかない。月5件まで、迷ったら6) / 6=準重要 / 1=仮・未確定 / 9=大学・学び / 10=仕事・収入 /',
11  '3=プライベート / 4=健康・通院 / 8=繰り返しの生活予定 / 5=目印・期間 / 指定なし=雑多な用事 / 7(ピーコック)は使わない。',
12].join('\n')
13
14const CALENDAR = 'mcp__claude_ai_Google_Calendar__'
15
16type CalendarArgs = {
17  summary?: string
18  startTime?: string
19  endTime?: string
20  allDay?: boolean
21  colorId?: string
22}
23
24// 終日予定を+09:00で渡すと1日前にずれる(2026-10-01に実際にずれた)。
25const allDayOffsetProblem = (a: CalendarArgs) =>
26  a.allDay === true && [a.startTime, a.endTime].some(t => t !== undefined && !t.endsWith('Z'))
27
28export const register: Register = on => {
29  on('tool.call', { tool: 'Write' }, ($, e, next) =>
30    PUBLIC_AREA.test(e.file_path) && PRIVATE_REF.test(e.content)
31      ? { deny: publicDeny(e.content) }
32      : next(e),
33  )
34
35  on('tool.call', { tool: 'Edit' }, ($, e, next) =>
36    PUBLIC_AREA.test(e.file_path) && PRIVATE_REF.test(e.new_string)
37      ? { deny: publicDeny(e.new_string) }
38      : next(e),
39  )
40
41  // 色なしの1回目は止めて確認させ、同じ引数での再実行は「雑多な用事」として通す。
42  const confirmedUncolored = new Set<string>()
43
44  on('tool.call', ($, e, next) => {
45    const tool = String(e.tool)
46    if (!tool.startsWith(CALENDAR)) return next(e)
47    const isCreate = tool === `${CALENDAR}create_event`
48    if (!isCreate && tool !== `${CALENDAR}update_event`) return next(e)
49
50    const args = e as unknown as CalendarArgs
51    if (args.colorId === '7') {
52      return { deny: `vault-guard: colorId 7(ピーコック)は使わない。\n${CALENDAR_RULES}` }
53    }
54    if (allDayOffsetProblem(args)) {
55      return {
56        deny: 'vault-guard: 終日予定の日付は 2026-12-23T00:00:00Z のように Z で渡す(+09:00 だと1日ずれる)。終了日は最終日の翌日。',
57      }
58    }
59    if (isCreate && args.colorId === undefined) {
60      const key = `${args.summary}|${args.startTime}`
61      if (!confirmedUncolored.has(key)) {
62        confirmedUncolored.add(key)
63        return {
64          deny: `vault-guard: colorId が未指定。\n${CALENDAR_RULES}\n雑多な用事で本当に色なしなら、同じ引数のまま再実行すれば通る。`,
65        }
66      }
67    }
68
69    return next(e)
70  })
71}
72
73const publicDeny = (text: string) => {
74  const hit = text.match(PRIVATE_REF)?.[0]
75  return `vault-guard: 公開領域(99_public / 89_profile)に非公開ノートへの参照「${hit}」が入っている。公開サイトでも GitHub 上でパスが見えるので外す(AGENTS.md「編集時の注意」)。`
76}
77