| name | confluence-digest |
| description | Use when the user runs /confluence-digest (optionally with a time window `Nh`/`Nd`, z.B. 24h, 72h, 7d) to get a prioritized overview of recently changed Confluence pages (mayflowergmbh) relevant to them â pages they are mentioned on or have contributed to, plus optional topic keywords â with AI summaries; `/confluence-digest setup` runs an interview to maintain those topics/keywords. Triggers: /confluence-digest, Confluence-Ăberblick, was ist neu in Confluence, Confluence-Digest, confluence-digest setup, eigene Themen/Keywords pflegen. |
Confluence-Digest (Stufe 2a)
Priorisierter Ăberblick ĂŒber kĂŒrzlich geĂ€nderte, fĂŒr die Nutzer:in relevante Confluence-Seiten
auf mayflowergmbh. Signal âmich betreffend" (Mentions + eigene Bearbeitungen), optionale
Themen-Keywords (âDeine Themen") und optional verfolgte Personen (âVerfolgte Personen").
Datenquelle: der Atlassian-MCP-Server, dessen Name in der Config unter mcpServer steht
(Default atlassian-mayflower). Ersetze <mcpServer> in allen Tool-Namen unten durch diesen
Wert â die Tools heiĂen also mcp__<mcpServer>__searchConfluenceUsingCql,
mcp__<mcpServer>__getConfluencePage, mcp__<mcpServer>__atlassianUserInfo,
mcp__<mcpServer>__lookupJiraAccountId (im Fan-out via ToolSearch select:mcp__<mcpServer>__âŠ).
Konfiguration: config.local.yaml im Skill-Verzeichnis (nutzer-lokal, gitignored).
Design: docs/plans/2026-06-11-confluence-digest-design.md
Aufruf
/confluence-digest â Standardfenster (24h; montags 72h)
/confluence-digest <Fenster> â Override-Fenster `Nh`/`Nd` (z.B. 24h, 72h, 7d â etwa nach Urlaub)
/confluence-digest --dry-run â nur CQL + Trefferzahlen, ohne Inhalte/Zusammenfassungen
/confluence-digest setup â Onboarding-Interview: eigene Themen (Keywords) pflegen
Argument-Routing: Lautet das Argument setup, springe direkt in den Abschnitt
â## setup / Onboarding-Interview" und fĂŒhre nicht den normalen Digest-Ablauf aus.
Alle anderen Argumente (<Fenster>, --dry-run, kein Argument) laufen wie unten beschrieben.
Ablauf
1. Config laden / Onboarding
- Das Skill-Verzeichnis wird ĂŒber einen Symlink erreicht
(
~/.claude/skills/confluence-digest â echtes Repo). Lies und schreibe config.local.yaml
neben SKILL.md im aufgelösten echten Verzeichnis (z.B. via realpath), damit die Datei
im gitignored Repo landet und nicht im Symlink-Pfad.
- PrĂŒfe, ob
config.local.yaml dort existiert.
- Fehlt sie (NEUE Nutzer:in): BegrĂŒĂe kurz, erzeuge
config.local.yaml aus
config.example.yaml. FĂŒhre dann das volle Onboarding-Interview aus dem Abschnitt
â## setup / Onboarding-Interview" durch (IdentitĂ€tsbestĂ€tigung via atlassianUserInfo
inkl. RĂŒckschreiben von accountId: <account_id>, der Keyword-Schritt und der
Personen-Schritt). Das ersetzt das frĂŒhere Mini-Onboarding der Stufe 1. Im Anschluss bietet
das Interview an, direkt einen Digest zu laufen.
- Existiert sie: lade Werte. Steht
accountId: auto, einmalig via atlassianUserInfo
auflösen und zurĂŒckschreiben. Fehlt der SchlĂŒssel onboarding (Bestandsconfig aus Stufe 1),
behandle onboarding.hintShown als false. Fehlt der SchlĂŒssel mcpServer (Bestandsconfig vor
Stufe 2a), nutze den Default atlassian-mayflower.
- Der aufgelöste
accountId wird fĂŒr spĂ€tere Stufen gespeichert; Stufe 1 nutzt in der CQL
currentUser() und verbraucht ihn nicht.
- SchlÀgt
atlassianUserInfo fehl â siehe Abschnitt Fehlerbehandlung.
2. Zeitfenster bestimmen
- Lies das aktuelle Datum aus dem Harness-Kontext (Feld âToday's date").
- Optionales Argument hat Vorrang, Form
Nh/Nd (Beispiele 24h, 72h, 7d):
Nh â now("-Nh"); Nd â now("-Nd").
- Kein Argument: ist heute Montag â
now("-72h") (FrâMo), sonst â now("-24h").
- Merke dir das gewĂ€hlte Fenster als Label fĂŒr die Ausgabe. Labeling-Regel:
- Standard 24h â âletzte 24h"; Montags-Standard 72h â âFrâMo".
- Sonst ohne Spezial-Label: bei
Nh â âletzte N Stunden", bei Nd â âletzte N Tage".
- FenstergröĂe in Stunden bestimmen (fĂŒr die Modus-Wahl):
Standard 24h â
24; Montags-Default â 72; Override Nh â N; Nd â N·24.
- AusfĂŒhrungsmodus ableiten:
- > 72h â Fan-out-Modus (Such-Queries und Summaries laufen ĂŒber Subagenten, siehe §3/§7).
- †72h â Inline-Modus (alles direkt im Haupt-Agenten, wie bisher).
--dry-run ist IMMER Inline-Modus (braucht nur CQL + totalSize, kein Fan-out, keine Inhalte) â
unabhĂ€ngig von der FenstergröĂe.
- Fallback: Steht die Agent/Task-FĂ€higkeit nicht zur VerfĂŒgung, fĂŒhre auch > 72h inline aus.
- Bei
--dry-run: Argument-Parsing identisch, nur spÀtere Schritte unterscheiden sich.
3. Relevante Seiten holen
AusfĂŒhrungsmodus (aus §2; die konkreten CQL-Strings je Signal weiter unten bleiben in beiden
Modi identisch â nur die AusfĂŒhrung unterscheidet sich):
-
Inline-Modus (†72h): FĂŒhre die CQL-Abfragen je Signal direkt via
mcp__<mcpServer>__searchConfluenceUsingCql aus (dĂŒrfen in einem Turn parallel abgesetzt
werden) â wie bisher. Lies die Felder selbst nach der Feld-Mapping-Tabelle weiter unten.
-
Fan-out-Modus (> 72h): FĂŒr jede aktive Signal-Query (mentions, ownEdits, je
signals.keywords-Eintrag, je signals.titleKeywords-Eintrag, je signals.people-Eintrag)
starte genau einen Subagenten via dem Agent/Task-Tool (subagent_type: general-purpose). Der
Subagent:
- lÀdt das MCP-Tool via
ToolSearch (select:mcp__<mcpServer>__searchConfluenceUsingCql),
- fĂŒhrt exakt dieselbe CQL fĂŒr genau dieses eine Signal aus (cloudId aus Config,
limit (= 50),
Fenster aus Schritt 2 â die CQL-Strings weiter unten gelten unverĂ€ndert),
- mappt die Felder selbst nach der Feld-Mapping-Tabelle weiter unten und gibt ausschlieĂlich das
unten definierte kompakte RĂŒckgabe-Schema zurĂŒck â niemals rohes JSON.
Die Subagenten der einzelnen Signale sind unabhÀngig und sollten parallel gestartet werden.
Merge/Dedupe (§4) und Ranking (§5) laufen danach unverÀndert im Haupt-Agenten auf diesen
kompakten RĂŒckgaben (SchlĂŒssel = id; Signale aggregieren; Scores wie gehabt
Mention = 4 / ownEdit = 2 / keyword = 1 / person = 1).
Fixes RĂŒckgabe-Schema des Such-Subagenten (Fan-out-Modus):
signal: <mentions | ownEdits | keyword:<kw> | titleKeyword:<kw> | person:<name>>
totalSize: <Gesamtzahl>
id | title | space | author | friendlyLastModified | url(absolut) | type
<id> | <Titel> | <Space> | <Autor> | <relatives Datum> | <absolute URL> | <page|blogpost>
... (eine Zeile je Treffer)
signal benennt das auslösende Signal eindeutig: mentions, ownEdits, keyword:<kw> (fĂŒr
signals.keywords-EintrĂ€ge), titleKeyword:<kw> (fĂŒr signals.titleKeywords-EintrĂ€ge) oder
person:<name> (Config-name, nicht die id).
- Die Spalten je Treffer entsprechen der Feld-Mapping-Tabelle weiter unten (rohes oder
vereinfachtes Antwort-Format â die Tabelle deckt beide Pfade ab; Beispiele unten = rohes Format):
space = resultGlobalContainer.title, author = content.history.createdBy.displayName,
url(absolut) = _links.base + <item>.url, type = page|blogpost. Der Subagent wendet dieses
Mapping selbst an und gibt nur die fertigen Spaltenwerte zurĂŒck.
- Fehler im Fan-out: SchlÀgt die Query eines Subagenten fehl oder liefert sie leer, meldet der
Subagent das kompakt (z.B.
totalSize: 0 bzw. eine kurze Fehlernotiz statt Trefferzeilen); der
Haupt-Agent verarbeitet die ĂŒbrigen Subagenten-RĂŒckgaben normal weiter (analog der bestehenden
âeinzelne CQL-Abfrage fehlgeschlagen"-Regel in der Fehlerbehandlung).
CQL-Definitionen je Signal (das WAS â beide Modi nutzen exakt diese Strings; im Fan-out fĂŒhrt
sie der jeweilige Subagent aus, im Inline-Modus der Haupt-Agent direkt. Nicht zusÀtzlich zentral
ausfĂŒhren.) Jeweils via mcp__<mcpServer>__searchConfluenceUsingCql, cloudId aus Config,
limit (= 50), Fenster aus Schritt 2:
- mentions:
mention = currentUser() AND type in (page, blogpost) AND lastmodified >= now("<FENSTER>") ORDER BY lastmodified DESC
- ownEdits:
contributor = currentUser() AND type in (page, blogpost) AND lastmodified >= now("<FENSTER>") ORDER BY lastmodified DESC
(Nur Signale ausfĂŒhren, die in der Config true sind.)
Alle Abfragen nutzen type in (page, blogpost) â so werden Seiten und Blogposts erfasst.
Blogposts sind ein eigener Confluence-Inhaltstyp (unabhÀngig von Labels/Tags); ohne blogpost im
Typ-Filter blieben sie sonst ĂŒber alle Signale unsichtbar.
Keyword-Signal (Stufe 1.5): Es gibt zwei Keyword-Listen, beide erzeugen dasselbe Signal
keyword (Gruppe âDeine Themen"); sie unterscheiden sich nur im CQL-Match:
signals.keywords (Volltext, auch im Body): pro Eintrag
text ~ "<kw>" AND type in (page, blogpost) AND lastmodified >= now("<FENSTER>") ORDER BY lastmodified DESC
signals.titleKeywords (nur Titel â schmal, gegen Footer-/Adress-Rauschen): pro Eintrag
title ~ "<kw>" AND type in (page, blogpost) AND lastmodified >= now("<FENSTER>") ORDER BY lastmodified DESC
Beide jeweils limit (= 50), gleiches Fenster aus Schritt 2; expand nicht nötig. FĂŒhre pro
Eintrag in beiden nicht-leeren Listen je eine Abfrage aus. Sind beide Listen leer, entfÀllt
dieser Schritt. (Tipp: breite Begriffe, die als Ortsname/Adresse ĂŒberall im Body vorkommen â
z.B. der eigene Standort â gehören in titleKeywords, nicht in keywords.)
Personen-Signal (Stufe 2a): Ist signals.people nicht leer, fĂŒhre pro Person eine eigene
Abfrage aus (eigene Query pro Person â Attribution âvon wem"):
- pro Eintrag mit aufgelöstem
id:
contributor = "<id>" AND type in (page, blogpost) AND lastmodified >= now("<FENSTER>") ORDER BY lastmodified DESC
Jeweils limit (= 50), gleiches Fenster aus Schritt 2. Ist die Liste leer, entfÀllt dieser Schritt.
Namen vs. IDs (Invariante): CQL-Abfragen nutzen immer die id (accountId); jede Anzeige
((von <name>), dry-run, Hinweise) nutzt den Config-name.
Runtime-Auflösung fehlender IDs (vor den Abfragen): Hat ein people-Eintrag einen name, aber
keine/leere id, löse den Namen einmalig via mcp__<mcpServer>__lookupJiraAccountId
(cloudId aus Config, searchString = <name>) auf:
- Genau ein Treffer in
data.users.users[] â ergĂ€nze dessen accountId als id im
bestehenden Eintrag (Name bleibt erhalten â Eintrag ist danach {name, id}) und schreibe das
nach config.local.yaml zurĂŒck (Wiederverwendung bei kĂŒnftigen LĂ€ufen).
- Mehrere Treffer (mehrdeutig) oder kein Treffer â diese Person fĂŒr diesen Lauf
ĂŒberspringen und einen kurzen Hinweis vermerken (âKonnte â' nicht eindeutig auflösen â
per
setup bestÀtigen."). Die nicht-interaktive Auflösung rÀt also nicht bei Mehrdeutigkeit;
die eindeutige Zuordnung erfolgt im setup-Interview.
Ausschluss-Filter (exclude): An jede Signal-CQL aus dem vorigen Block (mentions,
ownEdits, je Keyword, je titleKeyword, je Person) werden vor ORDER BY lastmodified DESC zusĂ€tzlich die folgenden Klauseln angehĂ€ngt â die unverĂ€nderten Signal-CQLs oben bleiben
also gleich, sie werden nur ergĂ€nzt (NICHT neu geschrieben). Das gilt in beiden AusfĂŒhrungsmodi
(Inline wie Fan-out â die CQL-Strings sind dort ohnehin identisch) und fĂŒr alle Signale:
- Immer (Default, unabhÀngig von der Config):
AND title !~ "Kopie von" AND title !~ "Copy of" â schlieĂt Seiten-Kopien aus.
- Wenn
exclude.spaces nicht leer: AND space not in ("<k1>","<k2>", âŠ) (Space-Keys).
- Je Eintrag in
exclude.titlePatterns: AND title !~ "<muster>".
Beispiel einer fertig zusammengesetzten Query (mentions, mit exclude.spaces: [hoth, OBKS] und
exclude.titlePatterns: ["Jour Fixe"]):
mention = currentUser() AND type in (page, blogpost) AND lastmodified >= now("<FENSTER>") AND title !~ "Kopie von" AND title !~ "Copy of" AND space not in ("hoth","OBKS") AND title !~ "Jour Fixe" ORDER BY lastmodified DESC
Caveats:
title !~ "<muster>" matcht den Substring â ein Muster kann ĂŒber-matchen (z.B. "Sync"
trifft auch âAsync"). Muster bewusst eng wĂ€hlen.
space not in (âŠ) ist grob: es verwirft alles in diesen Spaces, auch ggf. relevante
Seiten. Nur fĂŒr Spaces nutzen, die durchgĂ€ngig Rauschen sind.
(Die Vorschlags-Queries im setup-Interview sind hiervon nicht betroffen â sie bleiben ohne
Ausschluss-Klauseln.)
Antwort-Format (wichtig â nach Bedeutung mappen, nicht auf einen festen Pfad verlassen):
searchConfluenceUsingCql liefert je nach Fall eine von zwei Formen. Lies die Felder nach
folgender Bedeutung, und nutze pro Feld den ersten vorhandenen Pfad:
| Bedeutung | Pfad (rohes Search-Format) | Pfad (vereinfachtes Format) |
|---|
| Trefferliste | results[] | content.nodes[] |
| Gesamtzahl | totalSize | content.totalCount |
| Seiten-ID | <item>.content.id | <item>.id |
| Titel | <item>.title | <item>.title |
| Space-Name | <item>.resultGlobalContainer.title | <item>.space.name |
| Autor/letzte:r Bearbeiter:in | <item>.content.history.createdBy.displayName | <item>.author.displayName |
| relatives Datum | <item>.friendlyLastModified | <item>.lastModified |
| URL | _links.base + <item>.url (relativ â absolut) | <item>.webUrl |
Die absolute URL im rohen Format = _links.base (z.B. https://mayflowergmbh.atlassian.net/wiki)
4. Mergen & dedupen
- Sammle alle Treffer aller Abfragen (mentions, ownEdits, je Keyword, je Person). SchlĂŒssel = Seiten-ID.
Im Fan-out-Modus kann dieselbe Seite in mehreren Subagenten-RĂŒckgaben auftauchen (je
auslösendem Signal eine) â ĂŒber
id zusammenfĂŒhren und die Signale ĂŒber alle RĂŒckgaben hinweg
vereinigen.
- Pro Seite merke die Menge der Treffer-Signale (eine Seite kann mehrere Signale tragen, z.B.
mention UND ownEdit UND keyword UND person).
- Keyword-Treffer mit dem Signal
keyword vermerken plus, welches Keyword sie ausgelöst hat
(aus signals.keywords ODER signals.titleKeywords; eine Seite kann von mehreren Keywords
getroffen werden â alle merken).
- Personen-Treffer mit dem Signal
person vermerken plus, welche Person (Name) sie bearbeitet
hat. Mehrere verfolgte Personen können dieselbe Seite bearbeitet haben â alle Namen merken.
(Ist eine verfolgte Person zufÀllig man selbst, landet die Seite via PrioritÀt ohnehin unter
âVon dir mitbearbeitet" â kein Sonderfall nötig.)
- Merke je Query die Gesamtzahl (
totalSize bzw. content.totalCount); ist sie > limit,
fĂŒr die Volumen-Notiz vormerken (N = Gesamtzahl - limit, also Gesamtzahl - 50). Das gilt
je mention-/ownEdit-Query, je Query pro Keyword (aus beiden Listen; Gesamtzahl pro
Keyword separat fĂŒhren) und je Query pro Person (Gesamtzahl pro Person separat fĂŒhren).
5. Ranken
Score je Seite: Mention = 4, ownEdit = 2, keyword = 1, person = 1, summiert ĂŒber die
Treffer-Signale. Mehrere Keyword-Treffer auf derselben Seite zÀhlen weiterhin als ein
keyword-Beitrag (= 1); ebenso zÀhlen mehrere verfolgte Personen auf derselben Seite als ein
person-Beitrag (= 1).
Beispiele:
- Mention + ownEdit = 6
- nur Mention = 4
- ownEdit + keyword = 3
- ownEdit + person = 3
- keyword + person = 2
- nur ownEdit = 2
- nur keyword = 1
- nur person = 1
SekundÀrsortierung: AktualitÀt (Reihenfolge aus ORDER BY lastmodified DESC).
6. Highlights & Gruppen aufteilen
- Highlights:
H = min(limits.highlights (Default 5), Anzahl gerankter Seiten) â die
Top-Seiten nach Score/AktualitĂ€t. Diese bekommen in §7 eine ausfĂŒhrliche Summary (2â4 SĂ€tze).
- Gruppen: Alle ĂŒbrigen gerankten Seiten (Rang > H) werden nach §8 in ihre Gruppe einsortiert
(höchstpriores Signal). Pro Gruppe bekommen die ersten
limits.groupSummaries (Default 8)
EintrĂ€ge (nach Ranking/AktualitĂ€t) in §7 eine kĂŒrzere Summary (2â3 SĂ€tze); weitere EintrĂ€ge
derselben Gruppe nur Titel + Link + Datum (siehe §8).
- Es werden also nur die Seiten via
getConfluencePage geholt, die auch eine Summary bekommen
(Highlights + die je Gruppe bis zur Obergrenze) â keine ĂŒberflĂŒssigen Fetches.
7. Zusammenfassen
Zusammengefasst werden (a) die H Highlights und (b) je Gruppe die ersten
limits.groupSummaries EintrĂ€ge (siehe §6 â diese Auswahl ist in beiden Modi gleich).
AusfĂŒhrungsmodus (aus §2):
- Inline-Modus (†72h): Hole fĂŒr jede zu summende Seite zentral
mcp__<mcpServer>__getConfluencePage (per id) und fasse sie selbst zusammen â wie bisher.
- Fan-out-Modus (> 72h): Jede zu summende Seite (die H Highlights + je Gruppe die bis
limits.groupSummaries gedeckelten EintrÀge) wird von genau einem Subagenten (Agent/Task-Tool,
subagent_type: general-purpose) erledigt. Der Subagent: lÀdt getConfluencePage via ToolSearch
(select:mcp__<mcpServer>__getConfluencePage), holt die Seite per id (markdown) und gibt
ausschlieĂlich die fertige Zusammenfassung zurĂŒck (Highlight 2â4 SĂ€tze, Gruppen-Eintrag 2â3
SĂ€tze) â nie den Roh-Body. SchlĂ€gt der Fetch fehl (Rechte/gelöscht), gibt der Subagent statt der
Summary die Notiz âInhalt nicht abrufbar" zurĂŒck. Die Subagenten sind unabhĂ€ngig und sollten parallel
gestartet werden. Der Haupt-Agent rendert anschlieĂend (§8) unverĂ€ndert.
In beiden Modi gilt pro Seite:
- Highlights: 2â4 SĂ€tze (ausfĂŒhrlich) â worum geht es / was ist der aktuelle Stand.
- Gruppen-EintrĂ€ge (bis zur Obergrenze): 2â3 SĂ€tze (kĂŒrzer) â worum geht es / was ist neu.
Kein echter Versions-Diff verfĂŒgbar â Stand beschreiben, nicht âdiese Zeilen kamen dazu".
SchlĂ€gt der Fetch fehl (Rechte/gelöscht) â Eintrag ohne Summary, Notiz âInhalt nicht abrufbar".
EintrÀge jenseits der Gruppen-Obergrenze werden NICHT geholt (nur Titel + Link + Datum).
ErgĂ€nze je Seite die âWarum fĂŒr dich"-BegrĂŒndung aus den Treffer-Signalen
(Mention â âDu wirst erwĂ€hnt", ownEdit â âVon dir mitbearbeitet",
keyword â âThema â'" â bei mehreren getroffenen Keywords diese aufzĂ€hlen,
person â âVon bearbeitet" â bei mehreren verfolgten Personen diese aufzĂ€hlen). In den
Gruppen genĂŒgt das jeweilige Gruppensignal (bei âDeine Themen" das/die Keyword(s) angeben, bei
âVerfolgte Personen" den/die Namen).
Hinweis zur Personen-Attribution: âVon bearbeitet" meint die verfolgte Person als
Contributor (contributor-Treffer). Das ist bewusst etwas anderes als das Render-Feld
âzuletzt von <author.displayName>" (= Seiten-Ersteller:in laut createdBy). Beide Namen dĂŒrfen
also abweichen â nicht versuchen, sie anzugleichen.
8. Rendern (Markdown im Chat)
# Confluence-Digest · <Fenster-Label>
<X relevante Seiten>
## â Highlights
### <Titel> · <space.name> · zuletzt von <author.displayName>, <lastModified>
<2â4 SĂ€tze Zusammenfassung>
**Warum fĂŒr dich:** <BegrĂŒndung>
đ <webUrl>
## Nach Signal gruppiert
### đ Dich betreffend (Mentions)
- **<Titel>** · <space.name> · <lastModified>
<2â3 SĂ€tze Summary>
đ <webUrl>
### âïž Von dir mitbearbeitet
- ...
### đ·ïž Deine Themen
- **<Titel>** · <space.name> · <lastModified> *(â<kw>')*
<2â3 SĂ€tze Summary>
đ <webUrl>
### đ„ Verfolgte Personen
- **<Titel>** · <space.name> · <lastModified> *(von <name>)*
<2â3 SĂ€tze Summary>
đ <webUrl>
(EintrĂ€ge jenseits der Gruppen-Obergrenze ohne Summary: - <Titel> · <space.name> · <lastModified> đ <webUrl>.)
Regeln:
- Jede Seite NUR EINMAL: als Highlight ODER in genau einer Gruppe.
In der Gruppenliste zÀhlt nur das höchstpriore Signal einer Seite.
Gruppen-PrioritÀtsreihenfolge: Mentions > ownEdits > Themen (keyword) > Verfolgte Personen (person).
Eine Seite landet also nur dann in âđ·ïž Deine Themen", wenn ihr höchstpriores Signal ein
Keyword ist (sie also weder Mention noch ownEdit ist). Eine Seite landet nur dann in
âđ„ Verfolgte Personen", wenn ihr höchstpriores Signal
person ist (sie also weder Mention
noch ownEdit noch Keyword-Treffer ist). Eine Mention+Keyword-Seite erscheint
ausschlieĂlich unter Mentions; eine von einer verfolgten Person bearbeitete Seite, die auch
Mention ist, bleibt unter Mentions. Die vollstĂ€ndige Mehrfach-BegrĂŒndung erscheint nur bei
den Highlights.
- Pro Gruppe bekommen die ersten
limits.groupSummaries (Default 8) EintrĂ€ge eine 2â3-Satz-Summary
(Reihenfolge nach Ranking/AktualitÀt); weitere EintrÀge derselben Gruppe nur Titel + Link + Datum.
Sind in einer Gruppe EintrĂ€ge ohne Summary ĂŒbrig, hĂ€nge ans Gruppenende
â(+N weitere ohne Zusammenfassung)" an.
- Leere Gruppen weglassen. Gar nichts â âđą Nichts Neues im Zeitraum ()."
- Volumen-Notiz anhÀngen, wo die Gesamtzahl
> limit: â(+N weitere â Fenster ggf. zu groĂ)".
Das gilt auch je Keyword-Query (z.B. â(+N weitere zu â' â Fenster ggf. zu groĂ)").
- Setup-Hinweis (Stufe 1.5): Sind beide Listen
signals.keywords UND
signals.titleKeywords leer UND onboarding.hintShown
false (ein fehlender onboarding-SchlĂŒssel gilt als false), hĂ€nge einmalig am Ende
des Digests eine dezente Zeile an:
âđĄ Tipp: /confluence-digest setup, um eigene Themen hinzuzufĂŒgen."
Setze danach onboarding.hintShown: true in config.local.yaml (SchlĂŒssel anlegen, falls er
fehlt), damit der Hinweis bei kĂŒnftigen LĂ€ufen nicht erneut erscheint. Sind in einer der beiden
Listen Keywords gesetzt, entfÀllt der Hinweis. Wurde in DIESEM Lauf das Interview durchlaufen oder wurden
gerade Keywords gesetzt, gilt onboarding.hintShown bereits als true â der Hinweis entfĂ€llt.
Fehlerbehandlung
- MCP-Server (
mcpServer, Default atlassian-mayflower) nicht verbunden / Auth-Fehler â klare Meldung:
âBitte /mcp öffnen und den Atlassian-MCP-Server (<mcpServer>) authentifizieren." â Abbruch.
atlassianUserInfo ohne account_id â âmich betreffend"-Signale ĂŒberspringen, Hinweis ausgeben.
- Einzelne CQL-Abfrage schlĂ€gt fehl â ĂŒberspringen, Hinweis, restliche Signale weiterverarbeiten.
--dry-run
--dry-run lĂ€uft immer inline (auch bei Fenstern > 72h) â kein Subagenten-Fan-out: Es braucht
nur das CQL je Signal und die Gesamtzahl (totalSize), aber keine Seiteninhalte.
Schritte 1â6 normal, aber statt Schritt 7/8: gib pro Signal das CQL und die Gesamtzahl aus,
plus die gerankte Trefferliste (Titel + Signale), ohne Seiten zu holen oder zusammenzufassen.
Bei nicht-leerer signals.keywords: je Eintrag das text ~-CQL plus Gesamtzahl ausgeben;
bei nicht-leerer signals.titleKeywords: je Eintrag das title ~-CQL plus Gesamtzahl.
Bei nicht-leerer signals.people: je Person das contributor = "<id>"-CQL plus Gesamtzahl
ausgeben (Name + id).
setup / Onboarding-Interview
Wird ausgelöst durch /confluence-digest setup oder beim Erststart einer neuen Nutzer:in
(§1, fehlende config.local.yaml). Ziel: die Keyword-Listen signals.keywords (Volltext) und
signals.titleKeywords (nur Titel) sowie die verfolgten Personen signals.people
({name, id}) in config.local.yaml pflegen.
Das Interview deckt also Keywords UND Personen ab (+ IdentitÀtsbestÀtigung).
1. IdentitÀt bestÀtigen. Rufe mcp__<mcpServer>__atlassianUserInfo auf, lies
account_id + name. Schreibe accountId: <account_id> in config.local.yaml (ersetzt auto)
und bestĂ€tige: âEingerichtet als ." SchlĂ€gt der Aufruf fehl â siehe Fehlerbehandlung.
2. VorschlĂ€ge sammeln. FĂŒhre zwei Suchen via searchConfluenceUsingCql aus â Fenster fest
now("-90d"), limit (= 50), jeweils mit expand: "content.metadata.labels":
mention = currentUser() AND type in (page, blogpost) AND lastmodified >= now("-90d") ORDER BY lastmodified DESC
contributor = currentUser() AND type in (page, blogpost) AND lastmodified >= now("-90d") ORDER BY lastmodified DESC
Aus den Treffern beider Abfragen Kandidaten ableiten:
- Labels: alle
content.metadata.labels.results[].name einsammeln (Labels sind oft
sparse/leer â das ist ok, sie sind nur ein Teil der Quelle).
- Titel-Terme: Tokenisiere die Titel an Whitespace/Satzzeichen. Verwirf: Tokens < 3 Zeichen,
reine Zahlen, Jahreszahlen (19xx/20xx), gÀngige de/en-Stoppwörter sowie generische
Confluence-Begriffe (z.B. Meeting, Notes, Protokoll, Doku, Seite, Page, Ăbersicht). ZĂ€hle die
verbleibenden Tokens case-insensitiv. Bilde KEINE Mehrwort-Phrasen (nur Einzeltoken).
- Kandidatenliste = zuerst Labels (nach HĂ€ufigkeit), dann Titel-Terme (nach HĂ€ufigkeit); bei
Gleichstand alphabetisch. Nimm nach case-insensitivem Dedup die ersten ~10.
3. Dialog. Zeige die Kandidaten nummeriert. Die Nutzer:in kann frei auswÀhlen, einzelne
streichen und beliebige eigene Keywords ergÀnzen. Es gibt kein Minimum/Maximum; eine leere
Auswahl ist erlaubt. Weise kurz auf die zwei Match-Arten hin und lass pro Keyword wÀhlen:
Volltext (Standard, findet das Wort auch im Body) â signals.keywords; nur Titel
(schmal, gut fĂŒr breite Orts-/Adressbegriffe wie den eigenen Standort) â signals.titleKeywords.
Im Zweifel Volltext.
4. Personen-Schritt. Frage, welche Kolleg:innen verfolgt werden sollen (frei, optional;
eine leere Auswahl ist erlaubt â dann bleibt signals.people leer).
- Optionaler Vorschlag: Leite aus der AktivitÀts-Analyse von Schritt 2 (die mention-/ownEdit-
Treffer wurden dort bereits geholt) hĂ€ufige Mit-Autor:innen ab: zĂ€hle ĂŒber alle Treffer die
content.history.createdBy.displayName (rohes Format) bzw. author.displayName (vereinfachtes
Format), ohne dich selbst (eigener name/accountId), und biete die hÀufigsten als
Kandidat:innen an. (Diese Quelle ist sparse/optional â ist nichts ableitbar, einfach frei fragen.)
- Pro genannter Person rufe
mcp__<mcpServer>__lookupJiraAccountId auf (cloudId aus Config,
searchString = <name>). Bei genau einem Treffer in data.users.users[] direkt ĂŒbernehmen;
bei mehreren Treffern die Auswahl (mit displayName/E-Mail) bestÀtigen lassen; bei keinem
Treffer Hinweis und Person ĂŒberspringen. Sammle die bestĂ€tigten Personen als {name, id}
(id = accountId).
5. Ergebnis speichern. Schreibe die gewÀhlten Keywords je nach Match-Art nach
signals.keywords (Volltext) bzw. signals.titleKeywords (nur Titel) und die gewÀhlten Personen
als {name, id}-EintrÀge nach signals.people in config.local.yaml
und setze onboarding.hintShown: true (SchlĂŒssel onboarding anlegen, falls er fehlt). Damit
erscheint der Setup-Hinweis aus §8 kĂŒnftig nicht mehr.
6. Direkt loslaufen. Biete an, sofort einen Digest zu laufen
(normaler Ablauf ab §2 mit Standardfenster). Bei Zustimmung ausfĂŒhren, sonst beenden.
Der unmittelbar danach angebotene Digest-Lauf verwendet die soeben geschriebenen Config-Werte
(Keywords in signals.keywords/signals.titleKeywords, Personen in signals.people
onboarding.hintShown: true).