Imported from tarotene/dotfiles (
config/claude/skills/slot-availability/SKILL.md). Install upstream withnpx skills add tarotene/dotfiles --skill slot-availability. Copyright stays with the author.
候補日程一覧(調整さん等の日程調整サービス、LINE・メールでの空き日程の
問い合わせ等)とユーザーのカレンダーを素朴に「時間指定予定と重なるか」
だけで突き合わせると、終日イベントとして登録された試験・研修等
(Google Calendar 上は transparent = 予定なし扱いのことが多い)を見落とし、
実際には拘束される日を ○ と誤判定する。判定ルールを毎回その場で言葉で
組み立てるとブレが出るため、判定ロジック(判定コア)は決定的なスクリプトに
固定し、Claude は候補一覧の取得・構造化、MCP 経由の読み書き、合意形成
(判定表の提示・承認)を担う。
候補一覧の取得元は調整さんに限らない(LINE アンケート・Spir・TimeRex・
口頭やチャットでの自由文など、さまざまな形で来る)。判定コア
(scripts/slot-hit.py の judge サブコマンド)は取得元を問わない構造化
JSON だけを入力に取るため、下記「汎用手順」はどの取得元でも共通に使える。
取得元ごとに異なるのは「候補一覧をどう読み取り構造化するか」と「判定後の
回答をどう書き戻すか」の2点だけで、これを個別の節(現時点では調整さん
向けの1本のみ)に分けている。次の取得元が実際に必要になった時点で、
その節を追記する。
いつ発動するか
- 人から提示された候補日程一覧(調整さん等のサービス、チャットでの 日程列挙など)を、ユーザーの Google Calendar の空き状況と突き合わせて 回答したい場面
- 押さえた候補にカレンダー側へ仮予定(マーカー)を作りたい場面
前提: 個人設定
判定ルール(コマの時間境界・判定元カレンダーの allowlist・除外プレフィクス
等)はこのリポジトリに置かない — ユーザー個人のカレンダー構成・所属団体名
を含むため。既定の読み込み先は ~/.config/slot-availability/config.toml。
存在しなければ、どこに置くか・どう作るかをユーザーに確認する(個人の主
カレンダーに無断で書き込まないのと同じ理由)。設定スキーマは
scripts/slot-hit.py --help と docs/claude/slot-availability.md を参照。
汎用手順
どの取得元でも共通の手順。手順1と手順7は取得元ごとに具体的なやり方が 異なる — 下記「調整さん向け具体手順」のような節を都度参照する。
- 候補日程一覧を取得し、構造化 JSON(
[{"date": "YYYY-MM-DD", "time": "HH:MM"}, ...])に変換する。日付が年を含まない・自由文である 等、取得元によって表記はさまざまだが、この手順の出口は必ずこの 構造化 JSON に揃える(判定コアはこの形式以外を受け付けない)。 - 設定の
[[calendars]]allowlist をlist_calendarsで ID 解決し、 候補期間を覆う範囲で各カレンダーをlist_events(timeZoneを設定のtimezoneに、pageSizeは大きめ)する。1 レスポンスがサイズ超過で ファイル保存になった場合はそのファイルパスをそのまま次の手順に渡す。 - 判定は
scripts/slot-hit.py judgeに任せる。手で ○△× を組み立てない — バッファ・終日イベントの扱いなど、その場の判断にブレが出やすい 要素を全てスクリプトに閉じているため。python3 scripts/slot-hit.py judge --config <config.toml> \ --candidates <candidates.json> --events-dir <dir> \ --event-title <イベント名> --source-url <候補日程一覧の URL> \ --out-plan <plan.json>--candidatesは手順1で作った構造化 JSON。--events-dirは手順2で 得たlist_eventsの生 JSON レスポンスを<calendarId>.jsonの ファイル名で置いたディレクトリ。 - 終日イベントが
soft_day_prefixesに一致し、かつ同日に時間指定の 確定予定が無い場合、スクリプトはエラー終了する。 これは「実際の 拘束時間が Google Calendar に正規化されていない」ことを意味する — エラーを握りつぶして ○ を出さない。一次情報(公式サイト等)で拘束 時間帯を調べ、create_eventで時間指定の予定として書き戻してから (下記「カレンダーの正規化」)再実行する。 - スクリプトが出す判定表(Markdown)を提示し、AskUserQuestion で 承認を取る。判定はカレンダーに載っている情報だけを反映する旨を明記 する(購読カレンダー側が更新されていない可能性がある)。
- 承認後、○/△ の全コマにマーカー予定を作る。
search_eventsで候補日程一覧の URL を含む既存マーカーを検索し、 見つかればdelete_eventしてから作り直す(再実行の冪等化)。- 件名:
【調整中】<イベント名>(△ は末尾に[△]を付ける)。 - 説明欄: 候補日程一覧の URL と、その枠を選んだ理由(空き/バッファ内)。
opaque、コマの時間区間そのまま、書き込み先は設定のmarker_calendar(主カレンダーに書く場合は事前にユーザーへ確認済み であることが前提 — 本人裁定が無ければ確認してから作る)。
- 判定表の承認後、取得元に回答を書き戻す。書き戻し方は取得元ごとに 異なる(下記「調整さん向け具体手順」参照)。
調整さん向け具体手順
上記「汎用手順」の手順1・手順7を調整さん(chouseisan.com)で行う場合の 具体的なやり方。
手順1(候補一覧の取得・構造化): ブラウザで開き、日程候補の表を
1 列ずつ読む。表記は M/D(曜) H:MM〜 で年を含まない。年は
scripts/slot-hit.py infer-year --month <M> --day <D> --weekday <曜> \ --today <YYYY-MM-DD> で解決する(今日以降で最初に曜日が一致する年を
1 行で返す)。手で曜日と年の対応を数えない — 閏年・年末年始をまたぐ候補が
混ざると誤りやすいため、必ずこのコマンドに委ねる。
手順7(回答の書き戻し): 名前だけでログイン不要のサービスが多く、
一度投稿すると同じブラウザからしか編集できない仕様があるため、承認を
経ずに投稿しない。判定表の承認後、投稿直前にユーザー名を再確認してから
「Add Attendance」ボタンを開き、plan.json の answers 配列を候補一覧の
表示順どおりに各行の ✔/?/X ボタンへ反映し(○→✔、△→?、×→X)、コメント欄に
△の理由があれば一言添えてから送信する。送信後はスナップショットで全行が
意図通り反映されたことを確認する。
カレンダーの正規化(調べて分かった事実は Calendar に書き戻す)
判定のために外部の一次情報(公式サイト等)を調べて拘束時間が判明したら、 その事実は判定スクリプトの設定ファイルに転記せず、Google Calendar 側に 時間指定の予定として書き戻す。設定ファイルは判定ルール(コマ境界・ allowlist)だけを持ち、個々の予定の事実(いつ拘束されるか)は Calendar 1 箇所を正本にする — 同じ事実を 2 箇所で管理すると、どちらかが古くなった ときに判定が静かに誤る。
書き戻す予定には、説明欄に出典 URL・取得日・(該当すれば)未確定要素 (会場未公開など)を明記する。既存の終日イベント(「その日は試験」といった 粗い印)は残してよい — 追加するのはその中の具体的な時間帯の予定。
外部サービスへの書き込みはサニタイズ対象
候補日程一覧サービスやカレンダーに送る文章は、external-call-scheduling
と同じ理由でサニタイズする — プロジェクト名・リポジトリ名・内部の Issue
番号・内部ファイルパスを書かない。
事例
(まだ無し。新しい失敗事例が出たらここに追記する。)
