Communityライティング&編集github.com

ChanMeng666/slack-waterline-skill

Slack ワークスペースの全メッセージを読み取り、見逃しがないことを証明する Claude Code スキルです。未読のキャッチアップ、日次/週次ダイジェストの DM 送信、返信の下書き作成をサポートします。依存関係ゼロ、1 回のインストールで複数ワークスペースに対応。

slack-waterline-skill とは?

Slack Waterline は、Claude Code エージェントが Slack ワークスペース内のすべてのメッセージを見逃さずに読み取り、その読取状態を的確に管理・証明するためのスキルです。ユーザートークン(xoxp-)を使用して公開・プライベートチャンネル、DM、スレッド、ピン留め、添付ファイル、Google シートまでを網羅的に読み取ります。 このスキルは、**「読んだこと」と「存在すること」を厳密に区別**する設計思想に基づいており、`discover.mjs` → `fetch.mjs` → `render.mjs` → `record.mjs` という明確なレイヤー構造で動作します。各会話の読取位置(watermarkTs)は `state.json` に記録され、`record.mjs` のみが書き込むため、次回の実行は前回の続きから正確に再開できます。 `audit.mjs` と `coverage.mjs` の 2 つのゲートにより、読取状態の自己矛盾と、認識されていない会話の両方をチェックし、「見逃しがない」ことを証明します。レポート機能(`report.mjs`)は日次/週次ダイジェストを DM で送信し、返信が必要なメッセージや文脈を整理しますが、レポートを見ただけでは「読んだ」とは見なさないという明確なポリシーを守ります。 セキュリティ面では、トークンを端末やチャットログに絶対に露出させず、クリップボード経由でのみ設定します。また、機械が読取位置を進めることは禁止され、すべての書き込みは事前のドライランとユーザー確認を必須としています。複数ワークスペースを 1 つのインストールで管理でき、各ワークスペースのトークンは独立した `.env` ファイルに保存されます。

対応✓Claude Code~Codex CLI~Cursor
npx skills add ChanMeng666/slack-waterline-skill

Installed? Explore more ライティング&編集 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Slack waterline

Slack ワークスペースを、時間があれば人が行うのと同じように読み取ります:すべてのチャンネル、すべての DM、すべてのスレッド返信、ピン留めメッセージ、添付ファイル、そして人々がリンクしている Google シートまで。そして、実際に読まれたものだけを正確に記録し、次回の実行は今回の終了位置から開始します。

ウォーターマーク(水線)は、その線より下では何も見逃せない線です。 このスキルのすべては、その線を正直に保つために存在します。

たった一つのルール

既読レコードは、DELIVERED(実際に届けられた)ものをのみ記述でき、EXISTS(存在する)ものを記述してはならない。

フェッチすることは読むことではありません。ペイロードが構築されたが誰も見ていないなら、それは読まれていません。それを既読として記録することは、メッセージを一時的に失うのではなく、恒久的に失うことです。なぜなら次回の実行では何も見つけるべきものが残っていないからです。

以下はすべてこれに従います。


ゼロからのセットアップ

何も設定されていない場合 — ワークスペースがない、トークンがない、スコープが不足している — まずドクターを実行し、その指示に従ってください。 状態を推測してはいけません。

cd skills/slack-waterline/scripts
node setup.mjs                       # または --workspace acme

Node、ワークスペース、トークン、すべてのスコープ、そして既読状態をチェックし、最初の実際の障害で停止し、それをクリアするための正確なコマンドまたはクリックを表示します。各修正後に再度実行してください。完全な手順は references/setup.md にあります。

新しいワークスペースの通常の手順:

node init.mjs  --workspace acme --dir /path/to/acme-slack-archive
node setup.mjs --workspace acme --manifest --out slack-app.yaml
# ユーザーがそのマニフェストからアプリを作成し、トークンをコピー
node setup.mjs --workspace acme --token-from-clipboard
node setup.mjs --workspace acme       # 確認; その後 discover + catchup

マニフェストが重要です:12 個のスコープすべてを一度に設定し、ユーザーがチェックボックスを探す必要がなくなり、それらを User Token Scopes の下に配置します。これが、DM を 1 つも見られないトークンを生成する原因となる間違いです。

1 つのスキル、複数のワークスペース。 ~/.slack-waterline/config.json のレジストリにはパスのみが保持されます。トークンは各ワークスペースの独自の .env に保存されます。node init.mjs --list で登録済みのワークスペースを表示します。

トークンを自分で扱わないこと

ライブトークンは、端末、シェル履歴、チャットのトランスクリプトに決して入力してはいけません。

トークンは Slack のページから一つのファイルに移動するだけでよいので、クリップボード経由で移動します:

node setup.mjs --workspace acme --token-from-clipboard

これにより、トークンを検証し、書き込み、Slack に対して確認し、どのチームとユーザーに属するかのみを表示します。トークンをコマンドライン引数として受け入れたり渡したりしてはいけません。ユーザーにチャットに貼り付けさせてはいけません。自分でファイルに書き込んでもいけません。

セットアップのためのブラウザ操作

ユーザーが Chrome での手順を求められた場合、references/setup.md に正確なツールコールを含むステップバイステップがあります。その流れ:

  1. まずマニフェストを生成し、必要なときに YAML を用意します。
  2. 1 回の ToolSearch 呼び出しでブラウザツールをロードし、タブを作成する前に tabs_context_mcp を実行します。
  3. 操作:マニフェストからアプリを作成 → インストール → OAuth & Permissions に移動。
  4. 許可をクリックする前に、スコープリストをユーザーに読み上げて表示します。それはユーザーのワークスペースであり、ユーザーの同意です。
  5. トークンで停止します。 ユーザーに コピー をクリックするよう依頼し、その後 setup.mjs --token-from-clipboard を実行します。

ページからトークンを読み取ってはいけません — read_page、get_page_text、find、javascript_tool、またはそのフィールドを狙ったスクリーンショットは使用せず、表示をクリックしてマスクを解除してもいけません。Slack はデフォルトでマスクしています。トークンをキャプチャすると、セッションを超えて存続し、漏洩よりもはるかに取り消しが難しいトランスクリプトにライブクレデンシャルが書き込まれます。ステップ 5 はそれを防ぐために存在します。

Client Secret、Signing Secret、App-Level Tokens には絶対に触れないでください — このスキルにはそれらは必要ありません。ユーザーがサインアウトしている場合は、サインインを依頼してください。ログインフォームを操作してはいけません。

トークン

SLACK_USER_TOKEN (xoxp-)SLACK_BOT_TOKEN (xoxb-)
公開チャンネル参加せずに見えるすべて参加したもののみ
プライベートチャンネルあなたが入っているすべて/invite されたもののみ
DM / グループ DMあなたのすべてなし
スレッド、ピン留め、ブックマーク、ファイル可可

ユーザートークンが存在する場合はそれが優先されます — 読み取りに関しては厳密なスーパーセットです。ボットトークンは DM を 1 つも見ることができません。そのため、個人用キャッチアップツールとしてはほとんど単体では役に立ちません。

ユーザートークンスコープ:channels:history channels:read groups:history groups:read im:history im:read mpim:history mpim:read users:read files:read pins:read bookmarks:read。ダイジェスト DM と返信送信のためだけに chat:write を追加します。

発行については references/tokens-and-scopes.md を参照してください。

修正不可能な 2 つの死角

カバレッジが存在しないことを約束するのではなく、これらを明確に述べてください:

  1. 他の 2 人間の DM。 Slack の Discovery API が必要 — Enterprise Grid のみ。
  2. 誰もトークンを持っていないプライベートチャンネル。 人間が誰かを /invite する必要があります。conversations.join はプライベートチャンネルを拒否します。

キャッチアップ — 通常のパス

node catchup.mjs --workspace acme --unread

これによりトリアージが実行され、アクション可能なすべてをフェッチし、各会話をレンダリングし、レンダリングされたデルタのインデックスと準備されたレコードバッチを出力します。

そしてそれらを読みます。 catchup.mjs は意図的に何も記録しません。

  1. インデックスにリストされた各レンダリング済み .md を読みます。大きなバックログの場合は、サブエージェントをバッチに分散します — 各サブエージェントは結果のみを報告し、生のメッセージテキストをメインコンテキストに持ち帰ることはありません。
  2. 開かなかったデルタのエントリを tmp/record-batch.json から削除します。
  3. 読んだ内容を記録します。理解が新しいうちにダイジェストも記録します:
node record.mjs --workspace acme --batch tmp/record-batch.json
node record.mjs --workspace acme --from tmp/<id>.json \
  --digest "スプリントのスコープは火曜日に確定。未処理:Olga の PR はレビューが必要。"

ダイジェストは沈殿物です。ウォーターマークは次回の実行で古いメッセージを再フェッチするのを防ぎます。ダイジェストはその意味を再導出するのを防ぎます。

  1. 何も隠れていないことを証明します:
node audit.mjs --workspace acme
node coverage.mjs --workspace acme --enumerate-only

読み取り範囲の指定

node catchup.mjs --workspace acme --all              # すべての会話
node catchup.mjs --workspace acme --since 7d         # 期間指定
node catchup.mjs --workspace acme --channels general,eng-backend
node catchup.mjs --workspace acme --dms-only
node catchup.mjs --workspace acme --all --full       # 初回アーカイブパス

レイヤー

レイヤー A — discover.mjs はワークスペース全体をスキャンし、会話ごとに 1 行を出力します。メッセージ本文は出力しません。これにより、モデルは 1 回の小さな読み取りでワークスペースの形状を学習します。列:新規数、優先度、アクション。

レイヤー B — fetch.mjs → render.mjs は重要なものを開きます。

ペイロードは render.mjs で読み取ってください。head、tail、jq を使用しないでください — それらは JSON ファイルの先頭部分を取得しますが、会話の先頭部分ではなく、スレッド返信を静かにドロップします。

render.mjs は出力の順序を意図的に決めています:以前のダイジェスト、次にピン留め(正規のもの。ウォーターマークに関係なく常にフェッチ)、ブックマーク、次にランシートとドキュメント、そして新しいメッセージ。ドキュメントはチャットよりも優先されます — 決定は通常スプレッドシートにあり、チャンネルはそれに反応している人々です。


読み取り状態 — 3 つの位置、それぞれ 1 つの書き手

state.json、チャンネル ID をキーにします(これはリネーム後も有効です)。

フィールド意味唯一の書き手
watermarkTs読了 — 読者に配信された最新のトップレベル tsrecord.mjs --from
scannedTsトリアージ — トリアージがスコアリングした最新の tsdiscover.mjs、ただし静かな行のみ
pendingTsSlack — トリアージがクリアできなかった行に Slack が保持していたものdiscover.mjs
readAt実際のペイロードが配信されたことrecord.mjs --from
readAtSourceその主張を狭める注釈--read-source

書き手を追加する場合:それは scannedTs を移動するか、読者にコンテンツを配信します。両方同時には不可、どちらも不可でも不可。

references/read-state.md は、それぞれがなぜ存在するのか、そしてそれ以前に何が未読だったのかを説明しています。lib/state.mjs の何かを変更する前に読んでください。

スレッドはメッセージが隠れる場所です

返信は親メッセージを移動しません。 ライブ会話全体が 1 つの古いスレッド内で発生しているチャンネルは、トップレベルから見ると完全に静かに見えます。そのため state.json はスレッドごとに replyCount + latestReplyTs を保存し、行が incremental (thread replies) と読める理由です — その会話を目で確認しても新しいものは何もなく、スレッドを開く必要があります。

シグナルゲートは PRIORITY を決定し、読了かどうかは決定しません

スコアはテーブルをソートします。スコアは行を解決済みにすることはありません。静かなチャンネルがついに重要なことを言った場合、スコアが 1 だからといってスキップされてはいけません。


異なる方法で失敗する 2 つのゲート

毎回両方を実行してください。

node audit.mjs --workspace acme                      # オフライン、即時
node coverage.mjs --workspace acme --enumerate-only  # ネットワーク、数秒

レコードを反復処理するゲートは、レコードが自己矛盾がないことしか確認できません。

audit.mjs は state.json を読みます — そのため、一度も列挙されたことのない会話はそれに対して不可視です。「クリーン」ではなく、不可視です。coverage.mjs --enumerate-only は conversations.list に対して exclude_archived: false で別の質問をします。そこに忘れられたチャンネルが存在します。

バックログはビルド失敗ではありません。これらは発見数で終了するため、スクリプトは分岐できます。誰かが Slack に投稿したためにスケジュール実行が赤くなると、全員が赤を無視するように訓練されます。


レポート

node report.mjs --workspace acme --period day
node report.mjs --workspace acme --period week --send

セクション:返信が必要、あなたに質問したが既にリアクション済み、コンテキスト、およびこのダイジェストが見えなかったもの。--send はそれをあなたに DM で送信し、長い場合は最初のメッセージの下にチャンク化してスレッド化します。

レポートはビューであり、読み取りではありません。 report.mjs は state.json に触れません。要約でメッセージを知らされることは、会話を読んだことと同じではありません。この 2 つを混同すると、誰もクリアしていないのにバックログが消えます。レポートはそのフッターにその旨を記載します。

スケジュール設定:assets/schedule-task.ps1 は Windows スケジュールタスクを登録します。assets/github-workflow.tmpl はクラウド版です。自動実行はトリアージに --no-record を渡します — マシンは決して読み取り位置を進めてはいけません。


Slack への書き込み

send.mjs はボットではなくあなたとして投稿します。オプションではない 2 つのガードレール:

  1. 常に最初に --dry-run を実行し、解決されたターゲットと正確なテキストをユーザーに表示してから送信してください。 自分の判断で送信してはいけません。
  2. Slack には送信取消しはありません。 すべての試行は API 呼び出しの前と後の両方で sent.log.jsonl に記録されます。
node send.mjs --workspace acme --to '@Saba' --file draft.md --dry-run
node send.mjs --workspace acme --to '#general' --text '…' --thread 1785…
node send.mjs --workspace acme --to '@me' --text '…'

--update <ts> はメッセージをその場で編集し、--reason が必要です。これはログに記録されます。ドライランは、上書きする内容を確認できるように、置換テキストの上に現在のリモートテキストを出力します。あいまいな @Name は拒否され、推測されることはありません。

返信の下書き

まずスレッドを読み(fetch.mjs + render.mjs)、ファイルに下書きを書き、ユーザーに表示してから送信します。下書きはスキルリポジトリではなく、ワークスペースディレクトリに書き込みます。


添付ファイル、シート、ドキュメント

node download-file.mjs F0ABC123 out/deck.pdf --workspace acme
node extract-docx.mjs out/bio.docx
node fetch-sheet.mjs 'https://docs.google.com/spreadsheets/d/…' --workspace acme

リンク内の gid はスコープではありません。

#gid= フラグメントは、誰かが URL をコピーしたときにたまたまブラウザが表示していたタブを記録します。fetch-sheet.mjs はすべてのタブを読み取り、リンクされたタブを最初にソートするだけです。--tab で意図的に絞り込むか、決して偶然に絞り込まないでください。

download-file.mjs と fetch-sheet.mjs は、応答がデータではなく HTML ページの場合に大声で拒否します — Slack と Google は両方とも HTTP 200 でログインページとエラーページを返すため、単純な読み手は .pdf という名前でログインページをディスクに書き込み、成功と呼びます。


アーカイブ

node fetch.mjs --workspace acme --many ids.txt --out-dir raw/ --skip-existing
node archive.mjs --workspace acme

Markdown(JSON ではありません)— 誰が何に同意したかを grep する人は、何もパースする必要があってはなりません。機械的なメッセージは削除されかつカウントされます。conversations/ は実行のたびにワイプされ再構築されます。リネームや新しい暦年によって孤立したファイルが残り、grep が両方のコピーを見つけてどちらが最新かわからなくなるからです。

--skip-existing は再開可能性ですが、古いリストでは罠です:すべての古いペイロードはすでに完了しているため、このフラグはその実行を完全な no-op にします。「新しい会話」パスでのみ使用してください。


ここに追加するもののための規則

  • 標準出力はペイロード。診断は標準エラー出力に。 fetch.mjs は JSON を標準出力に書き込みます。そのチャンネルを共有するロガーは、負荷がかかるとバグになります。
  • --out を使用し、> は使用しない。 シェルリダイレクトはスクリプトの実行前にターゲットを切り詰めるため、失敗したフェッチは置き換えようとしていたペイロードを破壊します。
  • アトミック書き込み。 一時ファイル、その後リネーム。半分書き込まれたマニフェストは、読まれたものを主張します。
  • 散文はファイルを経由する。 --digest-file、--reason-file、--batch:PowerShell は bash がしない場所で引用符を二重にします。ファイルには引用符ルールがまったくありません。
  • あいまいさを拒否する。 間違った人の DM を読むことは回復不可能です。

このスキルがやらないこと

  • 自分の判断で何かを投稿する。すべての書き込みは最初に表示されます。
  • 誰も読んでいないコンテンツのために読み取り位置を進める。
  • スコアが低いために会話を解決済みとマークする。
  • 上記の 2 つの死角のカバレッジを主張する。
  • シークレットをコピーする。クレデンシャル、アクセスコード、プライベート招待リンクを Slack の外に移動してはいけません。

関連スキル