x-article-publisher

Publique artigos em Markdown no editor de Artigos do X (Twitter) com formatação adequada.

O que é x-article-publisher?

Este skill permite publicar conteúdo Markdown no editor de Artigos do X (Twitter), convertendo automaticamente Markdown para rich text, gerenciando upload de imagens de capa e de conteúdo, e inserindo divisores de seção através do menu do editor. Ele é útil para criadores de conteúdo que desejam publicar artigos longos e bem formatados no X Premium+, otimizando o fluxo de trabalho com scripts Python para processamento local (parsing, cópia para área de transferência, conversão de tabelas e diagramas). A habilidade suporta as plataformas Claude (via MCP) e depende do Playwright para automação do navegador. O workflow segue uma estratégia de “texto primeiro, imagens depois, divisores por último”, inserindo imagens e divisores na ordem inversa do índice de blocos para evitar deslocamentos. Regras críticas incluem nunca publicar automaticamente (apenas salvar como rascunho) e usar a API de área de transferência para colar rich text. O documento também fornece diretrizes de eficiência para evitar snapshots desnecessários do navegador, executar operações independentes em paralelo e preparar todos os dados antes de iniciar a automação.

Funciona com✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/wshuyi/x-article-publisher-skill/tree/main/skills/x-article-publisher

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Prévia

Do README da skill

Tabela
Diagrama

Documentação

Publicador de Artigos X

Publique conteúdo Markdown no editor de Artigos do X (Twitter), preservando a formatação com conversão para rich text.

Pré-requisitos

  • Playwright MCP para automação de navegador
  • Usuário logado no X com assinatura Premium Plus
  • Python 3.9+ com dependências:
    • macOS: pip install Pillow pyobjc-framework-Cocoa
    • Windows: pip install Pillow pywin32 clip-util
  • Para diagramas Mermaid: npm install -g @mermaid-js/mermaid-cli

Scripts

Localizados em ~/.claude/skills/x-article-publisher/scripts/:

parse_markdown.py

Analisar Markdown e extrair dados estruturados:

python parse_markdown.py <arquivo_markdown> [--output json|html] [--html-only]

Retorna JSON com: title, cover_image, content_images, dividers (com block_index para posicionamento), html, total_blocks

copy_to_clipboard.py

Copiar imagem ou HTML para a área de transferência do sistema (multiplataforma):

# Copiar imagem (com compressão opcional)
python copy_to_clipboard.py image /caminho/para/imagem.jpg [--quality 80]

# Copiar HTML para colar rich text
python copy_to_clipboard.py html --file /caminho/para/conteudo.html

table_to_image.py

Converter tabela Markdown para imagem PNG:

python table_to_image.py <entrada.md> <saida.png> [--scale 2]

Use quando o editor de Artigos do X não suportar renderização nativa de tabelas ou para estilização consistente.

Pré-Processamento (Opcional)

Antes de publicar, verifique o Markdown em busca de elementos que precisam de conversão:

Tabelas → PNG

# Extrair tabela para arquivo temporário e depois converter
python ~/.claude/skills/x-article-publisher/scripts/table_to_image.py /tmp/tabela.md /tmp/tabela.png
# Substituir tabela no markdown por: ![Tabela](https://raw.githubusercontent.com/wshuyi/x-article-publisher-skill/main/tmp/tabela.png)

Diagramas Mermaid → PNG

# Extrair bloco mermaid para arquivo .mmd, depois converter
mmdc -i /tmp/diagrama.mmd -o /tmp/diagrama.png -b white -s 2
# Substituir bloco mermaid por: ![Diagrama](https://raw.githubusercontent.com/wshuyi/x-article-publisher-skill/main/tmp/diagrama.png)

Divisores (---)

Os divisores são detectados automaticamente pelo parse_markdown.py e exibidos no array dividers. Eles devem ser inseridos através do menu Inserir > Divisor do editor de Artigos do X (as tags HTML <hr> são ignoradas pelo X).

Fluxo de Trabalho

Estratégia: "Texto Primeiro, Imagens Depois, Divisores por Último"

Para artigos com imagens e divisores, cole TODO o conteúdo de texto primeiro, depois insira imagens e divisores nas posições corretas usando o índice de blocos.

  1. (Opcional) Pré-processar: Converter tabelas/mermaid para imagens
  2. Analisar Markdown com script Python → obter título, imagens, divisores com block_index, HTML
  3. Navegar para o editor de Artigos do X
  4. Fazer upload da imagem de capa (primeira imagem)
  5. Preencher título
  6. Copiar HTML para a área de transferência (Python) → Colar com Cmd+V
  7. Inserir imagens de conteúdo nas posições especificadas por block_index
  8. Inserir divisores nas posições especificadas por block_index (via menu Inserir > Divisor)
  9. Salvar como rascunho (NUNCA publicar automaticamente)

Princípios de Execução Eficiente

Objetivo: Minimizar o tempo de espera entre operações para uma experiência de automação fluida.

1. Evitar browser_snapshot desnecessário

A maioria das operações do navegador (click, type, press_key, etc.) já inclui o estado da página no resultado. Não chame browser_snapshot separadamente após cada operação; use diretamente o estado da página retornado pela operação.

❌ Incorreto:
browser_click → browser_snapshot → análise → browser_click → browser_snapshot → ...

✅ Correto:
browser_click → obter estado da página do resultado → browser_click → ...

2. Evitar browser_wait_for desnecessário

Use browser_wait_for apenas nestes casos:

  • Aguardar upload de imagem ser concluído (textGone="Fazendo upload da mídia")
  • Aguardar carregamento inicial da página (raro)

Não use browser_wait_for para aguardar o aparecimento de botões ou campos de entrada – eles estão disponíveis imediatamente após o carregamento da página.

3. Executar operações independentes em paralelo

Quando duas operações não têm dependência, você pode chamar várias ferramentas em paralelo na mesma mensagem:

✅ Pode ser paralelo:
- Preencher título (browser_type) + Copiar HTML para a área de transferência (Bash)
- Analisar Markdown para gerar JSON + Gerar arquivo HTML

❌ Não pode ser paralelo (tem dependência):
- É preciso clicar em create primeiro para fazer upload da imagem de capa
- É preciso colar o conteúdo primeiro para inserir imagens

4. Executar operações do navegador em sequência

Cada operação do navegador retorna um estado da página com todas as referências de elementos necessárias. Use essas referências diretamente para a próxima operação:

# Fluxo ideal (cada etapa executa diretamente, sem esperas extras):
browser_navigate → a partir do estado retornado, encontrar botão create → browser_click(create)
→ a partir do estado retornado, encontrar botão de upload → browser_click(upload) → browser_file_upload
→ a partir do estado retornado, encontrar botão de aplicar → browser_click(aplicar)
→ a partir do estado retornado, encontrar campo de título → browser_type(título)
→ clicar no editor → browser_press_key(Meta+v)
→ ...

5. Preparação antecipada

Antes de iniciar as operações do navegador, conclua toda a preparação:

  1. Analisar Markdown para obter dados JSON
  2. Gerar arquivo HTML em /tmp/
  3. Registrar informações de title, cover_image, content_images, etc.

Dessa forma, a fase de operações do navegador pode ser executada continuamente, sem precisar parar para processar dados.

Etapa 1: Analisar Markdown (Python)

Use parse_markdown.py para extrair todos os dados estruturados:

python ~/.claude/skills/x-article-publisher/scripts/parse_markdown.py /caminho/para/artigo.md

Saída JSON:

{
  "title": "Título do Artigo",
  "cover_image": "/caminho/para/primeira-imagem.jpg",
  "cover_exists": true,
  "content_images": [
    {"path": "/caminho/para/img2.jpg", "original_path": "/md/dir/assets/img2.jpg", "exists": true, "block_index": 5, "after_text": "contexto..."},
    {"path": "/caminho/para/img3.jpg", "original_path": "/md/dir/assets/img3.jpg", "exists": true, "block_index": 12, "after_text": "outro..."}
  ],
  "html": "<p>Conteúdo...</p><h2>Seção</h2>...",
  "total_blocks": 45,
  "missing_images": 0
}

Campos principais:

  • block_index: A imagem deve ser inserida APÓS o elemento de bloco neste índice (baseado em 0)
  • total_blocks: Número total de elementos de bloco no HTML
  • after_text: Mantido para referência/depuração apenas, NÃO para posicionamento
  • exists: Se o arquivo de imagem foi encontrado (se false, o upload falhará)
  • original_path: O caminho resolvido do Markdown (antes da busca automática)
  • path: O caminho real a ser usado (pode diferir de original_path se a busca automática foi usada)
  • missing_images: Contagem de imagens não encontradas em lugar nenhum

Salve o HTML em arquivo temporário para a área de transferência:

python parse_markdown.py artigo.md --html-only > /tmp/artigo_html.html

Etapa 2: Abrir o Editor de Artigos do X

Tratamento de erros do navegador

Se encontrar o erro Error: Browser is already in use:

# Opção 1: Fechar o navegador e reabrir
browser_close
browser_navigate: https://x.com/compose/articles

# Opção 2: Se browser_close não funcionar (travado), solicitar que o usuário feche o Chrome manualmente

# Opção 3: Usar uma aba existente e navegar diretamente
browser_tabs action=list  # Ver abas existentes
browser_navigate: https://x.com/compose/articles  # Navegar na aba atual

Melhor prática: Antes de começar, use browser_tabs action=list para verificar o estado, evitando criar abas em branco desnecessárias.

Navegar para o editor

browser_navigate: https://x.com/compose/articles

Importante: Após o carregamento da página, a lista de rascunhos será exibida, não o editor. É necessário:

  1. Aguardar o carregamento da página: Use browser_snapshot para verificar o estado
  2. Clicar imediatamente no botão "create": Não espere por elementos do editor como "Adicionar título", eles só aparecem após clicar em create
  3. Aguardar o carregamento do editor: Após clicar em create, aguarde o aparecimento dos elementos do editor
# 1. Navegar para a página
browser_navigate: https://x.com/compose/articles

# 2. Obter snapshot da página, encontrar o botão create
browser_snapshot

# 3. Clicar no botão create (geralmente ref é algo como "create" ou com a tag create)
browser_click: element="botão create", ref=<ref_botao_create>

# 4. Agora o editor deve estar aberto, pode continuar com upload da imagem de capa, etc.

Nota: Não use browser_wait_for text="Adicionar título" para aguardar o carregamento da página, pois este texto só aparece após clicar em create, o que causará timeout.

Se for necessário login, solicite que o usuário faça login manualmente.

Etapa 3: Fazer Upload da Imagem de Capa

  1. Clique no botão "Adicionar foto ou vídeo"
  2. Use browser_file_upload com o caminho da imagem de capa (da saída JSON)
  3. Verifique se a imagem foi carregada

Etapa 4: Preencher Título

  • Encontre a caixa de texto com o placeholder "Adicionar título"
  • Use browser_type para inserir o título (da saída JSON)

Etapa 5: Colar Conteúdo de Texto (Área de Transferência Python)

Copie o HTML para a área de transferência do sistema usando Python e depois cole:

# Copiar HTML para a área de transferência
python ~/.claude/skills/x-article-publisher/scripts/copy_to_clipboard.py html --file /tmp/artigo_html.html

Depois, no navegador:

browser_click na caixa de texto do editor
browser_press_key: Meta+v

Isso preserva toda a formatação rich text (H2, negrito, links, listas).

Etapa 6: Inserir Imagens de Conteúdo (Posicionamento por Busca de Texto)

Método recomendado: Use a busca pelo texto after_text para posicionamento, mais intuitivo e confiável que block_index.

Princípio de Posicionamento

O campo after_text de cada imagem registra o texto final do parágrafo anterior (máximo 80 caracteres). No editor, procure o parágrafo que contém esse texto e clique para inserir a imagem.

Passos

Para cada imagem de conteúdo (do array content_images), na ordem decrescente de block_index:

# 1. Copiar imagem para a área de transferência (com compressão)
python ~/.claude/skills/x-article-publisher/scripts/copy_to_clipboard.py image /caminho/para/img.jpg --quality 85
# 2. No browser_snapshot, procure o parágrafo que contém after_text
#    Encontre a ref desse parágrafo

# 3. Clique no parágrafo que contém after_text
browser_click: element="parágrafo com texto alvo", ref=<ref_paragrafo>

# 4. **Passo crucial**: Pressione End para mover o cursor para o final da linha
#    Muito importante! Evita clicar em links dentro do parágrafo causando deslocamento de posição
browser_press_key: End

# 5. Colar imagem
browser_press_key: Meta+v

# 6. Aguardar upload (use apenas textGone, sem parâmetro de tempo)
browser_wait_for textGone="Fazendo upload da mídia"

Por que pressionar End?

Problema: Quando o parágrafo contém um link (como [texto do link](https://raw.githubusercontent.com/wshuyi/x-article-publisher-skill/main/url)), clicar no parágrafo pode:

  • Abrir um popup de edição de link
  • Posicionar o cursor dentro do link, em vez de no final do parágrafo

Solução: Após clicar no parágrafo, pressione imediatamente End:

  • Garante que o cursor vá para o final do parágrafo
  • Evita interferência de links
  • A imagem será inserida corretamente após o parágrafo

Estratégia de Posicionamento

Na estrutura retornada pelo browser_snapshot, pesquise pela palavra-chave de after_text:

textbox [ref=editor]:
  generic [ref=p1]:
    - StaticText: "Nas férias de Ano Novo eu estava no celular vendo fotos..."  # Se after_text contém este texto, clique em p1
  heading [ref=h1]:
    - StaticText: "Demonstração"
  generic [ref=p2]:
    - StaticText: "Quão fácil é isso?"
    - link [ref=link1]: "Claude Code"  # Nota: o parágrafo pode conter links
  ...

Exemplo de Inserção Reversa

Se houver 3 imagens, com block_index 5, 12, 27:

  1. Primeiro insira a imagem com block_index=27 (busca por after_text + End + colar)
  2. Depois insira a imagem com block_index=12
  3. Por último, insira a imagem com block_index=5

Inserir do maior para o menor evita problemas de deslocamento de posição.

Etapa 6.5: Inserir Divisores (Via Menu)

Importante: Os divisores --- do Markdown não podem ser colados via tag HTML <hr> (o X Articles ignora). Eles devem ser inseridos através do menu Inserir do X Articles.

Passos

Para cada divisor (do array dividers), na ordem inversa de block_index:

# 1. Clique no elemento de bloco na posição block_index
browser_click no elemento na posição block_index no editor

# 2. Abrir o menu Inserir (botão Adicionar Mídia)
browser_click no botão "Inserir" ou "Adicionar mídia"

# 3. Clicar no item de menu Divisor
browser_click no item de menu "Divisor" ou "Divider"

# O divisor é inserido na posição do cursor

Ordem de inserção com imagens

Recomenda-se inserir todas as imagens primeiro e depois todos os divisores. Ambos na ordem decrescente de block_index:

  1. Inserir todas as imagens (do maior block_index para o menor)
  2. Inserir todos os divisores (do maior block_index para o menor)

Etapa 7: Salvar Rascunho

  1. Verifique se o conteúdo foi colado (confira o indicador de contagem de palavras)
  2. O rascunho salva automaticamente, ou clique no botão Salvar se necessário
  3. Clique em "Visualizar" para verificar a formatação
  4. Relate: "Rascunho salvo. Revise e publique manualmente."

Regras Críticas

  1. NUNCA publique - Apenas salve como rascunho
  2. Primeira imagem = capa - Faça upload da primeira imagem como imagem de capa
  3. Conversão para rich text - Sempre converta Markdown para HTML antes de colar
  4. Use a API de área de transferência - Cole via área de transferência para formatação adequada
  5. Posicionamento por índice de bloco - Use block_index para posicionamento preciso de imagens e divisores
  6. Inserção em ordem reversa - Insira imagens e divisores do maior para o menor block_index
  7. Tratamento de H1 - H1 é usado apenas como título, não incluído no corpo
  8. Divisores via menu - --- do Markdown deve ser inserido via menu Inserir > Divisor (HTML <hr> é ignorado)

Formatação Suportada

ElementoSuporteNotas
H2 (##)NativoCabeçalhos de seção
Negrito (**)NativoÊnfase forte
Itálico (*)NativoÊnfase
Links ([](https://raw.githubusercontent.com/wshuyi/x-article-publisher-skill/main/url))NativoHiperlinks
Listas ordenadasNativo1. 2. 3.
Listas não ordenadasNativo- marcadores
Blockquotes (>)NativoTexto citado
Blocos de códigoConvertido→ Blockquotes
TabelasConvertido→ Imagens PNG (use table_to_image.py)
MermaidConvertido→ Imagens PNG (use mmdc)
Divisores (---)Inserção via menu→ Inserir > Divisor

Fluxo de Exemplo

Usuário: "Publique /caminho/para/artigo.md no X"

# Etapa 1: Analisar Markdown
python ~/.claude/skills/x-article-publisher/scripts/parse_markdown.py /caminho/para/artigo.md > /tmp/artigo.json
python ~/.claude/skills/x-article-publisher/scripts/parse_markdown.py /caminho/para/artigo.md --html-only > /tmp/artigo_html.html
  1. Navegar para https://x.com/compose/articles
  2. Fazer upload da imagem de capa (browser_file_upload apenas para a capa)
  3. Preencher título (do JSON: title)
  4. Copiar e colar HTML:
    python ~/.claude/skills/x-article-publisher/scripts/copy_to_clipboard.py html --file /tmp/artigo_html.html
    
    Depois: browser_press_key Meta+v
  5. Para cada imagem de conteúdo, na ordem inversa de block_index:
    python copy_to_clipboard.py image /caminho/para/img.jpg --quality 85
    
    • Clique no elemento de bloco na posição block_index
    • browser_press_key Meta+v
    • Aguarde até o upload ser concluído
  6. Verificar na visualização
  7. "Rascunho salvo. Revise e publique manualmente."

Melhores Práticas

Por que usar block_index em vez de correspondência de texto?

  1. Posicionamento preciso: Não depende do conteúdo do texto, mesmo que textos semelhantes apareçam em vários lugares, a posição é correta
  2. Confiabilidade: O índice é determinístico, não há confusão devido a textos semelhantes
  3. Depuração facilitada: after_text ainda é mantido para verificação manual

Por que usar Python em vez de JavaScript no navegador?

  1. Processamento local mais confiável: O Python opera diretamente na área de transferência do sistema, sem as restrições de sandbox do navegador
  2. Compressão de imagem: Comprimir imagens antes do upload (--quality 85), reduzindo o tempo de upload
  3. Reutilização de código: Os scripts são fixos, não sendo necessário reescrever a lógica de conversão a cada vez
  4. Depuração facilitada: Os scripts podem ser testados separadamente, problemas são fáceis de localizar

Estratégia de Espera

Descoberta importante: O comportamento real do browser_wait_for do Playwright MCP é primeiro aguardar time segundos, depois verificar a condição, não fazer polling!

// Código real executado:
await new Promise(f => setTimeout(f, time * 1000));  // Espera fixa primeiro
await page.getByText("xxx").waitFor({ state: 'hidden' });  // Depois verifica

Uso correto:

  • ✅ Apenas textGone, sem time: Deixa o Playwright fazer polling sozinho
  • ✅ Apenas time: Espera fixa pelos segundos especificados
  • ❌ textGone + time juntos: Espera time segundos primeiro e depois verifica, perdendo tempo
# Recomendado: apenas textGone, deixa esperar automaticamente a condição ser atendida
browser_wait_for textGone="Fazendo upload da mídia"

# Ou: use browser_snapshot para verificar o estado via polling
# Após cada operação, verifique o estado da página retornado, sem espera extra

Eficiência na Inserção de Imagens

As operações no navegador para cada imagem foram reduzidas de 5 para 2 etapas:

  • Antigo: Clicar → Adicionar mídia → Mídia → Adicionar foto → file_upload
  • Novo: Clicar no parágrafo → Meta+v

Imagem de Capa vs. Imagens de Conteúdo

  • Imagem de capa: Use browser_file_upload (porque há um botão de upload específico)
  • Imagens de conteúdo: Use a área de transferência Python + colar (mais eficiente)

Solução de Problemas

Problemas de Conexão MCP

Se a ferramenta Playwright MCP não estiver disponível (erro No such tool available ou Not connected):

Opção 1: Reconectar o MCP (recomendado)

Execute o comando /mcp, selecione playwright e escolha Restart

Opção 2: Limpar processos residuais e reconectar

# Mate todos os processos playwright residuais
pkill -f "mcp-server-playwright"
pkill -f "@playwright/mcp"

# Depois execute /mcp para reconectar

Localização do arquivo de configuração: ~/.claude/mcp_servers.json

Tratamento de Erros do Navegador

Se encontrar o erro Error: Browser is already in use:

# Opção 1: Fechar o navegador e reabrir
browser_close
browser_navigate: https://x.com/compose/articles

# Opção 2: Matar o processo Chrome
pkill -f "Chrome.*--remote-debugging"
# Depois navegar novamente

Deslocamento de Posição de Imagem

Se a imagem for inserida na posição errada (especialmente ao clicar em parágrafos com links):

Causa: Clicar no parágrafo pode ativar o link acidentalmente, fazendo o cursor ficar na posição errada

Solução: Após clicar, é obrigatório pressionar End para mover o cursor para o final da linha

# Fluxo correto
1. browser_click no parágrafo alvo
2. browser_press_key: End        # Passo crucial!
3. browser_press_key: Meta+v     # Colar imagem
4. browser_wait_for textGone="Fazendo upload da mídia"

Caminho de Imagem Não Encontrado

Se as imagens com caminho relativo no Markdown não forem encontradas (por exemplo, ./assets/image.png está em outro local):

Busca automática: O script parse_markdown.py pesquisa em diretórios comuns. Se ainda assim não encontrar, você pode:

  • Verificar manualmente o local do arquivo e fornecer o caminho absoluto
  • Mover a imagem para o diretório do artigo
  • Usar uma imagem placeholder ou remover a referência

Notas Finais

  • Sempre teste com um artigo curto primeiro antes de publicar conteúdo longo
  • Mantenha os scripts Python atualizados conforme as mudanças na interface do X
  • Para artigos com muitas imagens, considere dividir a publicação em sessões para evitar timeouts

Habilidades Relacionadas