Communitygithub.com

hankunpeng/hkp-skills

Eine Sammlung benutzerdefinierter KI-Agenten-Fähigkeiten für Google Antigravity und das offene Ökosystem der Agenten-Fertigkeiten.

Was ist hkp-skills?

Diese Fähigkeit (Skill) ermöglicht es KI-Agenten, Text und Bilder auf X (Twitter) zu posten, Tweets zu löschen und Tweets zu exportieren. Sie folgt einem hybriden Ausführungsmodell: Im Idealfall wird die offizielle X-API verwendet, was schnell und ressourcenschonend ist. Falls die API nicht verfügbar oder das monatliche Kontingent ausgeschöpft ist, wird automatisch auf den Chrome Computer Use Mode oder einen CLI Bridge-Modus umgeschaltet, der Browserinteraktionen simuliert. Der Skill unterstützt macOS, Linux und Windows und enthält detaillierte Skripte für die Zwischenablage, den API-Zugriff und den Tweet-Export. Entwickler profitieren von robusten DOM-Automatisierungsrichtlinien, die häufige Probleme mit React/Draft.js Editoren vermeiden, und von einer klaren Trennung zwischen API- und Browser-Fallback, um eine hohe Zuverlässigkeit zu gewährleisten.

Funktioniert mit✓Claude Code~Codex CLI~Cursor✓Antigravity
npx skills add hankunpeng/hkp-skills

In Ihrer bevorzugten KI fragen

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

Dokumentation

Twitter-Fähigkeit (Skill)

Ermöglicht das Posten von Text und Bildern auf X (Twitter) mittels Chrome Computer Use Mode.

Skriptverzeichnis

Wichtig: Alle Skripte befinden sich im scripts/-Unterverzeichnis dieser Fähigkeit.

Anweisungen für die Agentenausführung:

  1. Ermitteln Sie den Verzeichnispfad dieser SKILL.md-Datei als {baseDir}
  2. Skriptpfade:
    • Zwischenablage-Helfer: {baseDir}/scripts/copy-to-clipboard.ts
    • Tweet-Exporter/-Scraper: {baseDir}/scripts/export-tweets.ts
    • Offizieller X-API-Helfer: {baseDir}/scripts/x-api.ts
  3. Ermitteln Sie die ${BUN_X}-Laufzeit: falls bun installiert → bun; falls npx verfügbar → npx -y bun; andernfalls Installation von bun vorschlagen
  4. Ersetzen Sie alle {baseDir} und ${BUN_X} in diesem Dokument durch die tatsächlichen Werte

Ausführungsmodus

Diese Fähigkeit folgt einem Hybriden Ausführungsmodell:

  1. API zuerst (Empfohlen für Textbeiträge): Versuchen Sie, den Tweet mit dem offiziellen X-API-Skript (x-api.ts) zu posten. Dies ist schnell, stabil und verbraucht keine Browser-Ressourcen.
  2. Browser-Fallback: Falls der API-Aufruf fehlschlägt (z.B. monatliches 1500-Tweet Free-Tier-Kontingent überschritten, Rate-Limit) oder API-Anmeldedaten nicht in subscription.yaml konfiguriert sind, wechseln Sie automatisch in den Chrome Computer Use Mode / CLI Bridge, um Browser-Aktionen zu simulieren.

Voraussetzungen

  • Für den API-Modus: Konfigurieren Sie Ihre X-API-Anmeldedaten (OAuth 1.0a User Context Tokens) in Ihrer lokalen subscription.yaml-Datei (die git-ignoriert ist):
    x_api:
      api_key: "IHR_API_SCHLÜSSEL"
      api_key_secret: "IHR_API_SCHLÜSSEL_GEHEIMNIS"
      access_token: "IHR_ZUGRIFFSTOKEN"
      access_token_secret: "IHR_ZUGRIFFSTOKEN_GEHEIMNIS"
    
  • Für den Browser-Fallback: Google Chrome installiert, in Chrome bei X (Twitter) angemeldet und, falls erforderlich, macOS-Barrierefreiheitsberechtigungen erteilt.

Arbeitsablauf für reguläre Beiträge (Text & Bilder)

Bei der Ausführung eines Beitrags:

  1. Starten Sie den Agenten-Durchgang mit dem Aufruf von get_app_state (oder einem entsprechenden Werkzeug) für Google Chrome.
  2. Öffnen Sie Google Chrome oder navigieren Sie zu https://x.com/compose/post.
  3. Lokalisieren Sie das Eingabefeld des Tweet-Verfassers.
  4. Geben Sie den Beitragstext mithilfe von Tastatureingaben des Computer Use in den Verfasser ein.
  5. Falls Bilder angehängt werden sollen (max. 4): Für jedes Bild: a. Führen Sie das Zwischenablage-Helfer-Skript aus, um das Bild in die Zwischenablage zu kopieren:
    ${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts image /absoluter/pfad/zu/bild.png
    
    b. Fügen Sie das Bild mit der Tastenkombination zum Einfügen (super+v auf macOS, control+v auf Windows/Linux) in den Verfasser ein. c. Warten Sie 2-3 Sekunden, bis X das Hochladen der Medien abgeschlossen hat.
  6. Veröffentlichungssicherheit: Klicken Sie niemals auf Veröffentlichen, Posten oder eine entsprechende Schaltfläche, um den Tweet zu veröffentlichen, ohne vorher eine explizite endgültige Bestätigung des Benutzers im aktuellen Gespräch eingeholt zu haben.
  7. Sobald der Benutzer bestätigt hat, klicken Sie zum Veröffentlichen auf die Schaltfläche Posten.
  8. Schließen Sie nach dem Veröffentlichen das Verfasser-Modal, damit die Benutzeroberfläche nicht im Verfassen-Dialog stecken bleibt. Verwenden Sie die Schaltfläche zum Schließen oder die Escape-Taste:
    • DOM-Selektor: [data-testid="app-bar-close"] oder [aria-label="Schließen"]
    • Fallback: Senden Sie ein Escape-keydown-Ereignis
  9. Automatisches Neuladen des Feeds (Optional): Falls der Benutzer andere Tabs mit seinem Profil (z.B. x.com/[benutzername]) oder dem Start-Feed (x.com/home) geöffnet hat, laden Sie diese neu, damit der neue Tweet sofort sichtbar ist.
var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Schließen"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }

CLI Bridge (Keine Computer Use Werkzeuge)

Wenn die Umgebung keine Computer Use Tastatur-/Mauswerkzeuge bereitstellt, verwenden Sie plattformspezifische Methoden, um Chrome zu öffnen und JavaScript in die Seite zu injizieren.

macOS (AppleScript)

Verfasser-Seite öffnen:

open -a "Google Chrome" "https://x.com/compose/post"

JavaScript in Chrome ausführen — schreiben Sie JS zuerst in eine temporäre Datei (vermeidet Probleme mit Shell-Escaping) und führen Sie es dann über AppleScript aus:

cat > /tmp/tweet.js << 'EOF'
(function() { /* Ihr Code */ })();
EOF

osascript -e '
tell application "Google Chrome"
    activate
    set js to read "/tmp/tweet.js"
    set result to execute front window''''s active tab javascript js
    return result
end tell'

Muster: Schreiben Sie die JS-Nutzlast immer zuerst in eine temporäre Datei. Versuchen Sie NICHT, osascript -e mit eingebettetem JS inline zu verwenden — Anführungszeichen-/Escaping-Konflikte führen zu Parsing-Fehlern.

Verfasser nach dem Veröffentlichen schließen (andernfalls bleibt das Modal geöffnet):

var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Schließen"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }

Profil-/Startseiten-Tabs neu laden, um den neuen Beitrag sofort anzuzeigen (Optional):

osascript -e '
tell application "Google Chrome"
    tell window 1
        set tabList to every tab whose URL contains "x.com/benutzername" or URL contains "x.com/home"
        repeat with t in tabList
            reload t
        end repeat
    end tell
end tell'

Linux

google-chrome "https://x.com/compose/post"
# Verwenden Sie chrome-remote-interface oder einen ähnlichen CDP-Client, um JS auszuführen

Windows

Start-Process "chrome" "https://x.com/compose/post"
# Verwenden Sie CDP über --remote-debugging-port oder PowerShell-Automatisierung

JavaScript / DOM-Automatisierungsrichtlinien (Fallback)

Wenn direkte Computer Use Tastatur-/Mausereignisse nicht verfügbar sind oder Sie im Browser-Scripting-/CDP-Modus arbeiten, befolgen Sie diese Richtlinien zur Interaktion mit X's React/Draft.js-Editor:

  1. Erkennung mehrerer Editoren: X-Seiten enthalten oft mehrere Verfasser-Textbereiche (z.B. inline-Verfasser im Start-Feed und aktives Modal-Dialogfeld). Fragen Sie immer alle Editoren ab:

    var els = document.querySelectorAll('[data-testid="tweetTextarea_0"]');
    var el = els.length > 1 ? els[els.length - 1] : els[0];
    

    Zielen Sie immer auf den aktiven Modal-Verfasser (normalerweise das letzte Element in der Liste).

  2. Bindungsauswahl & Fokus: Bevor Sie Text einfügen, MÜSSEN Sie auf das Element klicken, um die Draft.js-Auswahlbindung auszulösen, und dann den Fokus setzen:

    el.click();
    el.focus();
    
  3. Erhaltung der Editorstruktur:

    • Verwenden Sie NICHT el.innerHTML = '' oder document.execCommand('delete') bei einem leeren Verfasser. Das Löschen der DOM-Knoten zerstört die interne Wrapper-Struktur von Draft.js (z.B. den public-DraftStyleDefault-block-Span), was die React-Komponente zum Absturz bringt und die Posten-Schaltfläche dauerhaft deaktiviert.
    • Verwenden Sie stattdessen document.execCommand('insertText', false, text) direkt im leeren, fokussierten Editor.
  4. Auslösen von React-Zustandsaktualisierungen: Senden Sie nach dem Texteinfügen ein bubbles input-Ereignis, um React zu benachrichtigen:

    el.dispatchEvent(new Event('input', { bubbles: true }));
    
  5. Auffinden der korrekten Posten-Schaltfläche: Die Test-IDs der Schaltflächen (tweetButtonInline und tweetButton) können je nach Kontext vertauscht sein. Suchen Sie immer nach der sichtbaren, aktivierten Schaltfläche:

    var btns = document.querySelectorAll('[data-testid="tweetButtonInline"], [data-testid="tweetButton"]');
    var activeBtn = Array.from(btns).find(function(btn) {
        var isVisible = btn.offsetWidth > 0 && btn.offsetHeight > 0;
        var isDisabled = btn.disabled || btn.getAttribute('aria-disabled') === 'true';
        return isVisible && !isDisabled;
    });
     if (activeBtn) activeBtn.click();
    

Arbeitsablauf zum Löschen eines Tweets

Bei der Ausführung einer Löschung:

  1. Öffnen Sie Google Chrome oder navigieren Sie zur Profilseite des Benutzers (https://x.com/[benutzername]) oder zur direkten Tweet-URL (https://x.com/[benutzername]/status/[tweetId]).
  2. Suchen Sie den Ziel-Tweet <article>-Container, der den zu löschenden Text enthält.
  3. Klicken Sie auf die Optionsmenü-Schaltfläche des Tweets:
    • DOM-Selektor: [data-testid="caret"]
  4. Warten Sie 1-2 Sekunden, dann klicken Sie auf den Menüpunkt "Löschen":
    • DOM-Selektor: Ein [role="menuitem"]-Element, dessen Text "Löschen" oder "Delete" enthält.
  5. Warten Sie 1-2 Sekunden, dann klicken Sie im Dialogfeld auf die Bestätigungsschaltfläche zum Löschen:
    • DOM-Selektor: [data-testid="confirmationSheetConfirm"] (oder Fallback auf eine beliebige Dialogs chaltfläche mit dem Text "Löschen" oder "Delete").

CLI Bridge Beispiel (macOS)

Verwenden Sie dasselbe Muster mit temporärer Datei und AppleScript wie beim Posten. Ersetzen Sie TWEET_TEXT_HERE durch den Inhalt des Ziel-Tweets.

Schritt 1 — Tweet finden und Caret klicken:

cat > /tmp/del-1.js << 'EOF'
(function() {
  var articles = document.querySelectorAll('article');
  var target = null;
  var needle = 'TWEET_TEXT_HERE';
  for (var i = 0; i < articles.length; i++) {
    var textEl = articles[i].querySelector('[data-testid="tweetText"]');
    if (textEl && textEl.textContent.trim().indexOf(needle) !== -1) {
      target = articles[i];
      break;
    }
  }
  if (!target) return 'ERROR: tweet not found';
  var caret = target.querySelector('[data-testid="caret"]');
  if (!caret) return 'ERROR: caret not found';
  caret.click();
  return 'OK: caret clicked';
})();
EOF

osascript -e '
tell application "Google Chrome"
    set js to read "/tmp/del-1.js"
    set result to execute front window''''s active tab javascript js
    return result
end tell'

Schritt 2 — Menüpunkt "Löschen" klicken (1-2s nach Schritt 1 warten):

cat > /tmp/del-2.js << 'EOF'
(function() {
  var items = document.querySelectorAll('[role="menuitem"]');
  for (var i = 0; i < items.length; i++) {
    var txt = items[i].textContent.trim();
    if (txt === 'Delete' || txt === '删除') {
      // Klicken Sie auf das Menüelement selbst oder, falls es ein verschachteltes klickbares Element enthält
      var clickTarget = items[i].querySelector('[role="menuitem"]') || items[i];
      clickTarget.click();
      return 'OK: delete clicked (index ' + i + ')';
    }
  }
  // Fallback: erstes Menüelement klicken (im Dropdown immer "Löschen")
  var first = document.querySelectorAll('[role="menuitem"]')[0];
  if (first) { first.click(); return 'OK: first menuitem clicked'; }
  return 'ERROR: no menuitems';
})();
EOF

osascript -e '
tell application "Google Chrome"
    set js to read "/tmp/del-2.js"
    set result to execute front window''''s active tab javascript js
    return result
end tell'

Schritt 3 — Löschung bestätigen (1-2s nach Schritt 2 warten):

cat > /tmp/del-3.js << 'EOF'
(function() {
  var confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
  if (!confirmBtn) {
    var buttons = document.querySelectorAll('[role="button"], button');
    for (var i = 0; i < buttons.length; i++) {
      var txt = buttons[i].textContent.trim();
      if (txt === 'Delete' || txt === '删除') {
        confirmBtn = buttons[i];
        break;
      }
    }
  }
  if (!confirmBtn) return 'ERROR: confirm button not found';
  confirmBtn.click();
  return 'OK: confirm clicked';
})();
EOF

osascript -e '
tell application "Google Chrome"
    set js to read "/tmp/del-3.js"
    set result to execute front window''''s active tab javascript js
    return result
end tell'

Arbeitsablauf zum Exportieren von Tweets

Um alle oder gefilterte Tweets von Ihrer Profilseite zu exportieren/zu speichern:

  1. Führen Sie das Exporter-Skript aus:
    ${BUN_X} {baseDir}/scripts/export-tweets.ts [startDatum] [endDatum]
    
    • Optionale Datumsfilter: Sie können startDatum (z.B. 2026-06-01) und endDatum (z.B. 2026-06-30) übergeben, um die Ausgabe nach Datumsbereich zu filtern. Wenn weggelassen, werden alle gescrapten Tweets exportiert.
    • Ausgabedatei: Die Tweets werden in /Users/alex/twitter/twitter.yaml gespeichert, wobei die Tweet-URL der Schlüssel und der Tweet-Text der Wert ist.

Verwandte Skills