CommunityRedacción y edicióngithub.com

Slack waterline

Lee cada mensaje en un espacio de trabajo de Slack y demuestra que no se ha perdido nada: una skill de Claude Code que te pone al día, te envía un resumen diario/semanal por DM y redacta la respuesta. Cero dependencias, muchos espacios de trabajo desde una sola instalación.

¿Qué es Slack waterline?

Slack waterline es una skill de Claude Code que garantiza que nunca se pierda ningún mensaje en un espacio de trabajo de Slack, ya sea en canales públicos, privados o DMs. Funciona como un archivista digital que lee cada conversación como lo haría una persona con tiempo ilimitado: respeta el estado de lectura (watermark) para no volver a procesar lo ya visto y genera resúmenes diarios o semanales por DM. Está diseñada para ser utilizada con Claude Code y soporta múltiples espacios de trabajo desde una sola instalación, almacenando los tokens de forma segura en archivos `.env` separados. La skill prioriza la seguridad: nunca expone tokens en terminales o transcripciones, y exige confirmación humana antes de enviar cualquier mensaje. Incluye herramientas para auditoría y cobertura que verifican que no haya conversaciones olvidadas, gestiona archivos adjuntos y hojas de Google, y redacta respuestas previa revisión del usuario.

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

Installed? Explore more Redacción y edición skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace Slack waterline?

Lee un espacio de trabajo de Slack como lo haría una persona si tuviera tiempo: cada canal, cada DM, cada respuesta en hilos, los mensajes fijados, los archivos adjuntos y las hojas de Google que la gente sigue enlazando. Luego registra exactamente lo que se leyó — y solo lo que se leyó — para que la siguiente ejecución continúe desde donde se detuvo esta.

La línea de flotación es la línea por debajo de la cual nada puede perderse. Todo en esta skill existe para mantener esa línea honesta.

La regla única

Un comprobante de lectura solo puede describir lo que fue ENTREGADO, nunca lo que EXISTE.

Obtener no es leer. Si se construyó una carga útil pero nadie la miró, no está leída, y registrarla como leída no pierde un mensaje temporalmente — lo pierde permanentemente, porque la siguiente ejecución no tiene nada más que encontrar.

Todo lo que sigue se deriva de eso.


Configuración desde cero

Si algo no está configurado — sin espacio de trabajo, sin token, sin ámbitos — ejecute primero el doctor y siga lo que diga. No adivine el estado.

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

Verifica Node, el espacio de trabajo, el token, cada ámbito y si se ha leído algo, luego se detiene en el primer bloqueo real e imprime el comando o clic exacto que lo soluciona. Ejecútelo de nuevo después de cada arreglo. Guía completa en references/setup.md.

La secuencia normal para un espacio de trabajo nuevo:

node init.mjs  --workspace acme --dir /path/to/acme-slack-archive
node setup.mjs --workspace acme --manifest --out slack-app.yaml
# el usuario crea la aplicación desde ese manifiesto y hace clic en Copiar en el token
node setup.mjs --workspace acme --token-from-clipboard
node setup.mjs --workspace acme       # confirmar; luego descubrir + ponerse al día

El manifiesto importa: establece los doce ámbitos en un solo pegado en lugar de que el usuario busque casillas de verificación, y los coloca bajo Ámbitos de Token de Usuario, que es el error que de otro modo produce un token que no puede ver ni un solo DM.

Una skill, muchos espacios de trabajo. El registro en ~/.slack-waterline/config.json solo contiene rutas; los tokens viven en el .env de cada espacio de trabajo. node init.mjs --list muestra lo que está registrado.

Nunca maneje el token usted mismo

Un token activo nunca debe ingresar a un terminal, un historial de shell o una transcripción de chat.

Solo tiene que viajar desde la página de Slack a un archivo, así que muévalo mediante el portapapeles:

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

Eso lo valida, lo escribe, lo verifica contra Slack e imprime solo a qué equipo y usuario pertenece. Nunca acepte ni pase un token como argumento de línea de comandos, nunca le pida al usuario que lo pegue en el chat y nunca lo escriba usted mismo en un archivo.

Navegación en el navegador para la configuración

Cuando el usuario pida que lo guíen en Chrome, references/setup.md tiene los pasos con las llamadas de herramienta exactas. La forma:

  1. Genere el manifiesto primero, para tener el YAML antes de necesitarlo.
  2. Cargue las herramientas del navegador en una llamada ToolSearch, luego tabs_context_mcp antes de crear una pestaña.
  3. Navegue: crear aplicación desde el manifiesto → instalar → llegar a OAuth y Permisos.
  4. Lea la lista de ámbitos al usuario antes de hacer clic en Permitir. Es su espacio de trabajo y su consentimiento.
  5. Deténgase en el token. Pídales que hagan clic en Copiar, luego ejecute setup.mjs --token-from-clipboard.

No lea el token de la página — no use read_page, get_page_text, find, javascript_tool ni capturas de pantalla dirigidas a ese campo, y no haga clic en Mostrar para revelarlo. Slack lo oculta por defecto; capturarlo escribiría una credencial activa en una transcripción que sobrevive a la sesión y es mucho más difícil de revocar que de filtrar. El paso 5 existe para que nunca tenga que hacerlo.

Nunca toque el Secreto del Cliente, el Secreto de Firma ni los Tokens a Nivel de Aplicación — esta skill no necesita ninguno de ellos. Si el usuario ha cerrado sesión, pídale que inicie sesión; nunca maneje un formulario de inicio de sesión.

Tokens

SLACK_USER_TOKEN (xoxp-)SLACK_BOT_TOKEN (xoxb-)
Canales públicostodo lo que puedas ver, sin unirtesolo aquellos a los que se unió
Canales privadostodos en los que estéssolo donde /invite
DMs / DMs grupalestodos los tuyosnunca
Hilos, fijados, marcadores, archivossísí

El token de usuario es preferido siempre que esté presente — para leer es un superconjunto estricto. Un token de bot no puede ver ni un solo DM, por lo que para una herramienta de puesta al día personal es casi inútil por sí solo.

Ámbitos del token de usuario: channels:history channels:read groups:history groups:read im:history im:read mpim:history mpim:read users:read files:read pins:read bookmarks:read. Agregue chat:write solo para el DM de resumen y el envío de respuestas.

Consulte references/tokens-and-scopes.md para generar uno.

Dos puntos ciegos que no se pueden solucionar

Indíquelos claramente en lugar de prometer una cobertura que no existe:

  1. DMs entre otras dos personas. Necesita la API de Discovery de Slack — solo Enterprise Grid.
  2. Un canal privado en el que nadie con un token está. Un humano tiene que hacer /invite. conversations.join rechaza los canales privados de plano.

Poniéndose al día — la ruta normal

node catchup.mjs --workspace acme --unread

Eso ejecuta el triaje, obtiene todo lo procesable, renderiza cada conversación e imprime un índice de los deltas renderizados más un lote de registros preparado.

Luego los lee. catchup.mjs deliberadamente no registra nada.

  1. Lea cada .md renderizado listado en el índice. Para un gran backlog, distribuya subagentes en lotes — cada uno informa solo hallazgos y nunca lleva texto de mensaje sin procesar de vuelta al contexto principal.
  2. Elimine de tmp/record-batch.json cualquier entrada cuyo delta no haya abierto.
  3. Registre lo que leyó, con un resumen mientras la comprensión esté fresca:
node record.mjs --workspace acme --batch tmp/record-batch.json
node record.mjs --workspace acme --from tmp/<id>.json \
  --digest "El alcance del sprint se definió el martes. Abierto: la PR de Olga necesita revisión."

El resumen es el sedimento. La marca de agua evita que la siguiente ejecución vuelva a obtener mensajes antiguos; el resumen evita que vuelva a derivar su significado.

  1. Demuestre que nada se esconde:
node audit.mjs --workspace acme
node coverage.mjs --workspace acme --enumerate-only

Limitando la lectura

node catchup.mjs --workspace acme --all              # cada conversación
node catchup.mjs --workspace acme --since 7d         # una ventana
node catchup.mjs --workspace acme --channels general,eng-backend
node catchup.mjs --workspace acme --dms-only
node catchup.mjs --workspace acme --all --full       # primera pasada de archivo

Las capas

Capa A — discover.mjs escanea todo el espacio de trabajo e imprime una fila por conversación, nunca cuerpos de mensajes, para que un modelo aprenda la forma del espacio de trabajo en una sola lectura pequeña. Columnas: conteo nuevo, prioridad, acción.

Capa B — fetch.mjs → render.mjs abre lo que importa.

Lea una carga útil con render.mjs. Nunca use head, tail o jq — esos le dan un prefijo de un archivo JSON, que no es un prefijo de la conversación, y silenciosamente eliminan las respuestas de hilos.

render.mjs ordena su salida deliberadamente: resumen anterior, luego fijados (canónico, siempre obtenido independientemente de la marca de agua), luego marcadores, luego hojas de cálculo y documentos, luego los mensajes nuevos. Los documentos superan al chat — la decisión suele estar en la hoja de cálculo, y el canal son personas reaccionando a ella.


Estado de lectura — tres posiciones, un escritor cada una

state.json, indexado por ID de canal porque eso sobrevive a los cambios de nombre.

camposignificaúnico escritor
watermarkTsLEÍDO — el ts de nivel superior más nuevo entregado a un lectorrecord.mjs --from
scannedTsTRIAGE — el ts más nuevo que el triaje puntuódiscover.mjs, y solo en filas tranquilas
pendingTsSLACK — lo que Slack tenía en una fila que el triaje no pudo limpiardiscover.mjs
readAtque se entregó una carga útil realrecord.mjs --from
readAtSourceuna salvedad que LIMITA esa afirmación--read-source

Si agrega un escritor: mueve scannedTs, o entrega contenido a un lector. Nunca ambos, nunca ninguno.

references/read-state.md explica por qué existe cada uno y qué quedó sin leer antes de que existiera. Léalo antes de cambiar algo en lib/state.mjs.

Los hilos son donde se esconden los mensajes

Una respuesta nunca mueve a su padre. Un canal cuya conversación activa completa ocurre dentro de un hilo más antiguo parece completamente tranquilo desde el nivel superior. Por eso state.json almacena replyCount + latestReplyTs por hilo, y por qué una fila puede leer incremental (respuestas de hilos) — revisar esa conversación visualmente no muestra nada nuevo; tienes que abrir el hilo.

La puerta de señal decide PRIORIDAD, nunca si algo está leído

Las puntuaciones ordenan la tabla. Nunca hacen que una fila esté resuelta. Un canal tranquilo que finalmente dice algo importante no debe omitirse porque obtuvo 1.


Dos puertas que fallan de manera diferente

Ejecute ambas, cada vez.

node audit.mjs --workspace acme                      # offline, instantáneo
node coverage.mjs --workspace acme --enumerate-only  # red, segundos

Una puerta que itera el registro solo puede confirmar que el registro es autoconsistente.

audit.mjs recorre state.json — por lo tanto, una conversación que nadie enumeró nunca es invisible para él. No "limpia", invisible. coverage.mjs --enumerate-only pregunta la otra pregunta contra conversations.list con exclude_archived: false, que es donde viven los canales olvidados.

Un backlog no es un fallo de compilación. Estos salen con un conteo de hallazgos para que un script pueda bifurcar; una ejecución programada que se vuelve roja porque alguien publicó en Slack entrena a todos para ignorar el rojo.


Informes

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

Secciones: necesita respuesta, te preguntó pero ya reaccionaste, contexto y lo que este resumen no pudo ver. --send te lo envía por DM, fragmentado y enhebrado debajo del primer mensaje si es largo.

Un informe es una vista, no una lectura. report.mjs nunca toca state.json. Que se te informe de un mensaje en un resumen no es lo mismo que haber leído la conversación, y confundir ambos es cómo un backlog desaparece sin que nadie lo limpie. El informe lo dice en su propio pie de página.

Programación: assets/schedule-task.ps1 registra una Tarea Programada de Windows; assets/github-workflow.tmpl es el equivalente en la nube. Cualquier ejecución automatizada pasa --no-record al triaje — una máquina nunca debe avanzar una posición de lectura.


Escribir en Slack

send.mjs publica como tú, no como un bot. Dos barreras de seguridad que no son opcionales:

  1. Siempre --dry-run primero, y muestra al usuario el destino resuelto y el texto exacto antes de enviar. Nunca envíes por iniciativa propia.
  2. Slack no tiene desenvío. Cada intento se registra en sent.log.jsonl antes de la llamada a la API y nuevamente después.
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> edita un mensaje en su lugar y requiere --reason, que va al registro; la simulación imprime el texto remoto actual sobre el reemplazo para que veas lo que estás sobrescribiendo. Un @Name ambiguo es rechazado, nunca adivinado.

Redactando una respuesta

Lea primero el hilo (fetch.mjs + render.mjs), redacte en un archivo, muéstrelo al usuario, luego envíe. Escriba los borradores en el directorio del espacio de trabajo, nunca en el repositorio de la skill.


Archivos adjuntos, hojas, documentos

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

Un gid en un enlace no es un ámbito.

El fragmento #gid= registra qué pestaña estaba mostrando el navegador de alguien cuando copió la URL. fetch-sheet.mjs lee cada pestaña y simplemente ordena la enlazada primero. Limite con --tab deliberadamente, nunca por accidente.

Tanto download-file.mjs como fetch-sheet.mjs se niegan en voz alta cuando la respuesta es una página HTML en lugar de datos — Slack y Google devuelven páginas de inicio de sesión y error con HTTP 200, por lo que un lector ingenuo escribe una página de inicio de sesión en el disco con un nombre .pdf y lo llama éxito.


Archivado

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

Markdown, no JSON — una persona que busque quién estuvo de acuerdo con qué no debería tener que analizar nada. Los mensajes mecánicos se eliminan y se cuentan. conversations/ se borra y se reconstruye en cada ejecución, porque un cambio de nombre o un nuevo año calendario deja un archivo huérfano y grep encuentra ambas copias sin nada que indique cuál es la actual.

--skip-existing es reanudabilidad y es una trampa en una lista obsoleta: cada carga útil obsoleta ya está completa, por lo que la bandera hace que esa ejecución sea un no-ópera total. Úselo solo para la pasada de "nuevas conversaciones".


Convenciones para cualquier cosa que agregues aquí

  • Stdout es la carga útil; los diagnósticos van a stderr. fetch.mjs escribe JSON a stdout. Un registrador que comparta ese canal es un error esperando una carga.
  • --out, nunca >. Una redirección de shell trunca el destino antes de que el script se ejecute, por lo que una obtención fallida destruye la carga útil que estaba reemplazando.
  • Escrituras atómicas. Archivo temporal, luego renombrar. Un manifiesto medio escrito afirma que se leyeron cosas.
  • La prosa va a través de archivos. --digest-file, --reason-file, --batch: PowerShell duplica una comilla donde bash no, y un archivo no tiene reglas de comillas en absoluto.
  • Rechazar la ambigüedad. Leer el DM de la persona equivocada no es recuperable.

Lo que esta skill no hace

  • Publicar algo por iniciativa propia. Cada escritura se muestra primero.
  • Avanzar una posición de lectura para contenido que nadie leyó.
  • Marcar una conversación como resuelta porque obtuvo una puntuación baja.
  • Afirmar cobertura de los dos puntos ciegos anteriores.
  • Copiar secretos. Nunca mover una credencial, código de acceso o enlace de invitación privado fuera de Slack.

Skills relacionados