CommunitySchreiben & Editierengithub.com

Slack Waterline

Liest jeden Nachricht in einem Slack-Workspace und beweist, dass nichts übersehen wurde – ein Claude Code Skill, der dich auf den neuesten Stand bringt, täglich/wöchentlich eine Zusammenfassung per DM schickt und Antworten entwirft. Null Abhängigkeiten, viele Workspaces von einer Installation aus.

Was ist Slack Waterline?

Der Slack Waterline Skill ist ein Werkzeug für KI-Agenten wie Claude Code, das sicherstellt, dass kein Nachricht in einem Slack-Workspace ungesehen bleibt. Er liest systematisch jeden Kanal, jede DM, jeden Thread, angepinnte Nachrichten, Dateien und verlinkte Google Sheets und verfolgt genau, was gelesen wurde (den "Wasserstand"), damit nachfolgende Durchläufe genau dort weitermachen, wo der letzte aufhörte. Der Skill unterstützt mehrere Workspaces aus einer einzigen Installation und arbeitet ohne externe Abhängigkeiten. Er bietet eine Triage (`discover.mjs`), ein Nachholen (`catchup.mjs`), eine Aufzeichnung des Lesefortschritts (`record.mjs`), Prüfungen (`audit.mjs`, `coverage.mjs`), Berichte (`report.mjs`) und das Senden von Nachrichten (`send.mjs`). Die Kernphilosophie ist, dass eine Lesebestätigung nur das beschreiben darf, was tatsächlich zugestellt (gelesen) wurde, nicht was existiert – so wird verhindert, dass Nachrichten versehentlich als gelesen markiert werden.

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

Installed? Explore more Schreiben & Editieren skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Slack Waterline?

Liest einen Slack-Workspace so, wie eine Person es tun würde, wenn sie Zeit hätte: jeden Kanal, jede DM, jede Thread-Antwort, die angepinnten Nachrichten, die Anhänge und die Google Sheets, die Leute ständig verlinken. Dann zeichnet es genau auf, was gelesen wurde – und nur, was gelesen wurde –, sodass der nächste Durchlauf genau dort weitermacht, wo dieser aufgehört hat.

Der Wasserstand ist die Linie, unterhalb derer nichts übersehen werden kann. Alles in diesem Skill existiert, um diese Linie ehrlich zu halten.

Die eine Regel

Eine Lesebestätigung darf nur beschreiben, was ZUGESTELLT wurde, niemals, was EXISTIERT.

Abrufen ist nicht Lesen. Wenn eine Nutzlast erstellt, aber niemand sie angesehen hat, ist sie nicht gelesen, und sie als gelesen aufzuzeichnen verliert eine Nachricht nicht vorübergehend – es verliert sie dauerhaft, weil der nächste Durchlauf nichts mehr zu finden hat.

Alles unten folgt daraus.


Von Null an einrichten

Wenn irgendetwas nicht konfiguriert ist – kein Workspace, kein Token, fehlende Berechtigungen –, führe zuerst den Doctor aus und befolge, was er sagt. Rate nicht über den Zustand.

cd skills/slack-waterline/scripts
node setup.mjs                       # oder --workspace acme

Er prüft Node, den Workspace, das Token, jede Berechtigung und ob etwas gelesen wurde, hält dann beim ersten echten Blockierer an und gibt den genauen Befehl oder Klick aus, der ihn beseitigt. Führe ihn nach jeder Korrektur erneut aus. Vollständige Anleitung in references/setup.md.

Die übliche Reihenfolge für einen brandneuen Workspace:

node init.mjs  --workspace acme --dir /pfad/zu/acme-slack-archiv
node setup.mjs --workspace acme --manifest --out slack-app.yaml
# Benutzer erstellt die App aus diesem Manifest und klickt auf "Kopieren" beim Token
node setup.mjs --workspace acme --token-from-clipboard
node setup.mjs --workspace acme       # bestätigen; dann entdecken + nachholen

Das Manifest ist wichtig: Es setzt alle zwölf Berechtigungen in einem einzigen Einfügen, statt dass der Benutzer Kontrollkästchen suchen muss, und platziert sie unter Benutzer- Token-Berechtigungen, was der Fehler ist, der ansonsten ein Token erzeugt, das keine einzige DM sehen kann.

Ein Skill, viele Workspaces. Das Register unter ~/.slack-waterline/config.json enthält nur Pfade; Tokens leben im eigenen .env jedes Workspaces. node init.mjs --list zeigt, was registriert ist.

Behandle das Token niemals selbst

Ein lebendiges Token darf niemals in ein Terminal, eine Shell-Historie oder ein Chat-Transkript gelangen.

Es muss nur von der Slack-Seite in eine Datei reisen, also bewege es per Zwischenablage:

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

Das validiert es, schreibt es, verifiziert es gegen Slack und gibt nur aus, zu welchem Team und Benutzer es gehört. Akzeptiere oder übergib ein Token niemals als Kommandozeilen- argument, bitte den Benutzer niemals, es in den Chat einzufügen, und schreibe es niemals selbst in eine Datei.

Browser-gestützte Einrichtung

Wenn der Benutzer bittet, Schritt für Schritt in Chrome geführt zu werden, enthält references/setup.md die detaillierte Anleitung mit den genauen Tool-Aufrufen. Der Ablauf:

  1. Generiere zuerst das Manifest, damit das YAML vorliegt, bevor es benötigt wird.
  2. Lade die Browser-Tools in einem ToolSearch-Aufruf, dann tabs_context_mcp, bevor ein Tab erstellt wird.
  3. Führe: App aus Manifest erstellen → installieren → auf OAuth & Berechtigungen landen.
  4. Lies die Berechtigungsliste dem Benutzer vor dem Klick auf "Zulassen" vor. Es ist sein Workspace und seine Zustimmung.
  5. Halte am Token an. Bitte den Benutzer, auf "Kopieren" zu klicken, führe dann setup.mjs --token-from-clipboard aus.

Lies das Token nicht von der Seite aus – kein read_page, get_page_text, find, javascript_tool oder Screenshot, der auf dieses Feld zielt, und klicke nicht auf "Anzeigen", um es zu entschleiern. Slack maskiert es standardmäßig; das Erfassen würde eine live Anmeldeinformation in ein Transkript schreiben, das die Sitzung überdauert und weitaus schwieriger zu widerrufen als zu leaken ist. Schritt 5 existiert, damit das nie passieren muss.

Fasse niemals Client Secret, Signing Secret oder App-Level-Token an – dieser Skill benötigt keines davon. Wenn der Benutzer abgemeldet ist, bitte ihn, sich anzumelden; steuere niemals ein Anmeldeformular.

Tokens

SLACK_USER_TOKEN (xoxp-)SLACK_BOT_TOKEN (xoxb-)
Öffentliche Kanälealles, was du sehen kannst, ohne beizutretennur die, denen er beigetreten ist
Private Kanälejeder, in dem du bistnur wo /invite eingeladen
DMs / Gruppen-DMsalle deineniemals
Threads, Pins, Lesezeichen, Dateienjaja

Das Benutzer-Token wird bevorzugt, wenn vorhanden – zum Lesen ist es eine echte Obermenge. Ein Bot-Token kann keine einzige DM sehen, daher ist es für ein persönliches Nachhol- Tool nahezu nutzlos.

Benutzer-Token-Berechtigungen: channels:history channels:read groups:history groups:read im:history im:read mpim:history mpim:read users:read files:read pins:read bookmarks:read. Füge chat:write nur für die Zusammenfassungs-DM und das Senden von Antworten hinzu.

Siehe references/tokens-and-scopes.md zum Erstellen eines Tokens.

Zwei blinde Flecken, die nicht behoben werden können

Benenne diese klar, anstatt eine Abdeckung zu versprechen, die nicht existiert:

  1. DMs zwischen zwei anderen Personen. Benötigt Slacks Discovery-API – nur Enterprise Grid.
  2. Ein privater Kanal, in dem niemand mit einem Token ist. Ein Mensch muss jemanden per /invite einladen. conversations.join lehnt private Kanäle grundsätzlich ab.

Nachholen – der normale Weg

node catchup.mjs --workspace acme --unread

Das führt die Triage aus, ruft alles Handlungsrelevante ab, rendert jede Unterhaltung und gibt einen Index der gerenderten Deltas plus einen vorbereiteten Aufzeichnungsbatch aus.

Dann liest du sie. catchup.mjs zeichnet bewusst nichts auf.

  1. Lies jede gerenderte .md aus dem Index. Bei einem großen Rückstand verteile Unteragenten auf Batches – jeder meldet nur Ergebnisse und trägt niemals rohen Nachrichtentext zurück in den Hauptkontext.
  2. Lösche aus tmp/record-batch.json jeden Eintrag, dessen Delta du nicht geöffnet hast.
  3. Zeichne auf, was du gelesen hast, mit einer Zusammenfassung, solange das Verständnis frisch ist:
node record.mjs --workspace acme --batch tmp/record-batch.json
node record.mjs --workspace acme --from tmp/<id>.json \
  --digest "Sprint Scope wurde Dienstag festgelegt. Offen: Olgas PR braucht Review."

Die Zusammenfassung ist das Sediment. Der Wasserstand verhindert, dass der nächste Durchlauf alte Nachrichten erneut abruft; die Zusammenfassung verhindert, dass er ihre Bedeutung erneut ableitet.

  1. Beweise, dass nichts versteckt ist:
node audit.mjs --workspace acme
node coverage.mjs --workspace acme --enumerate-only

Den Leseumfang eingrenzen

node catchup.mjs --workspace acme --all              # jede Unterhaltung
node catchup.mjs --workspace acme --since 7d         # ein Zeitfenster
node catchup.mjs --workspace acme --channels general,eng-backend
node catchup.mjs --workspace acme --dms-only
node catchup.mjs --workspace acme --all --full       # erster Archivierungsdurchlauf

Die Schichten

Schicht A – discover.mjs scannt den gesamten Workspace und gibt eine Zeile pro Unterhaltung aus, niemals Nachrichtentexte, sodass ein Modell die Form des Workspaces in einem einzigen kleinen Lesevorgang lernt. Spalten: neue Anzahl, Priorität, Aktion.

Schicht B – fetch.mjs → render.mjs öffnet, was wichtig ist.

Lies eine Nutzlast mit render.mjs. Verwende niemals head, tail oder jq da – diese geben dir ein Präfix einer JSON-Datei, das kein Präfix der Unterhaltung ist, und sie lassen stillschweigend Thread-Antworten weg.

render.mjs ordnet seine Ausgabe bewusst: vorherige Zusammenfassung, dann Angepinntes (kanonisch, immer abgerufen, unabhängig vom Wasserstand), dann Lesezeichen, dann Run- Blätter und Dokumente, dann die neuen Nachrichten. Dokumente haben Vorrang vor dem Chat – die Entscheidung steht normalerweise in der Tabelle, und der Kanal besteht aus Leuten, die darauf reagieren.


Lesezustand – drei Positionen, eine Schreiberin pro

state.json, indiziert nach Kanal-ID, da diese Umbenennungen überlebt.

FeldBedeutungeinzige Schreiberin
watermarkTsGELESEN – neuester Top-Level-Zeitstempel, der einer Leserin zugestellt wurderecord.mjs --from
scannedTsTRIAGE – neuester Zeitstempel, den die Triage bewertet hatdiscover.mjs, und nur bei ruhigen Zeilen
pendingTsSLACK – was Slack bei einer Zeile hatte, die die Triage nicht klären konntediscover.mjs
readAtdass eine echte Nutzlast zugestellt wurderecord.mjs --from
readAtSourceeine Einschränkung, die diese Behauptung EINENGT--read-source

Wenn du eine Schreiberin hinzufügst: Sie bewegt scannedTs oder sie stellt einer Leserin Inhalte zu. Niemals beides, niemals keins.

references/read-state.md erklärt, warum jedes existiert und was davor ungelesen blieb. Lies es, bevor du etwas in lib/state.mjs änderst.

Threads sind, wo Nachrichten sich verstecken

Eine Antwort verschiebt nie ihr übergeordnetes Element. Ein Kanal, dessen gesamtes Live-Gespräch in einem älteren Thread stattfindet, wirkt von der oberen Ebene aus völlig ruhig. Deshalb speichert state.json pro Thread replyCount + latestReplyTs, und warum eine Zeile incremental (thread replies) anzeigen kann – die Überprüfung dieser Unterhaltung mit eigenen Augen zeigt nichts Neues; du musst den Thread öffnen.

Die Signal-Gate bestimmt PRIORITÄT, niemals, ob etwas gelesen ist

Bewertungen sortieren die Tabelle. Sie lassen niemals eine Zeile als erledigt gelten. Ein ruhiger Kanal, der endlich etwas Wichtiges sagt, darf nicht übersprungen werden, weil seine Bewertung 1 war.


Zwei Gates, die unterschiedlich fehlschlagen

Führe beide jedes Mal aus.

node audit.mjs --workspace acme                      # offline, sofort
node coverage.mjs --workspace acme --enumerate-only  # Netzwerk, Sekunden

Ein Gate, das den Datensatz durchläuft, kann nur bestätigen, dass der Datensatz in sich konsistent ist.

audit.mjs durchläuft state.json – eine Unterhaltung, die niemand jemals aufgelistet hat, ist für ihn unsichtbar. Nicht "sauber", unsichtbar. coverage.mjs --enumerate-only stellt die andere Frage gegen conversations.list mit exclude_archived: false, wo vergessene Kanäle leben.

Ein Rückstand ist kein Build-Fehler. Diese beenden sich mit einer Anzahl von Fundstellen, damit ein Skript verzweigen kann; ein geplanter Durchlauf, der rot wird, weil jemand in Slack gepostet hat, trainiert alle, Rot zu ignorieren.


Berichte

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

Abschnitte: braucht eine Antwort, hat dich gefragt, aber du hast bereits reagiert, Kontext und was diese Zusammenfassung nicht sehen konnte. --send sendet sie per DM an dich, aufgeteilt und bei langem Text unter der ersten Nachricht verknüpft.

Ein Bericht ist eine Ansicht, kein Lesevorgang. report.mjs berührt state.json niemals. Über eine Nachricht in einer Zusammenfassung informiert zu werden, ist nicht dasselbe, wie die Unterhaltung gelesen zu haben, und die Vermischung beider Dinge ist, wie ein Rückstand verschwindet, ohne dass ihn jemand abarbeitet. Der Bericht sagt dies in seiner eigenen Fußzeile.

Planung: assets/schedule-task.ps1 registriert eine geplante Windows-Aufgabe; assets/github-workflow.tmpl ist das Cloud-Äquivalent. Jeder automatisierte Durchlauf übergibt --no-record an die Triage – eine Maschine darf niemals eine Leseposition verschieben.


In Slack schreiben

send.mjs postet als du, nicht als Bot. Zwei Leitplanken, die nicht optional sind:

  1. Führe immer zuerst --dry-run aus und zeige dem Benutzer das aufgelöste Ziel und den genauen Text vor dem Senden. Sende niemals aus eigener Initiative.
  2. Slack hat kein "Rückgängig machen". Jeder Versuch wird vor dem API-Aufruf und erneut danach in sent.log.jsonl protokolliert.
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> bearbeitet eine Nachricht an Ort und Stelle und erfordert --reason, was im Protokoll landet; der Dry-Run gibt den aktuellen entfernten Text über dem Ersatz aus, damit du siehst, was du überschreibst. Ein mehrdeutiges @Name wird abgelehnt, niemals geraten.

Eine Antwort entwerfen

Lies zuerst den Thread (fetch.mjs + render.mjs), erstelle einen Entwurf in einer Datei, zeige ihn dem Benutzer, dann sende. Schreibe Entwürfe in das Workspace-Verzeichnis, niemals in das Skill-Repository.


Anhänge, Tabellen, Dokumente

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

Ein gid in einem Link ist kein Geltungsbereich.

Das #gid=-Fragment zeichnet auf, welchen Tab der Browser des Benutzers gerade anzeigte, als sie die URL kopierten. fetch-sheet.mjs liest jeden Tab und sortiert lediglich den verlinkten zuerst. Grenze mit --tab bewusst ein, niemals aus Versehen.

Sowohl download-file.mjs als auch fetch-sheet.mjs weigern sich lautstark, wenn die Antwort eine HTML-Seite statt Daten ist – Slack und Google geben beide Anmelde- und Fehlerseiten mit HTTP 200 zurück, sodass ein naiver Leser eine Anmeldeseite unter einem .pdf-Namen auf die Festplatte schreibt und es als Erfolg bezeichnet.


Archivierung

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

Markdown, nicht JSON – eine Person, die sucht, wer was zugestimmt hat, sollte nichts parsen müssen. Mechanische Nachrichten werden weggelassen und gezählt. conversations/ wird bei jedem Durchlauf gelöscht und neu aufgebaut, da eine Umbenennung oder ein neues Kalenderjahr eine verwaiste Datei zurücklässt und grep dann beide Kopien findet, ohne zu sagen, welche aktuell ist.

--skip-existing ist Wiederaufnahmefähigkeit und eine Falle bei einer veralteten Liste: jede veraltete Nutzlast ist bereits vollständig, daher macht das Flag diesen Durchlauf zu einem totalen Nullum. Verwende es nur für den "neue Unterhaltungen"-Durchlauf.


Konventionen für alles, was du hier hinzufügst

  • Stdout ist die Nutzlast; Diagnose geht nach stderr. fetch.mjs schreibt JSON nach stdout. Ein Logger, der diesen Kanal teilt, ist ein Fehler, der auf eine Auslastung wartet.
  • --out, niemals >. Eine Shell-Umleitung kürzt das Ziel bevor das Skript läuft, daher zerstört ein fehlgeschlagener Abruf die Nutzlast, die er ersetzen sollte.
  • Atomare Schreibvorgänge. Temporäre Datei, dann umbenennen. Ein halb geschriebenes Manifest behauptet, Dinge seien gelesen worden.
  • Text geht durch Dateien. --digest-file, --reason-file, --batch: PowerShell verdoppelt ein Anführungszeichen, wo bash es nicht tut, und eine Datei hat überhaupt keine Anführungszeichenregeln.
  • Lehne Mehrdeutigkeit ab. Die DM der falschen Person zu lesen, ist nicht wiederherstellbar.

Was dieser Skill nicht tut

  • Aus eigener Initiative etwas posten. Jeder Schreibvorgang wird zuerst gezeigt.
  • Eine Leseposition für Inhalte verschieben, die niemand gelesen hat.
  • Eine Unterhaltung als erledigt markieren, weil sie niedrig bewertet wurde.
  • Abdeckung der beiden obigen blinden Flecken beanspruchen.
  • Geheimnisse kopieren. Bewege niemals eine Anmeldeinformation, einen Zugangscode oder einen privaten Einladungslink aus Slack heraus.

(Übersetzt von Slack Waterline, siehe GitHub)

Verwandte Skills