Communitygithub.com

skillManager

Persönliche Skill-Bibliothek: Sammeln, Organisieren und Versionieren von KI-Agent-Fähigkeiten

Was ist skillManager?

Dieses Skill bietet eine umfassende Vorlage und Referenz für die Erstellung von Feishu-Karten (Lark-Karten) im JSON-Schema 2.0. Es ist ein unverzichtbares Werkzeug für Entwickler, die KI-Agenten in die Feishu-Plattform integrieren. Der Skill definiert klare Regeln für den Aufbau interaktiver Karten, einschließlich der Struktur von Headern, bunten Komponenten, Schaltflächen mit `behaviors`, Eingabefeldern, Tabellen und Collapsible-Panels. Besonders hilfreich sind die detaillierten Farb- und Emoji-Referenzen sowie die Syntax für @-Erwähnungen in Karten. Der Skill enthält außerdem Bash-Befehle zum Senden, Antworten und Aktualisieren von Karten über die Lark-CLI, inklusive Idempotenz-Schlüssel zur Vermeidung von Duplikaten. Entwickler können mit diesem Skill schnell ansprechende und funktionsreiche Nutzeroberflächen für ihre KI-Assistenten erstellen.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add Aquarius-mu/skillManager

In Ihrer bevorzugten KI fragen

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

Vorschau

Aus dem Skill-README

Hover-Text

Dokumentation

feishu-card (schema 2.0)

Schlüsselregeln

  • Alle Karten verwenden Schema 2.0, Elemente werden in body.elements[] platziert, nicht auf oberster Ebene elements[]
  • Karten müssen mit --as bot, --msg-type interactive und --content als JSON-String gesendet werden
  • Erwähnungen in Karten verwenden <at id="ou_xxx">Name</at> (nicht <at user_id>)
  • JSON 2.0 Buttons haben keinen action-Container mehr, Buttons werden direkt in body.elements platziert, Interaktionen erfolgen über das behaviors-Array
  • Zuerst eine "Denken"-Karte senden, um die message_id zu erhalten, nach Claudes Antwort mit PATCH lokal aktualisieren, um Nachrichtenflut zu vermeiden
  • Der note-Tag wird in Schema 2.0 nicht mehr unterstützt (Fehler 200861), für Notizen stattdessen div + lark_md verwenden (<font color=grey>Notiztext</font>)
  • Das JSON der Karte muss mit Python heredoc oder einer Datei erstellt werden, $(python3 -c "...") ist verboten – bei Inhalten mit Backticks oder Sonderzeichen führt Bash zu Syntaxfehlern

Vollständige Felder der obersten Ebene

{
  "schema": "2.0",
  "config": {
    "streaming_mode": false,
    "enable_forward": true,
    "update_multi": true,
    "width_mode": "default"
  },
  "card_link": {
    "url": "https://...",
    "pc_url": "https://...",
    "ios_url": "https://...",
    "android_url": "https://..."
  },
  "header": { "...": "..." },
  "body": {
    "direction": "vertical",
    "padding": "0px",
    "elements": []
  }
}

config-Felder

FeldTypStandardBeschreibung
streaming_modeboolfalseStreaming-Update-Modus (für Platzhalter während des Denkens)
enable_forwardbooltrueErlaubt das Weiterleiten der Karte
update_multiboolfalseJSON 2.0 Shared-Card-Modus (für synchrone Updates mehrerer Benutzer auf true setzen)
width_modestringdefault (600px)compact (400px) / fill (volle Breite, responsiv) / default

Header-Felder

{
  "header": {
    "title":    {"tag": "plain_text", "content": "Titel"},
    "subtitle": {"tag": "plain_text", "content": "Untertitel (optional)"},
    "template": "blue",
    "icon": {
      "tag": "standard_icon",
      "token": "robot-outlined",
      "color": "blue"
    },
    "text_tag_list": [
      {"tag": "text_tag", "text": {"tag": "plain_text", "content": "Neu"}, "color": "blue"}
    ]
  }
}

template-Farben: blue / wathet / turquoise / green / yellow / orange / red / carmine / violet / purple / indigo / grey


Feishu-Farben-Schnellreferenz

Alle Felder, die Farben unterstützen (background_color, text_color, icon color, usw.), verwenden das folgende Format. Es dürfen nicht nur die Basis-Farbnamen verwendet werden (z. B. führt "blue" bei einigen Komponenten zu Fehlern). Es muss ein Ebenen-Suffix angegeben werden:

FarbsystemHell (für Hintergründe empfohlen)MittelDunkel (für Text empfohlen)
Blau blueblue-50 blue-100 blue-200blue-300~blue-500blue-600~blue-900
Wasserblau wathetwathet-50 wathet-100 wathet-200wathet-300~wathet-500wathet-600~wathet-900
Türkis turquoiseturquoise-50 turquoise-100turquoise-300~turquoise-500turquoise-600~turquoise-900
Grün greengreen-50 green-100 green-200green-300~green-500green-600~green-900
Gelbgrün limelime-50 lime-100lime-300~lime-500lime-600~lime-900
Gelb yellowyellow-50 yellow-100yellow-300~yellow-500yellow-600~yellow-900
Orange orangeorange-50 orange-100orange-300~orange-500orange-600~orange-900
Rot redred-50 red-100red-300~red-500red-600~red-900
Pink carminecarmine-50 carmine-100carmine-300~carmine-500carmine-600~carmine-900
Lila purplepurple-50 purple-100purple-300~purple-500purple-600~purple-900
Blaulila violetviolet-50 violet-100violet-300~violet-500violet-600~violet-900
Indigo indigoindigo-50 indigo-100indigo-300~indigo-500indigo-600~indigo-900
Grau greygrey-00 grey-50 grey-100grey-300~grey-500grey-600~grey-1000
Weißwhite bg-white——

Empfohlene helle Hintergründe (für collapsible_panel, column-Hintergründe usw.):

blue-100    wathet-100    green-100    yellow-100
orange-100  red-100       purple-100   carmine-100
turquoise-100    violet-100    indigo-100    lime-100

Komponenten-Schnellreferenz

tagTypBeschreibung
divInhaltTextblock (plain_text / lark_md), unterstützt vorangestelltes Icon
markdownInhaltReiner Markdown-Text (kein @, @ mit div+lark_md)
imgInhaltBild
tableInhaltDatentabelle (max. 5 pro Karte, nicht verschachtelbar)
hrLayoutTrennlinie
noteLayout❌ In Schema 2.0 veraltet, stattdessen div + lark_md + <font color=grey> verwenden
column_setContainerMehrspaltiges Layout
collapsible_panelContainerAufklappbares Panel
buttonInteraktionSchaltfläche (JSON 2.0 direkt in elements, kein action-Container)
inputInteraktionTexteingabefeld (muss in einem form verwendet werden)
select_staticInteraktionStatischer Dropdown-Selektor

div (Textblock)

{
  "tag": "div",
  "text": {
    "tag": "lark_md",
    "content": "**Fett** _Kursiv_ `Code`",
    "text_size": "normal",
    "text_color": "default",
    "text_align": "left",
    "lines": 3
  },
  "icon": {"tag": "standard_icon", "token": "robot-outlined", "color": "blue"}
}

text.tag: plain_text (Klartext) oder lark_md (Markdown + @) text_size: heading-0 / heading / normal (Standard) / notation / x-small text_align: left / center / right


img (Bild)

{
  "tag": "img",
  "img_key": "img_xxx",
  "alt": {"tag": "plain_text", "content": "Hover-Beschreibung"},
  "title": {"tag": "plain_text", "content": "Bildtitel"},
  "scale_type": "crop_center",
  "size": "medium",
  "corner_radius": "4px",
  "preview": true
}

scale_type: crop_center / crop_top / fit_horizontal size: stretch (volle Breite) / large / medium / small / tiny / "300px 200px" (benutzerdefiniert)

JSON 2.0 unterstützt stretch_without_padding nicht, für volle Breite stattdessen negativen Margin verwenden


column_set (Mehrspaltiges Layout)

{
  "tag": "column_set",
  "flex_mode": "bisect",
  "horizontal_spacing": "medium",
  "background_style": "default",
  "columns": [
    {
      "tag": "column",
      "width": "weighted",
      "weight": 1,
      "vertical_align": "top",
      "elements": [{"tag": "div", "text": {"tag": "plain_text", "content": "Linke Spalte"}}]
    },
    {
      "tag": "column",
      "width": "weighted",
      "weight": 1,
      "elements": [{"tag": "div", "text": {"tag": "plain_text", "content": "Rechte Spalte"}}]
    }
  ]
}

flex_mode: none (proportional komprimieren) / stretch (auf schmalen Bildschirmen vertikal anordnen) / flow (automatischer Umbruch) / bisect (zwei gleich breite Spalten) / trisect (drei gleich breite Spalten) horizontal_spacing: small(4px) / medium(8px) / large(12px) / extra_large(16px) column.width: auto / weighted (mit weight 1-5) / "200px"

Maximal 5 Ebenen Verschachtelung, innerhalb einer Spalte dürfen kein form-Container und keine table platziert werden


collapsible_panel (Aufklappbares Panel)

{
  "tag": "collapsible_panel",
  "expanded": false,
  "background_color": "default",
  "header": {
    "title": {"tag": "plain_text", "content": "Zum Aufklappen klicken"},
    "background_color": "grey",
    "icon": {"tag": "standard_icon", "token": "arrow-down-outlined"},
    "icon_expanded_angle": -180
  },
  "elements": [
    {"tag": "div", "text": {"tag": "plain_text", "content": "Aufgeklappter Inhalt"}}
  ]
}

table (Datentabelle)

{
  "tag": "table",
  "page_size": 5,
  "row_height": "low",
  "freeze_first_column": false,
  "header_style": {
    "text_align": "left",
    "text_size": "normal",
    "background_style": "grey",
    "bold": true
  },
  "columns": [
    {"name": "name", "display_name": "Name", "data_type": "text", "width": "auto"},
    {"name": "status", "display_name": "Status", "data_type": "options", "width": "120px"},
    {"name": "count", "display_name": "Anzahl", "data_type": "number",
     "format": {"precision": 0, "separator": true}},
    {"name": "ts", "display_name": "Zeit", "data_type": "date",
     "format": "YYYY/MM/DD HH:mm"}
  ],
  "rows": [
    {"name": "Max Mustermann", "status": [{"name": "In Bearbeitung", "color": "blue"}], "count": 42, "ts": 1700000000000}
  ]
}

data_type: text / lark_md / markdown / options / number / persons / date row_height: low / middle / high / auto / "48px" (32-124px)

Maximal 5 Tabellen pro Karte, nicht verschachtelbar


button (JSON 2.0)

{
  "tag": "button",
  "text": {"tag": "plain_text", "content": "Bestätigen"},
  "type": "primary",
  "size": "medium",
  "width": "default",
  "disabled": false,
  "behaviors": [
    {
      "type": "callback",
      "value": {"action": "confirm", "data": "payload"}
    }
  ],
  "confirm": {
    "title": {"tag": "plain_text", "content": "Zweite Bestätigung"},
    "text":  {"tag": "plain_text", "content": "Möchten Sie diesen Vorgang wirklich ausführen?"}
  }
}

type: default (grau) / primary (blau) / danger (rot) / text / primary_text / danger_text / primary_filled / danger_filled / laser size: tiny / small / medium / large behaviors[].type: callback (Ereignis auslösen) / open_url (Link öffnen)

JSON 2.0 Änderung: Es gibt keinen action-Container mehr, Buttons werden direkt in body.elements oder column.elements platziert; Interaktionen erfolgen über behaviors anstelle des alten value


input (Texteingabe)

{
  "tag": "input",
  "name": "field_name",
  "placeholder": {"tag": "plain_text", "content": "Bitte eingeben..."},
  "default_value": "",
  "input_type": "text",
  "max_length": 200,
  "required": false,
  "disabled": false,
  "label": {"tag": "plain_text", "content": "Beschriftung"},
  "label_position": "top",
  "width": "fill"
}

input_type: text / multiline_text / password Der Benutzer löst beim Absenden das Ereignis card.action.trigger aus, das Ereignis enthält input_value


@-Erwähnungs-Syntax

Nachrichtentyp@-Person-Syntax@-Alle
Karte interactive lark_md<at id="ou_xxx">Name</at><at id="all">Alle</at>
Text text / post<at user_id="ou_xxx">Name</at><at user_id="all"></at>

@-Erwähnung innerhalb einer Karte – Verifizierte Elementstruktur (div + lark_md, kein nackter markdown-Tag):

{
  "tag": "div",
  "text": {
    "tag": "lark_md",
    "content": "<at id=\"ou_xxx\">Name</at> Nachrichtentext"
  }
}

Markdown-Syntax-Schnellreferenz (lark_md / markdown-Tag)

Textstile

**Fett**    *Kursiv*    ~~Durchgestrichen~~    `Inline-Code`

Farbiger Text

<font color=red>Rot</font>
<font color=green>Grün</font>
<font color=grey>Grau</font>
<font color=blue>Blau</font>
<font color=orange>Orange</font>
<font color=wathet>Wasserblau</font>
<font color=purple>Lila</font>

Vollständige Farben: red / green / grey / blue / orange / wathet / purple / yellow / turquoise / indigo / violet / lime

Tags / Zahlen-Badges

<text_tag color='red'>Tag</text_tag>
<number_tag>99</number_tag>

Lokalisierte Zeit

<local_datetime millisecond='1700000000000' format_type='date_num'></local_datetime>

format_type: date_num (2023/11/15) / date_full (15. November 2023) / datetime (mit Stunden/Minuten/Sekunden)

Überschriften / Listen / Zitate

# H1  ## H2  ### H3  #### H4  ##### H5  ###### H6

- Ungeordnet  1. Geordnet  > Zitat

Codeblöcke

```python
print("hallo")
```

Bilder (innerhalb von Markdown)

![Hover-Text](img_xxx)

Tabellen (maximal 5 Datenzeilen)

| Spalte1 | Spalte2 |
|-----|-----|
| A   | B   |

Links

[Text](https://url)    [Telefon](tel://13800138000)

Trennlinien / Zeilenumbrüche

---    oder   <hr>
<br/>   oder   zweimal Eingabetaste (Absatzumbruch)

Emojis

⚠️ Strenge Regeln

  1. Es dürfen nur die Keys der offiziellen Liste verwendet werden. Nicht erfasste Keys (wie :WAITING:, :PROCESSING:) werden als Klartext gerendert und sind verboten.
  2. Die Groß-/Kleinschreibung der Keys muss exakt mit der offiziellen Liste übereinstimmen (z. B. Fire nicht FIRE, Trophy nicht TROPHY, Shrug nicht SHRUG).
  3. Innerhalb von <text_tag> wird :KEY: nicht gerendert, stattdessen müssen Unicode-Emojis verwendet werden (z. B. ✅).
  4. :KEY: funktioniert nur im normalen Textbereich von lark_md.

Häufig verwendet für Release-Benachrichtigungen

:DONE:        :OK:           :THUMBSUP:     :APPLAUSE:
:PARTY:       :FIREWORKS:    :MUSCLE:       :THANKS:
:Trophy:      :Fire:         :Hundred:      :CheckMark:

Arbeit & Status

:LGTM:        :OnIt:         :OneSecond:    :Get:
:ERROR:        :CrossMark:    :Pin:          :Alarm:
:Loudspeaker: :OKR:          :Yes:          :No:
:HEADSET:     :Typing:       :MeMeMe:       :Sigh:
:Lemon:       :AWESOMEN:     :VRHeadset:    :YouAreTheBest:
:SALUTE:      :HIGHFIVE:     :FISTBUMP:     :FINGERHEART:
:WAVE:        :ThumbsDown:

Gesichts-Emojis – Positiv

:SMILE:       :LAUGH:        :LOL:          :LOVE:
:WINK:        :PROUD:        :WITTY:        :JOYFUL:
:WOW:         :BLUSH:        :HUG:          :BeamingFace:
:Delighted:   :ThanksFace:   :SaluteFace:   :PRAISE:
:CLAP:        :JIAYI:        :FACEPALM:     :YEAH:

Gesichts-Emojis – Negativ / Lustig

:CRY:         :SOB:          :ANGRY:        :SHOCKED:
:Shrug:       :THINKING:     :DIZZY:        :SLEEP:
:YAWN:        :SICK:         :SWEAT:        :EMBARRASSED:
:SKULL:       :RAINBOWPUKE:  :PUKE:         :ClownFace:
:ColdSweat:   :SMUG:         :SILENT:       :TERROR:
:PETRIFIED:   :BETRAYED:     :WRONGED:

Feiern & Feiertage

:PARTY:       :FIREWORKS:    :REDPACKET:    :FORTUNE:
:FIRECRACKER: :XmasTree:     :XmasHat:      :Snowman:
:Pumpkin:     :Mooncake:     :MoonRabbit:   :Partying:
:StickyRiceBalls:            :JubilantRabbit:  :LUCK:
:FullMoonFace: :GoGoGo:      :HappyDragon:

Gegenstände & Essen

:ROSE:        :HEART:        :BEER:         :CAKE:
:GIFT:        :BOMB:         :Fire:         :Trophy:
:Coffee:      :BubbleTea:    :Drumstick:    :Pepper:
:CUCUMBER:    :CANDIEDHAWS:  :Soccer:       :Basketball:
:Music:       :CLEAVER:      :EatingFood:   :LIPS:

Statusanzeigen (nicht häufig verwenden, nur für Status-Szenarien)

:GeneralDoNotDisturb:        :GeneralInMeetingBusy:
:StatusReading:              :GeneralWorkFromHome:
:GeneralBusinessTrip:        :StatusInFlight:
:GeneralSun:                 :GeneralMoonRest:
:StatusFlashOfInspiration:   :Status_PrivateMessage:
:GeneralTravellingCar:       :StatusBus:

Unicode-Emojis (in Titeln, text_tag, Header-Subtiteln)

🎉 ✅ ❌ ⚠️ 🚀 📦 💬 ⏳ 🔔 📝 🛠️ 🔍 👋 🎯 💡 🌟
👥 📅 🤖 🏆 🔥 💯 📣 🎁 ☕ 🍺

Empfohlener Stil: Titel mit Unicode-Emojis für visuelle Verstärkung, Status/Rückmeldungen mit Feishu-spezifischer Syntax wie :DONE: :LGTM:


Senden / Antworten / Aktualisieren

An Gruppe senden (neue Nachricht)

CARD=$(python3 -c "
import json
card = {
  'schema': '2.0',
  'header': {'title': {'tag': 'plain_text', 'content': 'Titel'}, 'template': 'blue'},
  'body': {'elements': [{'tag': 'markdown', 'content': 'Inhalt'}]}
}
print(json.dumps(card))
")
lark-cli im +messages-send \
  --chat-id oc_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --as bot

Auf bestimmte Nachricht antworten (message_id für spätere Aktualisierung abrufen)

MSG_ID=$(lark-cli im +messages-reply \
  --message-id om_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --as bot \
  --jq '.data.message_id')

In Thread antworten (erscheint nicht im Haupt-Chat-Feed)

lark-cli im +messages-reply \
  --message-id om_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --reply-in-thread \
  --as bot

Duplikate vermeiden (innerhalb von 1 Stunde wird derselbe Key nur einmal gesendet)

lark-cli im +messages-send \
  --chat-id oc_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --idempotency-key "deploy-654.0-notify" \
  --as bot

Karte lokal aktualisieren (PATCH)

PAYLOAD=$(jq -n --argjson card "$CARD" '{"msg_type":"interactive","content":($card|tojson)}')
lark-cli api PATCH "/open-apis/im/v1/messages/${MSG_ID}" \
  --data "$PAYLOAD" \
  --as bot

Kartenvorlagen

Wird gedacht (Streaming-Status, zuerst Platzhalter senden)

make_thinking_card() {
  local name="$1" question="$2"
  jq -n --arg name "$name" --arg q "$question"

...

[truncated for translation]

Verwandte Skills