| name | inbox-daily-capture |
| description | Capture today's and tomorrow's calendar plus Google Tasks into Inbox for the core-agent daily briefing. |
| version | 1.0.0 |
| author | your-org |
| license | MIT |
| platforms | ["linux","macos","windows"] |
| metadata | {"hermes":{"tags":["vault","capture","calendar","google-tasks","vault-capture"],"related_skills":["google-workspace","google-tasks","obsidian"]}} |
inbox-daily-capture
vault の capture 係(Hermes 側)。ユーザーが指示したタイミングで実行する。今日と明日の Google Calendar 予定と
Google Tasks の未完了タスクを取得し、生のまま Inbox/{YYYY-MM-DD}/daily/daily.md に書き出す。
役割境界(重要):このスキルは Inbox/ 内にのみ書き込む。root Daily/ や
curated(Wiki)には絶対に触れない。整理(curate)と root Daily への
集約は コア側の daily-briefing が行う(single-writer 原則)。
ToDo の正本は Google Tasks。ここは読み取りの写しを置くだけで、タスクを起票しない。
前提・パス解決
- 当日の日付は実行時に取得(推測しない):
date +%Y-%m-%d
- vault ルートを解決(この順で):
OBSIDIAN_VAULT_PATH(設定されていればそれ)
- 無ければカレントディレクトリ、または
HERMES_HOME の親ディレクトリが vault に見える場合のみ採用(HERMES_HOME=.../<vault>/.hermes → vault=.../<vault>)。~/.hermes の親など vault に見えない場所へは書かない。
- 書き込み先:
{vault}/Inbox/{YYYY-MM-DD}/daily/daily.md(dated parent folder が日付を持つ。ファイル名は固定 daily.md)
- file tools は環境変数を展開しない。先にパスを文字列解決してから絶対パスで書く。
- Windows でもパスは forward slash 可(例
<vault root>/Inbox/2026-06-03/daily/daily.md)。
手順
1. 既存チェック(冪等)
Daily/{today}.md に [[Inbox/{today}/daily/daily.md]] または拡張子なしの同等 link があれば、handoff 済みなので何も書かず終了する。raw file が移動済みまたは不在でも再作成しない。
{vault}/Inbox/{today}/daily/daily.md が既にあれば上書きせず終了(同日多重実行の防止)。
- handoff 前後を問わず Hermes は同じパスを明示再生成しない。誤 capture の訂正は、handoff 後の owner であるコア + ユーザーが Daily に監査記録を残して行う。
2. Calendar 取得(ics 直 fetch・OAuth 不要)
Calendar の基本経路は 限定公開 ics URL の直 fetch に一本化している(OAuth 不要・依存が少なく壊れにくい。hermes-owned HTTP source として [[.codex/rules/agent-boundaries.md]] §1.1 push 表に整合)。
uv run "${HERMES_HOME:-$HOME/.hermes}/skills/vault-capture/inbox-daily-capture/scripts/fetch_calendar_ics.py" --format md
- 設定ファイル
scripts/calendars.local.json(.gitignore 済み・限定公開 ics URL を格納)に取得したいカレンダーを列挙する(複数可。書式は [[Meta/connections/google-calendar-tasks.md]] 経路 A 参照)。
- 出力フォーマット:
### 📅 今日の予定 / ### 📅 明日の予定 の md(そのまま Step 4 に流せる)。各予定は uid 由来の Source: gcal:event:<id> を保持する。構造化が要れば --format json、別日基準は --date YYYY-MM-DD。
- 繰り返し予定 (RRULE) 展開・ローカルタイムゾーン変換・複数カレンダー横断 dedup 済み。OAuth 不要(ics URL に token 内包)。
- 失敗時(ネット不通・config 不在等):
<!-- ics calendar unavailable: {message} --> を残してスキップ。briefing 全体は止めない。
2.5. 追加アカウントの Calendar 取得(任意・gws CLI)
組織の Google Workspace など ics の限定公開 URL を出せないアカウントが別にある場合のみ、gws(Google Workspace CLI)のアカウント別 config dir で追加取得する。1 アカウント = 1 config dir(例:既定 ~/.config/gws = 個人、追加分は GOOGLE_WORKSPACE_CLI_CONFIG_DIR="$HOME/.config/gws-<name>")。
export GOOGLE_WORKSPACE_CLI_CONFIG_DIR="$HOME/.config/gws-<name>"
gws calendar events list --params '{"calendarId":"primary","timeMin":"'"${TODAY}"'T00:00:00+09:00","timeMax":"'"${TOMORROW}"'T23:59:59+09:00","singleEvents":true,"orderBy":"startTime"}'
戻り JSON は Google Calendar API ネイティブ形状(items[])。各要素から抽出:
summary / description
start.dateTime / end.dateTime(all-day なら start.date / end.date、end.date は exclusive=翌日になっている点に注意)
attendees[].displayName(無ければ .email、ただしdomain は剥がさない)
location
- 会議URL =
hangoutLink(Meet)または conferenceData.entryPoints[] の entryPointType == "video" の uri
id(Source: gcal:event:<id> の provenance)
フィルタ:
status == "cancelled" の event は drop
visibility == "private" かつ summary が空の event は drop(情報無し)
- attendees のうち
resource == true(会議室)は drop
複数カレンダー間の重複除去:同じ予定が複数アカウントに見える構成(同期コピー等)では、Step 2 の ics 予定と (start, end) が一致する event を drop する。
注意:
- どのアカウントに繋がっているかの確認は各 config dir の
gws auth status で行う(gcloud auth list は GCP CLI のログインアカウントで、capture 対象とは無関係)。
- capture を実行するマシンのローカルに該当 config dir の資格情報が必要。
- OAuth アプリが Testing 公開ステータスのままだと refresh token が約 7 日で失効する(Google の仕様)。無人運用するなら Production 公開にするか、定期再認証で割り切る([[Meta/connections/google-calendar-tasks.md]] §5)。
- 失敗時(未 provision / 失効 / 401 等)はエラーから secret を mask(
oauth2_token=... / access_token=... / bearer ... / authorization: ...)した上で <!-- secondary calendar unavailable: {message} --> を該当セクションに残してスキップ。briefing 全体は止めない。
境界:gws は hermes 経由 capture として呼び、コア側 daily-briefing は直接叩かない。drift は control-plane を編集せず Inbox/{YYYY-MM-DD}/clippings/hermes-obs-inbox-daily-capture.md に、共通 6 fields + affected_path / observed_at / evidence / proposed_change / source を持つ observation note として残す。
3. Google Tasks 取得(list_tasks.py・library 直叩き)
list_tasks.py は Google library が入った Hermes runtime の Python を明示して呼ぶ。これは通常の repo Python (uv) とは別で、Hermes の共有 OAuth/runtime package を再利用するための限定例外。command -v hermes の隣に Python があるとは限らないので推測しない。
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
if [ -n "${HERMES_PYTHON:-}" ] && [ -x "$HERMES_PYTHON" ]; then
HERMES_RUNTIME_PY="$HERMES_PYTHON"
elif [ -x "$HERMES_HOME/hermes-agent/venv/bin/python" ]; then
HERMES_RUNTIME_PY="$HERMES_HOME/hermes-agent/venv/bin/python"
elif [ -x "$HERMES_HOME/hermes-agent/venv/Scripts/python.exe" ]; then
HERMES_RUNTIME_PY="$HERMES_HOME/hermes-agent/venv/Scripts/python.exe"
else
echo "Hermes runtime Python not found; set HERMES_PYTHON to its absolute path" >&2
exit 1
fi
GTASKS="$HERMES_RUNTIME_PY $HERMES_HOME/skills/vault-capture/google-tasks/scripts/list_tasks.py"
$GTASKS
list_tasks.py は hermes 共有 OAuth token(${HERMES_HOME}/google_token.json)を library で直叩きする設計(gws npm CLI には依存しない)。認可済みアカウントの Tasks(複数リスト横断)を返す。
- 出力 JSON を「✅ Google Tasks」節に写す(読み取りのみ・vault で新規起票しない)。各 task の
id は Source: gtasks:task:<id> として保持する。
- 非ゼロ終了(スコープ未付与・未認証等)なら、stderr の 1 行を使って
<!-- google-tasks unavailable: {message} --> を残してスキップ(briefing 全体は止めない)。
- 初回は tasks.readonly スコープ追加+再認証が必要:手順は [[.hermes/skills/vault-capture/google-tasks/SKILL.md]]。
4. 生 markdown を書き出し
- 下記フォーマットで
{vault}/Inbox/{today}/daily/daily.md を新規作成(file tools で絶対パス書き込み)。
---
title: "Daily capture - {YYYY-MM-DD}"
type: "capture"
status: "inbox"
tags: ["daily", "calendar", "google-tasks"]
created: {YYYY-MM-DD}
updated: {YYYY-MM-DD}
source: "hermes:daily:{YYYY-MM-DD}"
calendar: primary
calendar_sources: ["gcal:event:{id}", ...]
task_sources: ["gtasks:task:{id}", ...]
---
# {YYYY-MM-DD} 取り込み(raw / Hermes capture)
## 📅 今日の予定
- HH:MM-HH:MM **{summary}** ({attendees数}名){ @location }
- 参加者: {名前を , 区切り}
- 会議URL: {conferenceUrl があれば(Zoom/Meet 等)。空なら行ごと省略}
- Source: gcal:event:{id}
<!-- 予定なしなら「- なし」 -->
<!-- Step 2(ics)と Step 2.5(gws 追加アカウント)の event を start 昇順で merge、(start,end) で dedup -->
## 📅 明日の予定
- ...
## ✅ Google Tasks(未完了・写し)
- [ ] {タスク名}{ (〜MM-DD)}
- Source: gtasks:task:{id}
<!-- 未対応時: <!-- google-tasks unavailable ... --> -->
5. 完了
- 書き込んだファイルパスを 1 行で報告。他のファイルは触らない。
起動方法(on-demand)
既定 = on-demand:ユーザーが Daily ジョブリストからコアエージェントに指示し、コアが Hermes に委譲する。
手動 invoke コマンド
hermes chat -q のスキル指定は -s <skill>(--skill / --workdir というフラグは無い)。vault ルートに cd してから呼ぶ。日本語 Windows では呼び出し前に PYTHONUTF8=1 を設定する(cp932 デコード起因の出力欠落防止 → [[.codex/skills/hermes-query/SKILL.md]])。
cd "<vault root>"
hermes chat -q "Load the inbox-daily-capture skill and run it for today: fetch today's and tomorrow's Google Calendar events and (if available) Google Tasks, then write the raw markdown to Inbox/<today>/daily/daily.md in the vault. Write only inside Inbox/ — never touch root Daily/ or curated notes." -s inbox-daily-capture -Q --source core-agent
既存環境の旧 cron は過渡期ジョブとして現状維持する。新規登録・変更はせず、Daily ジョブリストから on-demand で実行する。
関連
- 整理側(core):
[[.codex/skills/daily-briefing/SKILL.md]](Inbox/{date}/daily → root Daily)
- セットアップ:[[Meta/connections/google-calendar-tasks.md]](ics 経路 / OAuth 経路の作り方・トラブルシューティング)
- 規約:
[[.codex/rules/agent-boundaries.md]](capture/curate 境界・single-writer)/ [[Inbox/README.md]]
- 依存:
google-workspace(Calendar)/ obsidian(vault 書き込み)