Merge pull request #313 from alirezarezvani/dev
Integrate x-twitter-growth skill across ecosystem
This commit is contained in:
@@ -4,18 +4,18 @@
|
||||
"name": "Alireza Rezvani",
|
||||
"url": "https://alirezarezvani.com"
|
||||
},
|
||||
"description": "150 production-ready skill packages for Claude AI across 9 domains: marketing (42), engineering (23+25), C-level advisory (28), regulatory/QMS (12), product (8), project management (6), business growth (4), and finance (2). Includes 240 Python tools, 343 reference documents, 14 agents, and 14 slash commands.",
|
||||
"description": "151 production-ready skill packages for Claude AI across 9 domains: marketing (43), engineering (23+25), C-level advisory (28), regulatory/QMS (12), product (8), project management (6), business growth (4), and finance (2). Includes 245 Python tools, 344 reference documents, 14 agents, and 14 slash commands.",
|
||||
"homepage": "https://github.com/alirezarezvani/claude-skills",
|
||||
"repository": "https://github.com/alirezarezvani/claude-skills",
|
||||
"metadata": {
|
||||
"description": "150 production-ready skill packages across 9 domains with 240 Python tools, 343 reference documents, 14 agents, and 14 slash commands. Compatible with Claude Code, Codex CLI, Gemini CLI, and OpenClaw.",
|
||||
"description": "151 production-ready skill packages across 9 domains with 245 Python tools, 344 reference documents, 14 agents, and 14 slash commands. Compatible with Claude Code, Codex CLI, Gemini CLI, and OpenClaw.",
|
||||
"version": "2.1.2"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "marketing-skills",
|
||||
"source": "./marketing-skill",
|
||||
"description": "42 marketing skills across 7 pods: Content, SEO, CRO, Channels, Growth, Intelligence, and Sales enablement. 46 Python tools, 72 reference docs.",
|
||||
"description": "43 marketing skills across 7 pods: Content, SEO, CRO, Channels, Growth, Intelligence, Sales enablement, and X/Twitter growth. 51 Python tools, 73 reference docs.",
|
||||
"version": "2.1.2",
|
||||
"author": {
|
||||
"name": "Alireza Rezvani"
|
||||
@@ -30,7 +30,9 @@
|
||||
"copywriting",
|
||||
"email",
|
||||
"social-media",
|
||||
"paid-ads"
|
||||
"paid-ads",
|
||||
"twitter",
|
||||
"x-twitter"
|
||||
],
|
||||
"category": "marketing"
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
/git:cm → Stage and commit (no push)
|
||||
/git:cp → Stage, commit, and push
|
||||
/git:pr → Create pull request
|
||||
/git:clean → Delete merged branches (local + remote)
|
||||
```
|
||||
|
||||
### Quality Gates
|
||||
|
||||
59
.claude/commands/git/clean.md
Normal file
59
.claude/commands/git/clean.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
description: Clean up merged branches locally and on remote, keeping only main, dev, and gh-pages.
|
||||
---
|
||||
|
||||
Clean up stale branches that have been merged or are no longer needed.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **List local branches** to delete (excluding `main`, `dev`, `gh-pages`):
|
||||
|
||||
```bash
|
||||
git branch | grep -v -E '^\*|main$|dev$|gh-pages$'
|
||||
```
|
||||
|
||||
Report what will be deleted. If no branches found, say "No local branches to clean" and skip to step 3.
|
||||
|
||||
2. **Delete local branches** that are fully merged:
|
||||
|
||||
```bash
|
||||
git branch -d <branch-name>
|
||||
```
|
||||
|
||||
If a branch is not fully merged, report it to the user and ask whether to force-delete. Do NOT force-delete without confirmation.
|
||||
|
||||
3. **List remote branches** to delete (excluding `main`, `dev`, `gh-pages`, `HEAD`):
|
||||
|
||||
```bash
|
||||
git branch -r | grep -v -E 'origin/main$|origin/dev$|origin/gh-pages$|origin/HEAD'
|
||||
```
|
||||
|
||||
Report what will be deleted. If no branches found, say "No remote branches to clean" and skip to step 5.
|
||||
|
||||
4. **Confirm with the user** before deleting remote branches. Show the full list and wait for approval. Then delete:
|
||||
|
||||
```bash
|
||||
git push origin --delete <branch-names>
|
||||
```
|
||||
|
||||
5. **Prune stale remote refs**:
|
||||
|
||||
```bash
|
||||
git remote prune origin
|
||||
```
|
||||
|
||||
6. **Report final state**:
|
||||
|
||||
```bash
|
||||
git branch # local
|
||||
git branch -r # remote
|
||||
```
|
||||
|
||||
Present a summary:
|
||||
|
||||
| Item | Count |
|
||||
|------|-------|
|
||||
| Local branches deleted | N |
|
||||
| Remote branches deleted | N |
|
||||
| Remaining local | main, dev |
|
||||
| Remaining remote | origin/main, origin/dev, origin/gh-pages |
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "claude-code-skills",
|
||||
"description": "Production-ready skill packages for AI agents - Marketing, Engineering, Product, C-Level, PM, and RA/QM",
|
||||
"repository": "https://github.com/alirezarezvani/claude-skills",
|
||||
"total_skills": 150,
|
||||
"total_skills": 151,
|
||||
"skills": [
|
||||
{
|
||||
"name": "contract-and-proposal-writer",
|
||||
@@ -749,6 +749,12 @@
|
||||
"category": "marketing",
|
||||
"description": "When the user wants to develop social media strategy, plan content calendars, manage community engagement, or grow their social presence across platforms. Also use when the user mentions 'social media strategy,' 'social calendar,' 'community management,' 'social media plan,' 'grow followers,' 'engagement rate,' 'social media audit,' or 'which platforms should I use.' For writing individual social posts, see social-content. For analyzing social performance data, see social-media-analyzer."
|
||||
},
|
||||
{
|
||||
"name": "x-twitter-growth",
|
||||
"source": "../../marketing-skill/x-twitter-growth",
|
||||
"category": "marketing",
|
||||
"description": "X/Twitter growth engine for building audience, crafting viral content, and analyzing engagement. Use when the user wants to grow on X/Twitter, write tweets or threads, analyze their X profile, research competitors on X, plan a posting strategy, or optimize engagement. Complements social-content (generic multi-platform) with X-specific depth: algorithm mechanics, thread engineering, reply strategy, profile optimization, and competitive intelligence via web search."
|
||||
},
|
||||
{
|
||||
"name": "agile-product-owner",
|
||||
"source": "../../product-team/agile-product-owner",
|
||||
@@ -933,7 +939,7 @@
|
||||
"description": "Financial analysis, valuation, and forecasting skills"
|
||||
},
|
||||
"marketing": {
|
||||
"count": 42,
|
||||
"count": 43,
|
||||
"source": "../../marketing-skill",
|
||||
"description": "Marketing, content, and demand generation skills"
|
||||
},
|
||||
|
||||
1
.codex/skills/x-twitter-growth
Symbolic link
1
.codex/skills/x-twitter-growth
Symbolic link
@@ -0,0 +1 @@
|
||||
../../marketing-skill/x-twitter-growth
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"name": "gemini-cli-skills",
|
||||
"total_skills": 207,
|
||||
"total_skills": 208,
|
||||
"skills": [
|
||||
{
|
||||
"name": "cs-agile-product-owner",
|
||||
@@ -893,6 +893,11 @@
|
||||
"category": "marketing",
|
||||
"description": "When the user wants to develop social media strategy, plan content calendars, manage community engagement, or grow their social presence across platforms. Also use when the user mentions 'social media strategy,' 'social calendar,' 'community management,' 'social media plan,' 'grow followers,' 'engagement rate,' 'social media audit,' or 'which platforms should I use.' For writing individual social posts, see social-content. For analyzing social performance data, see social-media-analyzer."
|
||||
},
|
||||
{
|
||||
"name": "x-twitter-growth",
|
||||
"category": "marketing",
|
||||
"description": "X/Twitter growth engine for building audience, crafting viral content, and analyzing engagement. Use when the user wants to grow on X/Twitter, write tweets or threads, analyze their X profile, research competitors on X, plan a posting strategy, or optimize engagement. Complements social-content (generic multi-platform) with X-specific depth: algorithm mechanics, thread engineering, reply strategy, profile optimization, and competitive intelligence via web search."
|
||||
},
|
||||
{
|
||||
"name": "agile-product-owner",
|
||||
"category": "product",
|
||||
@@ -1069,7 +1074,7 @@
|
||||
"description": "Finance resources"
|
||||
},
|
||||
"marketing": {
|
||||
"count": 43,
|
||||
"count": 44,
|
||||
"description": "Marketing resources"
|
||||
},
|
||||
"product": {
|
||||
|
||||
1
.gemini/skills/x-twitter-growth/SKILL.md
Symbolic link
1
.gemini/skills/x-twitter-growth/SKILL.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../marketing-skill/x-twitter-growth/SKILL.md
|
||||
21
CLAUDE.md
21
CLAUDE.md
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
This is a **comprehensive skills library** for Claude AI and Claude Code - reusable, production-ready skill packages that bundle domain expertise, best practices, analysis tools, and strategic frameworks. The repository provides modular skills that teams can download and use directly in their workflows.
|
||||
|
||||
**Current Scope:** 171 production-ready skills across 9 domains with 240 Python automation tools, 343 reference guides, 14 agents, and 14 slash commands.
|
||||
**Current Scope:** 172 production-ready skills across 9 domains with 245 Python automation tools, 344 reference guides, 14 agents, and 14 slash commands.
|
||||
|
||||
**Key Distinction**: This is NOT a traditional application. It's a library of skill packages meant to be extracted and deployed by users into their own Claude workflows.
|
||||
|
||||
@@ -41,7 +41,7 @@ claude-code-skills/
|
||||
├── engineering-team/ # 23 core engineering skills + Playwright Pro + Self-Improving Agent
|
||||
├── engineering/ # 25 POWERFUL-tier advanced skills
|
||||
├── product-team/ # 8 product skills + Python tools
|
||||
├── marketing-skill/ # 42 marketing skills (7 pods) + Python tools
|
||||
├── marketing-skill/ # 43 marketing skills (7 pods) + Python tools
|
||||
├── c-level-advisor/ # 28 C-level advisory skills (10 roles + orchestration)
|
||||
├── project-management/ # 6 PM skills + Atlassian MCP
|
||||
├── ra-qm-team/ # 12 RA/QM compliance skills
|
||||
@@ -148,9 +148,9 @@ See [standards/git/git-workflow-standards.md](standards/git/git-workflow-standar
|
||||
|
||||
## Roadmap
|
||||
|
||||
**Phase 1-2 Complete:** 171 production-ready skills deployed across 9 domains
|
||||
**Phase 1-2 Complete:** 172 production-ready skills deployed across 9 domains
|
||||
- Engineering Core (23), Engineering POWERFUL (25), Product (8), Marketing (42), PM (6), C-Level (28), RA/QM (12), Business & Growth (4), Finance (2)
|
||||
- 240 Python automation tools, 343 reference guides, 14 agents, 14 commands
|
||||
- 245 Python automation tools, 344 reference guides, 14 agents, 14 commands
|
||||
- Complete enterprise coverage from engineering through regulatory compliance, sales, customer success, and finance
|
||||
- MkDocs Material docs site with 206 indexed pages for SEO
|
||||
|
||||
@@ -164,6 +164,17 @@ See domain-specific roadmaps in each skill folder's README.md or roadmap files.
|
||||
4. **Template-heavy** - Provide ready-to-use templates users customize
|
||||
5. **Platform-specific** - Specific best practices > generic advice
|
||||
|
||||
## ClawHub Publishing Constraints
|
||||
|
||||
This repository publishes skills to **ClawHub** (clawhub.com) as the distribution registry. The following rules are **non-negotiable**:
|
||||
|
||||
1. **cs- prefix for slug conflicts only.** When a skill slug is already taken on ClawHub by another publisher, publish with the `cs-` prefix (e.g., `cs-copywriting`, `cs-seo-audit`). The `cs-` prefix applies **only on the ClawHub registry** — repo folder names, local skill names, and all other tools (Claude Code, Codex, Gemini CLI) remain unchanged.
|
||||
2. **Never rename repo folders or local skill names** to match ClawHub slugs. The repo is the source of truth.
|
||||
3. **No paid/commercial service dependencies.** Skills must not require paid third-party API keys or commercial services unless provided by the project itself. Free-tier APIs and BYOK (bring-your-own-key) patterns are acceptable.
|
||||
4. **Rate limit: 5 new skills per hour** on ClawHub. Batch publishes must respect this. Use the drip timer (`clawhub-drip.timer`) for bulk operations.
|
||||
5. **plugin.json schema** — ONLY these fields: `name`, `description`, `version`, `author`, `homepage`, `repository`, `license`, `skills: "./"`. No extra fields.
|
||||
6. **Version follows repo versioning.** ClawHub package versions must match the repo release version (currently v2.1.2+).
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- Creating dependencies between skills (keep each self-contained)
|
||||
@@ -192,4 +203,4 @@ See domain-specific roadmaps in each skill folder's README.md or roadmap files.
|
||||
|
||||
**Last Updated:** March 10, 2026
|
||||
**Version:** v2.1.2
|
||||
**Status:** 171 skills deployed across 9 domains, 19 marketplace plugins, docs site live
|
||||
**Status:** 172 skills deployed across 9 domains, 19 marketplace plugins, docs site live
|
||||
|
||||
@@ -22,6 +22,14 @@ Skills are organized into domain folders. Each skill is a directory containing a
|
||||
| **Business & Growth** | `business-growth/` |
|
||||
| **Finance** | `finance/` |
|
||||
|
||||
### ClawHub Publishing Constraints
|
||||
|
||||
When skills are published to **ClawHub** (clawhub.com):
|
||||
- **cs- prefix for slug conflicts only** — applies only on the ClawHub registry when another publisher already owns the slug. Repo folder names and local skill names are never renamed.
|
||||
- **No paid/commercial service dependencies** — skills must not require paid third-party API keys or commercial services unless provided by the project itself.
|
||||
- **plugin.json** — ONLY fields: `name`, `description`, `version`, `author`, `homepage`, `repository`, `license`, `skills: "./"`.
|
||||
- **Rate limit:** 5 new skills/hour on ClawHub. Use drip publishing for bulk operations.
|
||||
|
||||
### Activating a Skill
|
||||
|
||||
To activate a skill, use the folder name. For example:
|
||||
|
||||
14
README.md
14
README.md
@@ -1,9 +1,9 @@
|
||||
# Claude Code Skills & Plugins
|
||||
|
||||
**171 production-ready skills and plugins for Claude Code, OpenAI Codex, Gemini CLI, and OpenClaw** — reusable expertise bundles that transform AI coding agents into specialized professionals across engineering, product, marketing, compliance, and more.
|
||||
**172 production-ready skills and plugins for Claude Code, OpenAI Codex, Gemini CLI, and OpenClaw** — reusable expertise bundles that transform AI coding agents into specialized professionals across engineering, product, marketing, compliance, and more.
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](#skills-overview)
|
||||
[](#skills-overview)
|
||||
[](#agents)
|
||||
[](#commands)
|
||||
[](https://github.com/alirezarezvani/claude-skills/stargazers)
|
||||
@@ -47,7 +47,7 @@ cd claude-skills
|
||||
/plugin install engineering-skills@claude-code-skills # 23 core engineering
|
||||
/plugin install engineering-advanced-skills@claude-code-skills # 25 POWERFUL-tier
|
||||
/plugin install product-skills@claude-code-skills # 8 product skills
|
||||
/plugin install marketing-skills@claude-code-skills # 42 marketing skills
|
||||
/plugin install marketing-skills@claude-code-skills # 43 marketing skills
|
||||
/plugin install ra-qm-skills@claude-code-skills # 12 regulatory/quality
|
||||
/plugin install pm-skills@claude-code-skills # 6 project management
|
||||
/plugin install c-level-skills@claude-code-skills # 28 C-level advisory (full C-suite)
|
||||
@@ -85,7 +85,7 @@ git clone https://github.com/alirezarezvani/claude-skills.git
|
||||
|
||||
## Skills Overview
|
||||
|
||||
**171 skills across 9 domains:**
|
||||
**172 skills across 9 domains:**
|
||||
|
||||
| Domain | Skills | Highlights | Details |
|
||||
|--------|--------|------------|---------|
|
||||
@@ -94,7 +94,7 @@ git clone https://github.com/alirezarezvani/claude-skills.git
|
||||
| **🧠 Self-Improving Agent** | 5+2 | Auto-memory curation, pattern promotion, skill extraction, memory health | [engineering-team/self-improving-agent](engineering-team/self-improving-agent/) |
|
||||
| **⚡ Engineering — POWERFUL** | 25 | Agent designer, RAG architect, database designer, CI/CD builder, security auditor, MCP builder | [engineering/](engineering/) |
|
||||
| **🎯 Product** | 8 | Product manager, agile PO, strategist, UX researcher, UI design, landing pages (TSX + Tailwind), SaaS scaffolder | [product-team/](product-team/) |
|
||||
| **📣 Marketing** | 42 | 7 pods: Content (8), SEO (5), CRO (6), Channels (5), Growth (4), Intelligence (4), Sales (2) + context foundation + orchestration router. 27 Python tools. | [marketing-skill/](marketing-skill/) |
|
||||
| **📣 Marketing** | 43 | 7 pods: Content (8), SEO (5), CRO (6), Channels (6), Growth (4), Intelligence (4), Sales (2) + context foundation + orchestration router. 32 Python tools. | [marketing-skill/](marketing-skill/) |
|
||||
| **📋 Project Management** | 6 | Senior PM, scrum master, Jira, Confluence, Atlassian admin, templates | [project-management/](project-management/) |
|
||||
| **🏥 Regulatory & QM** | 12 | ISO 13485, MDR 2017/745, FDA, ISO 27001, GDPR, CAPA, risk management | [ra-qm-team/](ra-qm-team/) |
|
||||
| **💼 C-Level Advisory** | 28 | Full C-suite (10 roles) + orchestration + board meetings + culture & collaboration | [c-level-advisor/](c-level-advisor/) |
|
||||
@@ -189,7 +189,7 @@ for MDR Annex II compliance gaps.
|
||||
|
||||
## Python Analysis Tools
|
||||
|
||||
240 CLI tools ship with the skills (all verified, stdlib-only):
|
||||
245 CLI tools ship with the skills (all verified, stdlib-only):
|
||||
|
||||
```bash
|
||||
# SaaS health check
|
||||
@@ -235,7 +235,7 @@ Yes. Skills work natively with Claude Code, OpenAI Codex, Gemini CLI, and OpenCl
|
||||
No. We follow semantic versioning and maintain backward compatibility within patch releases. Existing script arguments, plugin source paths, and SKILL.md structures are never changed in patch versions. See the [CHANGELOG](CHANGELOG.md) for details on each release.
|
||||
|
||||
**Are the Python tools dependency-free?**
|
||||
Yes. All 240 Python CLI tools use the standard library only — zero pip installs required. Every script is verified to run with `--help`.
|
||||
Yes. All 245 Python CLI tools use the standard library only — zero pip installs required. Every script is verified to run with `--help`.
|
||||
|
||||
**How do I create my own Claude Code skill?**
|
||||
Each skill is a folder with a `SKILL.md` (frontmatter + instructions), optional `scripts/`, `references/`, and `assets/`. See the [Skills & Agents Factory](https://github.com/alirezarezvani/claude-code-skills-agents-factory) for a step-by-step guide.
|
||||
|
||||
@@ -14,6 +14,14 @@ This guide provides comprehensive instructions for creating **cs-* prefixed agen
|
||||
|
||||
**Key Principle**: Agents ORCHESTRATE skills, they don't replace them. Skills remain self-contained and portable.
|
||||
|
||||
### ClawHub Publishing Constraints
|
||||
|
||||
When skills are published to **ClawHub** (clawhub.com):
|
||||
- **cs- prefix for slug conflicts only** — applies only on the ClawHub registry when another publisher already owns the slug. Repo folder names and local skill names are never renamed.
|
||||
- **No paid/commercial service dependencies** — skills must not require paid third-party API keys or commercial services unless provided by the project itself.
|
||||
- **plugin.json** — ONLY fields: `name`, `description`, `version`, `author`, `homepage`, `repository`, `license`, `skills: "./"`.
|
||||
- **Rate limit:** 5 new skills/hour on ClawHub. Use drip publishing for bulk operations.
|
||||
|
||||
### Production Agents
|
||||
|
||||
**5 Agents Currently Available** (as of November 5, 2025):
|
||||
|
||||
@@ -73,7 +73,7 @@ Choose your platform and follow the steps:
|
||||
| **Engineering Core** | `/plugin install engineering-skills@claude-code-skills` | 23 |
|
||||
| **Engineering POWERFUL** | `/plugin install engineering-advanced-skills@claude-code-skills` | 25 |
|
||||
| **Product** | `/plugin install product-skills@claude-code-skills` | 8 |
|
||||
| **Marketing** | `/plugin install marketing-skills@claude-code-skills` | 42 |
|
||||
| **Marketing** | `/plugin install marketing-skills@claude-code-skills` | 43 |
|
||||
| **Regulatory & Quality** | `/plugin install ra-qm-skills@claude-code-skills` | 12 |
|
||||
| **Project Management** | `/plugin install pm-skills@claude-code-skills` | 6 |
|
||||
| **C-Level Advisory** | `/plugin install c-level-skills@claude-code-skills` | 28 |
|
||||
@@ -112,7 +112,7 @@ AI-augmented development. Optimize for SEO.
|
||||
|
||||
## Python Tools
|
||||
|
||||
All 240 tools use the standard library only — zero pip installs, all verified.
|
||||
All 245 tools use the standard library only — zero pip installs, all verified.
|
||||
|
||||
```bash
|
||||
# Security audit a skill before installing
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Claude Code Skills & Plugins
|
||||
description: "171 production-ready skills, 14 agents, and 14 commands for Claude Code, OpenAI Codex, Gemini CLI, and OpenClaw."
|
||||
description: "172 production-ready skills, 14 agents, and 14 commands for Claude Code, OpenAI Codex, Gemini CLI, and OpenClaw."
|
||||
hide:
|
||||
- toc
|
||||
- edit
|
||||
@@ -14,7 +14,7 @@ hide:
|
||||
|
||||
# Claude Code Skills
|
||||
|
||||
171 production-ready skills that transform AI coding agents into specialized professionals.
|
||||
172 production-ready skills that transform AI coding agents into specialized professionals.
|
||||
{ .hero-subtitle }
|
||||
|
||||
**Claude Code** | **OpenAI Codex** | **Gemini CLI** | **OpenClaw**
|
||||
@@ -27,13 +27,13 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-counter:{ .lg .middle } **171**
|
||||
- :material-counter:{ .lg .middle } **172**
|
||||
|
||||
---
|
||||
|
||||
Production-ready skills
|
||||
|
||||
- :material-language-python:{ .lg .middle } **240**
|
||||
- :material-language-python:{ .lg .middle } **245**
|
||||
|
||||
---
|
||||
|
||||
@@ -87,9 +87,9 @@ hide:
|
||||
|
||||
---
|
||||
|
||||
Content, SEO, CRO, channels, growth, intelligence, sales — 7 specialist pods with 27 Python tools
|
||||
Content, SEO, CRO, channels, growth, intelligence, sales, X/Twitter — 7 specialist pods with 32 Python tools
|
||||
|
||||
[:octicons-arrow-right-24: 42 skills](skills/marketing-skill/)
|
||||
[:octicons-arrow-right-24: 43 skills](skills/marketing-skill/)
|
||||
|
||||
- :material-clipboard-check:{ .lg .middle } **Project Management**
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ description: "All Marketing skills for Claude Code, OpenAI Codex, and OpenClaw."
|
||||
|
||||
# Marketing Skills
|
||||
|
||||
43 skills in this domain.
|
||||
44 skills in this domain.
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
@@ -52,3 +52,4 @@ description: "All Marketing skills for Claude Code, OpenAI Codex, and OpenClaw."
|
||||
| [Social Content](social-content.md) | `social-content` |
|
||||
| [Social Media Analyzer](social-media-analyzer.md) | `social-media-analyzer` |
|
||||
| [Social Media Manager](social-media-manager.md) | `social-media-manager` |
|
||||
| [X/Twitter Growth Engine](x-twitter-growth.md) | `x-twitter-growth` |
|
||||
|
||||
227
docs/skills/marketing-skill/x-twitter-growth.md
Normal file
227
docs/skills/marketing-skill/x-twitter-growth.md
Normal file
@@ -0,0 +1,227 @@
|
||||
---
|
||||
title: "X/Twitter Growth Engine"
|
||||
description: "X/Twitter Growth Engine - Claude Code skill from the Marketing domain."
|
||||
---
|
||||
|
||||
# X/Twitter Growth Engine
|
||||
|
||||
**Domain:** Marketing | **Skill:** `x-twitter-growth` | **Source:** [`marketing-skill/x-twitter-growth/SKILL.md`](https://github.com/alirezarezvani/claude-skills/tree/main/marketing-skill/x-twitter-growth/SKILL.md)
|
||||
|
||||
---
|
||||
|
||||
|
||||
# X/Twitter Growth Engine
|
||||
|
||||
X-specific growth skill. For general social media content across platforms, see `social-content`. For social strategy and calendar planning, see `social-media-manager`. This skill goes deep on X.
|
||||
|
||||
## When to Use This vs Other Skills
|
||||
|
||||
| Need | Use |
|
||||
|------|-----|
|
||||
| Write a tweet or thread | **This skill** |
|
||||
| Plan content across LinkedIn + X + Instagram | social-content |
|
||||
| Analyze engagement metrics across platforms | social-media-analyzer |
|
||||
| Build overall social strategy | social-media-manager |
|
||||
| X-specific growth, algorithm, competitive intel | **This skill** |
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Profile Audit
|
||||
|
||||
Before any growth work, audit the current X presence. Run `scripts/profile_auditor.py` with the handle, or manually assess:
|
||||
|
||||
### Bio Checklist
|
||||
- [ ] Clear value proposition in first line (who you help + how)
|
||||
- [ ] Specific niche — not "entrepreneur | thinker | builder"
|
||||
- [ ] Social proof element (followers, title, metric, brand)
|
||||
- [ ] CTA or link (newsletter, product, site)
|
||||
- [ ] No hashtags in bio (signals amateur)
|
||||
|
||||
### Pinned Tweet
|
||||
- [ ] Exists and is less than 30 days old
|
||||
- [ ] Showcases best work or strongest hook
|
||||
- [ ] Has clear CTA (follow, subscribe, read)
|
||||
|
||||
### Recent Activity (last 30 posts)
|
||||
- [ ] Posting frequency: minimum 1x/day, ideal 3-5x/day
|
||||
- [ ] Mix of formats: tweets, threads, replies, quotes
|
||||
- [ ] Reply ratio: >30% of activity should be replies
|
||||
- [ ] Engagement trend: improving, flat, or declining
|
||||
|
||||
Run: `python3 scripts/profile_auditor.py --handle @username`
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Competitive Intelligence
|
||||
|
||||
Research competitors and successful accounts in your niche using web search.
|
||||
|
||||
### Process
|
||||
1. Search `site:x.com "topic" min_faves:100` via Brave to find high-performing content
|
||||
2. Identify 5-10 accounts in your niche with strong engagement
|
||||
3. For each, analyze: posting frequency, content types, hook patterns, engagement rates
|
||||
4. Run: `python3 scripts/competitor_analyzer.py --handles @acc1 @acc2 @acc3`
|
||||
|
||||
### What to Extract
|
||||
- **Hook patterns** — How do top posts start? Question? Bold claim? Statistic?
|
||||
- **Content themes** — What 3-5 topics get the most engagement?
|
||||
- **Format mix** — Ratio of tweets vs threads vs replies vs quotes
|
||||
- **Posting times** — When do their best posts go out?
|
||||
- **Engagement triggers** — What makes people reply vs like vs retweet?
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Content Creation
|
||||
|
||||
### Tweet Types (ordered by growth impact)
|
||||
|
||||
#### 1. Threads (highest reach, highest follow conversion)
|
||||
```
|
||||
Structure:
|
||||
- Tweet 1: Hook — must stop the scroll in <7 words
|
||||
- Tweet 2: Context or promise ("Here's what I learned:")
|
||||
- Tweets 3-N: One idea per tweet, each standalone-worthy
|
||||
- Final tweet: Summary + explicit CTA ("Follow @handle for more")
|
||||
- Reply to tweet 1: Restate hook + "Follow for more [topic]"
|
||||
|
||||
Rules:
|
||||
- 5-12 tweets optimal (under 5 feels thin, over 12 loses people)
|
||||
- Each tweet should make sense if read alone
|
||||
- Use line breaks for readability
|
||||
- No tweet should be a wall of text (3-4 lines max)
|
||||
- Number the tweets or use "↓" in tweet 1
|
||||
```
|
||||
|
||||
#### 2. Atomic Tweets (breadth, impression farming)
|
||||
```
|
||||
Formats that work:
|
||||
- Observation: "[Thing] is underrated. Here's why:"
|
||||
- Listicle: "10 tools I use daily:\n\n1. X — for Y"
|
||||
- Contrarian: "Unpopular opinion: [statement]"
|
||||
- Lesson: "I [did X] for [time]. Biggest lesson:"
|
||||
- Framework: "[Concept] explained in 30 seconds:"
|
||||
|
||||
Rules:
|
||||
- Under 200 characters gets more engagement
|
||||
- One idea per tweet
|
||||
- No links in tweet body (kills reach — put link in reply)
|
||||
- Question tweets drive replies (algorithm loves replies)
|
||||
```
|
||||
|
||||
#### 3. Quote Tweets (authority building)
|
||||
```
|
||||
Formula: Original tweet + your unique take
|
||||
- Add data the original missed
|
||||
- Provide counterpoint or nuance
|
||||
- Share personal experience that validates/contradicts
|
||||
- Never just say "This" or "So true"
|
||||
```
|
||||
|
||||
#### 4. Replies (network growth, fastest path to visibility)
|
||||
```
|
||||
Strategy:
|
||||
- Reply to accounts 2-10x your size
|
||||
- Add genuine value, not "great post!"
|
||||
- Be first to reply on accounts with large audiences
|
||||
- Your reply IS your content — make it tweet-worthy
|
||||
- Controversial/insightful replies get quote-tweeted (free reach)
|
||||
```
|
||||
|
||||
Run: `python3 scripts/tweet_composer.py --type thread --topic "your topic" --audience "your audience"`
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Algorithm Mechanics
|
||||
|
||||
### What X rewards (2025-2026)
|
||||
| Signal | Weight | Action |
|
||||
|--------|--------|--------|
|
||||
| Replies received | Very high | Write reply-worthy content (questions, debates) |
|
||||
| Time spent reading | High | Threads, longer tweets with line breaks |
|
||||
| Profile visits from tweet | High | Curiosity gaps, tease expertise |
|
||||
| Bookmarks | High | Tactical, save-worthy content (lists, frameworks) |
|
||||
| Retweets/Quotes | Medium | Shareable insights, bold takes |
|
||||
| Likes | Low-medium | Easy agreement, relatable content |
|
||||
| Link clicks | Low (penalized) | Never put links in tweet body — use reply |
|
||||
|
||||
### What kills reach
|
||||
- Links in tweet body (put in first reply instead)
|
||||
- Editing tweets within 30 min of posting
|
||||
- Posting and immediately going offline (no early engagement)
|
||||
- More than 2 hashtags
|
||||
- Tagging people who don't engage back
|
||||
- Threads with inconsistent quality (one weak tweet tanks the whole thread)
|
||||
|
||||
### Optimal Posting Cadence
|
||||
| Account size | Tweets/day | Threads/week | Replies/day |
|
||||
|-------------|------------|--------------|-------------|
|
||||
| < 1K followers | 2-3 | 1-2 | 10-20 |
|
||||
| 1K-10K | 3-5 | 2-3 | 5-15 |
|
||||
| 10K-50K | 3-7 | 2-4 | 5-10 |
|
||||
| 50K+ | 2-5 | 1-3 | 5-10 |
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Growth Playbook
|
||||
|
||||
### Week 1-2: Foundation
|
||||
1. Optimize bio and pinned tweet (Step 1)
|
||||
2. Identify 20 accounts in your niche to engage with daily
|
||||
3. Reply 10-20 times per day to larger accounts (genuine value only)
|
||||
4. Post 2-3 atomic tweets per day testing different formats
|
||||
5. Publish 1 thread
|
||||
|
||||
### Week 3-4: Pattern Recognition
|
||||
1. Review what formats got most engagement
|
||||
2. Double down on top 2 content formats
|
||||
3. Increase to 3-5 posts per day
|
||||
4. Publish 2-3 threads per week
|
||||
5. Start quote-tweeting relevant content daily
|
||||
|
||||
### Month 2+: Scale
|
||||
1. Develop 3-5 recurring content series (e.g., "Friday Framework")
|
||||
2. Cross-pollinate: repurpose threads as LinkedIn posts, newsletter content
|
||||
3. Build reply relationships with 5-10 accounts your size (mutual engagement)
|
||||
4. Experiment with spaces/audio if relevant to niche
|
||||
5. Run: `python3 scripts/growth_tracker.py --handle @username --period 30d`
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Content Calendar Generation
|
||||
|
||||
Run: `python3 scripts/content_planner.py --niche "your niche" --frequency 5 --weeks 2`
|
||||
|
||||
Generates a 2-week posting plan with:
|
||||
- Daily tweet topics with hook suggestions
|
||||
- Thread outlines (2-3 per week)
|
||||
- Reply targets (accounts to engage with)
|
||||
- Optimal posting times based on niche
|
||||
|
||||
---
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `scripts/profile_auditor.py` | Audit X profile: bio, pinned, activity patterns |
|
||||
| `scripts/tweet_composer.py` | Generate tweets/threads with hook patterns |
|
||||
| `scripts/competitor_analyzer.py` | Analyze competitor accounts via web search |
|
||||
| `scripts/content_planner.py` | Generate weekly/monthly content calendars |
|
||||
| `scripts/growth_tracker.py` | Track follower growth and engagement trends |
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Posting links directly** — Always put links in the first reply, never in the tweet body
|
||||
2. **Thread tweet 1 is weak** — If the hook doesn't stop scrolling, nothing else matters
|
||||
3. **Inconsistent posting** — Algorithm rewards daily consistency over occasional bangers
|
||||
4. **Only broadcasting** — Replies and engagement are 50%+ of growth, not just posting
|
||||
5. **Generic bio** — "Helping people do things" tells nobody anything
|
||||
6. **Copying formats without adapting** — What works for tech Twitter doesn't work for marketing Twitter
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `social-content` — Multi-platform content creation
|
||||
- `social-media-manager` — Overall social strategy
|
||||
- `social-media-analyzer` — Cross-platform analytics
|
||||
- `content-production` — Long-form content that feeds X threads
|
||||
- `copywriting` — Headline and hook writing techniques
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "marketing-skills",
|
||||
"description": "42 production-ready marketing skills across 7 pods: Content (copywriting, content strategy, content production), SEO (audits, schema markup, programmatic SEO, site architecture), CRO (A/B testing, forms, popups, signup flows, pricing, onboarding), Channels (email sequences, social media, paid ads, cold email), Growth (launch strategy, referral programs, free tools), Intelligence (competitor analysis, marketing psychology, analytics tracking), and Sales enablement",
|
||||
"description": "43 production-ready marketing skills across 7 pods: Content (copywriting, content strategy, content production), SEO (audits, schema markup, programmatic SEO, site architecture), CRO (A/B testing, forms, popups, signup flows, pricing, onboarding), Channels (email sequences, social media, paid ads, cold email, X/Twitter growth), Growth (launch strategy, referral programs, free tools), Intelligence (competitor analysis, marketing psychology, analytics tracking), and Sales enablement",
|
||||
"version": "2.1.2",
|
||||
"author": {
|
||||
"name": "Alireza Rezvani",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## For All Agents (Claude Code, Codex CLI, OpenClaw)
|
||||
|
||||
This directory contains 42 marketing skills organized into specialist pods.
|
||||
This directory contains 43 marketing skills organized into specialist pods.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -21,10 +21,11 @@ This directory contains 42 marketing skills organized into specialist pods.
|
||||
- `page-cro/` — Conversion rate optimization
|
||||
- `pricing-strategy/` — Pricing and packaging
|
||||
- `content-humanizer/` — Fix AI-sounding content
|
||||
- `x-twitter-growth/` — X/Twitter audience growth, tweet composing, competitor analysis
|
||||
|
||||
### Python Tools
|
||||
|
||||
27 scripts, all stdlib-only. Run directly:
|
||||
32 scripts, all stdlib-only. Run directly:
|
||||
```bash
|
||||
python3 <skill>/scripts/<tool>.py [args]
|
||||
```
|
||||
@@ -32,7 +33,7 @@ No pip install needed. Scripts include embedded samples for demo mode (run with
|
||||
|
||||
### Anti-Patterns
|
||||
|
||||
❌ Don't read all 42 SKILL.md files
|
||||
❌ Don't read all 43 SKILL.md files
|
||||
❌ Don't skip marketing-context.md if it exists
|
||||
❌ Don't use content-creator (deprecated → use content-production)
|
||||
❌ Don't install pip packages for Python tools
|
||||
|
||||
1
marketing-skill/x-twitter-growth/.gitignore
vendored
Normal file
1
marketing-skill/x-twitter-growth/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.growth-data/
|
||||
226
marketing-skill/x-twitter-growth/SKILL.md
Normal file
226
marketing-skill/x-twitter-growth/SKILL.md
Normal file
@@ -0,0 +1,226 @@
|
||||
---
|
||||
name: "x-twitter-growth"
|
||||
description: "X/Twitter growth engine for building audience, crafting viral content, and analyzing engagement. Use when the user wants to grow on X/Twitter, write tweets or threads, analyze their X profile, research competitors on X, plan a posting strategy, or optimize engagement. Complements social-content (generic multi-platform) with X-specific depth: algorithm mechanics, thread engineering, reply strategy, profile optimization, and competitive intelligence via web search."
|
||||
license: MIT
|
||||
metadata:
|
||||
version: 1.0.0
|
||||
author: Alireza Rezvani
|
||||
category: marketing
|
||||
updated: 2026-03-10
|
||||
---
|
||||
|
||||
# X/Twitter Growth Engine
|
||||
|
||||
X-specific growth skill. For general social media content across platforms, see `social-content`. For social strategy and calendar planning, see `social-media-manager`. This skill goes deep on X.
|
||||
|
||||
## When to Use This vs Other Skills
|
||||
|
||||
| Need | Use |
|
||||
|------|-----|
|
||||
| Write a tweet or thread | **This skill** |
|
||||
| Plan content across LinkedIn + X + Instagram | social-content |
|
||||
| Analyze engagement metrics across platforms | social-media-analyzer |
|
||||
| Build overall social strategy | social-media-manager |
|
||||
| X-specific growth, algorithm, competitive intel | **This skill** |
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Profile Audit
|
||||
|
||||
Before any growth work, audit the current X presence. Run `scripts/profile_auditor.py` with the handle, or manually assess:
|
||||
|
||||
### Bio Checklist
|
||||
- [ ] Clear value proposition in first line (who you help + how)
|
||||
- [ ] Specific niche — not "entrepreneur | thinker | builder"
|
||||
- [ ] Social proof element (followers, title, metric, brand)
|
||||
- [ ] CTA or link (newsletter, product, site)
|
||||
- [ ] No hashtags in bio (signals amateur)
|
||||
|
||||
### Pinned Tweet
|
||||
- [ ] Exists and is less than 30 days old
|
||||
- [ ] Showcases best work or strongest hook
|
||||
- [ ] Has clear CTA (follow, subscribe, read)
|
||||
|
||||
### Recent Activity (last 30 posts)
|
||||
- [ ] Posting frequency: minimum 1x/day, ideal 3-5x/day
|
||||
- [ ] Mix of formats: tweets, threads, replies, quotes
|
||||
- [ ] Reply ratio: >30% of activity should be replies
|
||||
- [ ] Engagement trend: improving, flat, or declining
|
||||
|
||||
Run: `python3 scripts/profile_auditor.py --handle @username`
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Competitive Intelligence
|
||||
|
||||
Research competitors and successful accounts in your niche using web search.
|
||||
|
||||
### Process
|
||||
1. Search `site:x.com "topic" min_faves:100` via Brave to find high-performing content
|
||||
2. Identify 5-10 accounts in your niche with strong engagement
|
||||
3. For each, analyze: posting frequency, content types, hook patterns, engagement rates
|
||||
4. Run: `python3 scripts/competitor_analyzer.py --handles @acc1 @acc2 @acc3`
|
||||
|
||||
### What to Extract
|
||||
- **Hook patterns** — How do top posts start? Question? Bold claim? Statistic?
|
||||
- **Content themes** — What 3-5 topics get the most engagement?
|
||||
- **Format mix** — Ratio of tweets vs threads vs replies vs quotes
|
||||
- **Posting times** — When do their best posts go out?
|
||||
- **Engagement triggers** — What makes people reply vs like vs retweet?
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Content Creation
|
||||
|
||||
### Tweet Types (ordered by growth impact)
|
||||
|
||||
#### 1. Threads (highest reach, highest follow conversion)
|
||||
```
|
||||
Structure:
|
||||
- Tweet 1: Hook — must stop the scroll in <7 words
|
||||
- Tweet 2: Context or promise ("Here's what I learned:")
|
||||
- Tweets 3-N: One idea per tweet, each standalone-worthy
|
||||
- Final tweet: Summary + explicit CTA ("Follow @handle for more")
|
||||
- Reply to tweet 1: Restate hook + "Follow for more [topic]"
|
||||
|
||||
Rules:
|
||||
- 5-12 tweets optimal (under 5 feels thin, over 12 loses people)
|
||||
- Each tweet should make sense if read alone
|
||||
- Use line breaks for readability
|
||||
- No tweet should be a wall of text (3-4 lines max)
|
||||
- Number the tweets or use "↓" in tweet 1
|
||||
```
|
||||
|
||||
#### 2. Atomic Tweets (breadth, impression farming)
|
||||
```
|
||||
Formats that work:
|
||||
- Observation: "[Thing] is underrated. Here's why:"
|
||||
- Listicle: "10 tools I use daily:\n\n1. X — for Y"
|
||||
- Contrarian: "Unpopular opinion: [statement]"
|
||||
- Lesson: "I [did X] for [time]. Biggest lesson:"
|
||||
- Framework: "[Concept] explained in 30 seconds:"
|
||||
|
||||
Rules:
|
||||
- Under 200 characters gets more engagement
|
||||
- One idea per tweet
|
||||
- No links in tweet body (kills reach — put link in reply)
|
||||
- Question tweets drive replies (algorithm loves replies)
|
||||
```
|
||||
|
||||
#### 3. Quote Tweets (authority building)
|
||||
```
|
||||
Formula: Original tweet + your unique take
|
||||
- Add data the original missed
|
||||
- Provide counterpoint or nuance
|
||||
- Share personal experience that validates/contradicts
|
||||
- Never just say "This" or "So true"
|
||||
```
|
||||
|
||||
#### 4. Replies (network growth, fastest path to visibility)
|
||||
```
|
||||
Strategy:
|
||||
- Reply to accounts 2-10x your size
|
||||
- Add genuine value, not "great post!"
|
||||
- Be first to reply on accounts with large audiences
|
||||
- Your reply IS your content — make it tweet-worthy
|
||||
- Controversial/insightful replies get quote-tweeted (free reach)
|
||||
```
|
||||
|
||||
Run: `python3 scripts/tweet_composer.py --type thread --topic "your topic" --audience "your audience"`
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Algorithm Mechanics
|
||||
|
||||
### What X rewards (2025-2026)
|
||||
| Signal | Weight | Action |
|
||||
|--------|--------|--------|
|
||||
| Replies received | Very high | Write reply-worthy content (questions, debates) |
|
||||
| Time spent reading | High | Threads, longer tweets with line breaks |
|
||||
| Profile visits from tweet | High | Curiosity gaps, tease expertise |
|
||||
| Bookmarks | High | Tactical, save-worthy content (lists, frameworks) |
|
||||
| Retweets/Quotes | Medium | Shareable insights, bold takes |
|
||||
| Likes | Low-medium | Easy agreement, relatable content |
|
||||
| Link clicks | Low (penalized) | Never put links in tweet body — use reply |
|
||||
|
||||
### What kills reach
|
||||
- Links in tweet body (put in first reply instead)
|
||||
- Editing tweets within 30 min of posting
|
||||
- Posting and immediately going offline (no early engagement)
|
||||
- More than 2 hashtags
|
||||
- Tagging people who don't engage back
|
||||
- Threads with inconsistent quality (one weak tweet tanks the whole thread)
|
||||
|
||||
### Optimal Posting Cadence
|
||||
| Account size | Tweets/day | Threads/week | Replies/day |
|
||||
|-------------|------------|--------------|-------------|
|
||||
| < 1K followers | 2-3 | 1-2 | 10-20 |
|
||||
| 1K-10K | 3-5 | 2-3 | 5-15 |
|
||||
| 10K-50K | 3-7 | 2-4 | 5-10 |
|
||||
| 50K+ | 2-5 | 1-3 | 5-10 |
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Growth Playbook
|
||||
|
||||
### Week 1-2: Foundation
|
||||
1. Optimize bio and pinned tweet (Step 1)
|
||||
2. Identify 20 accounts in your niche to engage with daily
|
||||
3. Reply 10-20 times per day to larger accounts (genuine value only)
|
||||
4. Post 2-3 atomic tweets per day testing different formats
|
||||
5. Publish 1 thread
|
||||
|
||||
### Week 3-4: Pattern Recognition
|
||||
1. Review what formats got most engagement
|
||||
2. Double down on top 2 content formats
|
||||
3. Increase to 3-5 posts per day
|
||||
4. Publish 2-3 threads per week
|
||||
5. Start quote-tweeting relevant content daily
|
||||
|
||||
### Month 2+: Scale
|
||||
1. Develop 3-5 recurring content series (e.g., "Friday Framework")
|
||||
2. Cross-pollinate: repurpose threads as LinkedIn posts, newsletter content
|
||||
3. Build reply relationships with 5-10 accounts your size (mutual engagement)
|
||||
4. Experiment with spaces/audio if relevant to niche
|
||||
5. Run: `python3 scripts/growth_tracker.py --handle @username --period 30d`
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Content Calendar Generation
|
||||
|
||||
Run: `python3 scripts/content_planner.py --niche "your niche" --frequency 5 --weeks 2`
|
||||
|
||||
Generates a 2-week posting plan with:
|
||||
- Daily tweet topics with hook suggestions
|
||||
- Thread outlines (2-3 per week)
|
||||
- Reply targets (accounts to engage with)
|
||||
- Optimal posting times based on niche
|
||||
|
||||
---
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `scripts/profile_auditor.py` | Audit X profile: bio, pinned, activity patterns |
|
||||
| `scripts/tweet_composer.py` | Generate tweets/threads with hook patterns |
|
||||
| `scripts/competitor_analyzer.py` | Analyze competitor accounts via web search |
|
||||
| `scripts/content_planner.py` | Generate weekly/monthly content calendars |
|
||||
| `scripts/growth_tracker.py` | Track follower growth and engagement trends |
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Posting links directly** — Always put links in the first reply, never in the tweet body
|
||||
2. **Thread tweet 1 is weak** — If the hook doesn't stop scrolling, nothing else matters
|
||||
3. **Inconsistent posting** — Algorithm rewards daily consistency over occasional bangers
|
||||
4. **Only broadcasting** — Replies and engagement are 50%+ of growth, not just posting
|
||||
5. **Generic bio** — "Helping people do things" tells nobody anything
|
||||
6. **Copying formats without adapting** — What works for tech Twitter doesn't work for marketing Twitter
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `social-content` — Multi-platform content creation
|
||||
- `social-media-manager` — Overall social strategy
|
||||
- `social-media-analyzer` — Cross-platform analytics
|
||||
- `content-production` — Long-form content that feeds X threads
|
||||
- `copywriting` — Headline and hook writing techniques
|
||||
@@ -0,0 +1,70 @@
|
||||
# X/Twitter Algorithm Signals (2025-2026)
|
||||
|
||||
## Ranking Factors by Weight
|
||||
|
||||
### Tier 1 — Strongest Signals
|
||||
| Signal | Impact | How to Optimize |
|
||||
|--------|--------|----------------|
|
||||
| Replies received | Very high | Ask questions, make controversial/insightful points |
|
||||
| Dwell time (time reading) | Very high | Threads, longer tweets with line breaks |
|
||||
| Profile clicks from tweet | High | Create curiosity gaps, tease expertise |
|
||||
| Bookmarks | High | Tactical content (lists, frameworks, templates) |
|
||||
|
||||
### Tier 2 — Moderate Signals
|
||||
| Signal | Impact | How to Optimize |
|
||||
|--------|--------|----------------|
|
||||
| Retweets/Quotes | Medium | Shareable insights, bold takes, data |
|
||||
| Likes | Medium-low | Easy agreement, relatable content |
|
||||
| Follows from tweet | Medium | Thread CTAs, high-value niche content |
|
||||
|
||||
### Tier 3 — Negative Signals
|
||||
| Signal | Impact | How to Avoid |
|
||||
|--------|--------|-------------|
|
||||
| Link in tweet body | Reach penalty | Put links in first reply |
|
||||
| Edit within 30 min | Suppresses | Don't edit — delete and repost if needed |
|
||||
| Low early engagement | Decay | Stay online 30 min after posting, engage with replies |
|
||||
| Hashtag spam (3+) | Spam signal | Max 1-2 hashtags, or zero |
|
||||
| Tagging non-engagers | Negative | Only tag people likely to engage |
|
||||
|
||||
## Content Format Performance (ranked)
|
||||
|
||||
1. **Threads** — Highest reach potential, best for follower conversion
|
||||
2. **Image tweets** — 2-3x engagement vs text-only
|
||||
3. **Quote tweets** — Network effect (appear in two audiences)
|
||||
4. **Text tweets** — Baseline, best for hot takes and questions
|
||||
5. **Polls** — High engagement but low follower conversion
|
||||
6. **Link tweets** — Lowest reach (algorithm penalizes external links)
|
||||
|
||||
## Optimal Timing
|
||||
|
||||
| Time Slot (UTC) | Why |
|
||||
|----------------|-----|
|
||||
| 12:00-14:00 | US East Coast morning, EU afternoon |
|
||||
| 16:00-18:00 | US afternoon peak |
|
||||
| 21:00-23:00 | US evening, high scroll time |
|
||||
| 07:00-08:00 | EU morning, commute scrolling |
|
||||
|
||||
Best days: Tuesday-Thursday for B2B. Saturday-Sunday for consumer/lifestyle.
|
||||
|
||||
## Thread-Specific Mechanics
|
||||
|
||||
- Tweet 1 gets 10-50x the impressions of tweet 5+
|
||||
- Hook quality determines 90% of thread performance
|
||||
- "Numbered" threads (1/, 2/, etc.) signal commitment — algorithm boosts
|
||||
- Self-reply threads perform better than tweetstorm threads
|
||||
- Last tweet should have CTA + restate hook for people who scroll fast
|
||||
|
||||
## Premium/Blue Subscriber Advantages
|
||||
|
||||
- Longer tweets (up to 4,000 chars for Premium+)
|
||||
- Edit button (use sparingly — edits can suppress reach)
|
||||
- Higher reply ranking
|
||||
- Revenue sharing eligibility
|
||||
- Analytics access
|
||||
|
||||
## Sources
|
||||
|
||||
- X Engineering Blog (algorithm open-source release, 2023)
|
||||
- Community testing and experimentation (ongoing)
|
||||
- Creator program documentation
|
||||
- Third-party analytics platforms (Typefully, Hypefury, Shield)
|
||||
235
marketing-skill/x-twitter-growth/scripts/competitor_analyzer.py
Normal file
235
marketing-skill/x-twitter-growth/scripts/competitor_analyzer.py
Normal file
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
X/Twitter Competitor Analyzer — Analyze competitor profiles for content strategy insights.
|
||||
|
||||
Takes competitor handles and available data, produces a competitive
|
||||
intelligence report with content patterns, engagement strategies, and gaps.
|
||||
|
||||
Usage:
|
||||
python3 competitor_analyzer.py --handles @user1 @user2 @user3
|
||||
python3 competitor_analyzer.py --handles @user1 --followers 50000 --niche "AI"
|
||||
python3 competitor_analyzer.py --import data.json
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class CompetitorProfile:
|
||||
handle: str
|
||||
followers: int = 0
|
||||
following: int = 0
|
||||
posts_per_week: float = 0
|
||||
avg_likes: float = 0
|
||||
avg_replies: float = 0
|
||||
avg_retweets: float = 0
|
||||
thread_frequency: str = "" # daily, weekly, rarely
|
||||
top_topics: list = field(default_factory=list)
|
||||
content_mix: dict = field(default_factory=dict) # format: percentage
|
||||
posting_times: list = field(default_factory=list)
|
||||
bio: str = ""
|
||||
notes: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class CompetitiveInsight:
|
||||
category: str
|
||||
finding: str
|
||||
opportunity: str
|
||||
priority: str # HIGH, MEDIUM, LOW
|
||||
|
||||
|
||||
def calculate_engagement_rate(profile: CompetitorProfile) -> float:
|
||||
if profile.followers <= 0:
|
||||
return 0
|
||||
total_engagement = profile.avg_likes + profile.avg_replies + profile.avg_retweets
|
||||
return (total_engagement / profile.followers) * 100
|
||||
|
||||
|
||||
def analyze_competitors(competitors: list) -> list:
|
||||
insights = []
|
||||
|
||||
# Engagement comparison
|
||||
engagement_rates = []
|
||||
for c in competitors:
|
||||
er = calculate_engagement_rate(c)
|
||||
engagement_rates.append((c.handle, er))
|
||||
|
||||
if engagement_rates:
|
||||
top = max(engagement_rates, key=lambda x: x[1])
|
||||
if top[1] > 0:
|
||||
insights.append(CompetitiveInsight(
|
||||
"Engagement", f"Highest engagement: {top[0]} ({top[1]:.2f}%)",
|
||||
"Study their top posts — what format and topics drive replies?",
|
||||
"HIGH"
|
||||
))
|
||||
|
||||
# Posting frequency
|
||||
frequencies = [(c.handle, c.posts_per_week) for c in competitors if c.posts_per_week > 0]
|
||||
if frequencies:
|
||||
avg_freq = sum(f for _, f in frequencies) / len(frequencies)
|
||||
insights.append(CompetitiveInsight(
|
||||
"Frequency", f"Average posting: {avg_freq:.0f}/week across competitors",
|
||||
f"Match or exceed {avg_freq:.0f} posts/week to compete for mindshare",
|
||||
"HIGH"
|
||||
))
|
||||
|
||||
# Thread usage
|
||||
thread_users = [c.handle for c in competitors if c.thread_frequency in ("daily", "weekly")]
|
||||
if thread_users:
|
||||
insights.append(CompetitiveInsight(
|
||||
"Format", f"Active thread users: {', '.join(thread_users)}",
|
||||
"Threads are a proven growth lever in your niche. Publish 2-3/week minimum.",
|
||||
"HIGH"
|
||||
))
|
||||
|
||||
# Reply engagement
|
||||
reply_heavy = [(c.handle, c.avg_replies) for c in competitors if c.avg_replies > c.avg_likes * 0.3]
|
||||
if reply_heavy:
|
||||
names = [h for h, _ in reply_heavy]
|
||||
insights.append(CompetitiveInsight(
|
||||
"Community", f"High reply ratios: {', '.join(names)}",
|
||||
"These accounts build community through conversation. Ask more questions in your tweets.",
|
||||
"MEDIUM"
|
||||
))
|
||||
|
||||
# Follower/following ratio
|
||||
for c in competitors:
|
||||
if c.followers > 0 and c.following > 0:
|
||||
ratio = c.followers / c.following
|
||||
if ratio > 10:
|
||||
insights.append(CompetitiveInsight(
|
||||
"Authority", f"{c.handle} has {ratio:.0f}x follower/following ratio",
|
||||
"Strong authority signal — they attract followers without follow-backs",
|
||||
"LOW"
|
||||
))
|
||||
|
||||
# Topic gaps
|
||||
all_topics = []
|
||||
for c in competitors:
|
||||
all_topics.extend(c.top_topics)
|
||||
|
||||
if all_topics:
|
||||
from collections import Counter
|
||||
common = Counter(all_topics).most_common(5)
|
||||
insights.append(CompetitiveInsight(
|
||||
"Topics", f"Most covered topics: {', '.join(t for t, _ in common)}",
|
||||
"Cover these topics to compete, but find unique angles. What are they NOT covering?",
|
||||
"MEDIUM"
|
||||
))
|
||||
|
||||
return insights
|
||||
|
||||
|
||||
def print_report(competitors: list, insights: list):
|
||||
print(f"\n{'='*70}")
|
||||
print(f" COMPETITIVE ANALYSIS REPORT")
|
||||
print(f"{'='*70}")
|
||||
|
||||
# Profile summary table
|
||||
print(f"\n {'Handle':<20} {'Followers':>10} {'Posts/wk':>10} {'Eng Rate':>10}")
|
||||
print(f" {'─'*20} {'─'*10} {'─'*10} {'─'*10}")
|
||||
for c in competitors:
|
||||
er = calculate_engagement_rate(c)
|
||||
print(f" {c.handle:<20} {c.followers:>10,} {c.posts_per_week:>10.0f} {er:>9.2f}%")
|
||||
|
||||
# Insights
|
||||
if insights:
|
||||
print(f"\n {'─'*66}")
|
||||
print(f" KEY INSIGHTS\n")
|
||||
|
||||
priority_order = {"HIGH": 0, "MEDIUM": 1, "LOW": 2}
|
||||
sorted_insights = sorted(insights, key=lambda x: priority_order.get(x.priority, 3))
|
||||
|
||||
for i in sorted_insights:
|
||||
icon = {"HIGH": "🔴", "MEDIUM": "🟡", "LOW": "⚪"}.get(i.priority, "❓")
|
||||
print(f" {icon} [{i.category}] {i.finding}")
|
||||
print(f" → {i.opportunity}")
|
||||
print()
|
||||
|
||||
# Action items
|
||||
print(f" {'─'*66}")
|
||||
print(f" NEXT STEPS\n")
|
||||
print(f" 1. Search each competitor's profile on X — note their pinned tweet and bio")
|
||||
print(f" 2. Read their last 20 posts — categorize by format and topic")
|
||||
print(f" 3. Identify their top 3 performing posts — what made them work?")
|
||||
print(f" 4. Find gaps — what topics do they NOT cover that you can own?")
|
||||
print(f" 5. Set engagement targets based on their metrics as benchmarks")
|
||||
print(f"\n{'='*70}\n")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Analyze X/Twitter competitors for content strategy insights",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s --handles @user1 @user2
|
||||
%(prog)s --import competitors.json
|
||||
|
||||
JSON format for --import:
|
||||
[{"handle": "@user1", "followers": 50000, "posts_per_week": 14, ...}]
|
||||
""")
|
||||
|
||||
parser.add_argument("--handles", nargs="+", default=[], help="Competitor handles")
|
||||
parser.add_argument("--import", dest="import_file", help="Import from JSON file")
|
||||
parser.add_argument("--json", action="store_true", help="Output JSON")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
competitors = []
|
||||
|
||||
if args.import_file:
|
||||
with open(args.import_file) as f:
|
||||
data = json.load(f)
|
||||
for item in data:
|
||||
competitors.append(CompetitorProfile(**item))
|
||||
elif args.handles:
|
||||
for handle in args.handles:
|
||||
if not handle.startswith("@"):
|
||||
handle = f"@{handle}"
|
||||
competitors.append(CompetitorProfile(handle=handle))
|
||||
|
||||
if all(c.followers == 0 for c in competitors):
|
||||
print(f"\n ℹ️ Handles registered: {', '.join(c.handle for c in competitors)}")
|
||||
print(f" To get full analysis, provide data via JSON import:")
|
||||
print(f" 1. Research each profile on X")
|
||||
print(f" 2. Create a JSON file with follower counts, posting frequency, etc.")
|
||||
print(f" 3. Run: {sys.argv[0]} --import data.json")
|
||||
print(f"\n Example JSON:")
|
||||
example = [asdict(CompetitorProfile(
|
||||
handle="@example",
|
||||
followers=25000,
|
||||
following=1200,
|
||||
posts_per_week=14,
|
||||
avg_likes=150,
|
||||
avg_replies=30,
|
||||
avg_retweets=20,
|
||||
thread_frequency="weekly",
|
||||
top_topics=["AI", "startups", "engineering"],
|
||||
))]
|
||||
print(f" {json.dumps(example, indent=2)}")
|
||||
print()
|
||||
return
|
||||
|
||||
if not competitors:
|
||||
print("Error: provide --handles or --import", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
insights = analyze_competitors(competitors)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps({
|
||||
"competitors": [asdict(c) for c in competitors],
|
||||
"insights": [asdict(i) for i in insights],
|
||||
}, indent=2))
|
||||
else:
|
||||
print_report(competitors, insights)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
210
marketing-skill/x-twitter-growth/scripts/content_planner.py
Normal file
210
marketing-skill/x-twitter-growth/scripts/content_planner.py
Normal file
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
X/Twitter Content Planner — Generate weekly posting calendars.
|
||||
|
||||
Creates structured content plans with topic suggestions, format mix,
|
||||
optimal posting times, and engagement targets.
|
||||
|
||||
Usage:
|
||||
python3 content_planner.py --niche "AI engineering" --frequency 5 --weeks 2
|
||||
python3 content_planner.py --niche "SaaS growth" --frequency 3 --weeks 1 --json
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from dataclasses import dataclass, field, asdict
|
||||
|
||||
CONTENT_FORMATS = {
|
||||
"atomic_tweet": {"growth_weight": 0.3, "effort": "low", "description": "Single tweet — observation, tip, or hot take"},
|
||||
"thread": {"growth_weight": 0.35, "effort": "high", "description": "5-12 tweet deep dive — highest reach potential"},
|
||||
"question": {"growth_weight": 0.15, "effort": "low", "description": "Engagement bait — drives replies"},
|
||||
"quote_tweet": {"growth_weight": 0.10, "effort": "low", "description": "Add value to someone else's content"},
|
||||
"reply_session": {"growth_weight": 0.10, "effort": "medium", "description": "30 min focused engagement on target accounts"},
|
||||
}
|
||||
|
||||
OPTIMAL_TIMES = {
|
||||
"weekday": ["07:00-08:00", "12:00-13:00", "17:00-18:00", "20:00-21:00"],
|
||||
"weekend": ["09:00-10:00", "14:00-15:00", "19:00-20:00"],
|
||||
}
|
||||
|
||||
TOPIC_ANGLES = [
|
||||
"Lessons learned (personal experience)",
|
||||
"Framework/system breakdown",
|
||||
"Tool recommendation (with honest take)",
|
||||
"Myth busting (challenge common belief)",
|
||||
"Behind the scenes (process, workflow)",
|
||||
"Industry trend analysis",
|
||||
"Beginner guide (explain like I'm 5)",
|
||||
"Comparison (X vs Y — which is better?)",
|
||||
"Prediction (what's coming next)",
|
||||
"Case study (real example with numbers)",
|
||||
"Mistake I made (vulnerability + lesson)",
|
||||
"Quick tip (tactical, immediately useful)",
|
||||
"Controversial take (spicy but defensible)",
|
||||
"Curated list (best resources, tools, accounts)",
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class DayPlan:
|
||||
date: str
|
||||
day_of_week: str
|
||||
posts: list = field(default_factory=list)
|
||||
engagement_target: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PostSlot:
|
||||
time: str
|
||||
format: str
|
||||
topic_angle: str
|
||||
topic_suggestion: str
|
||||
notes: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class WeekPlan:
|
||||
week_number: int
|
||||
start_date: str
|
||||
end_date: str
|
||||
days: list = field(default_factory=list)
|
||||
thread_count: int = 0
|
||||
total_posts: int = 0
|
||||
focus_theme: str = ""
|
||||
|
||||
|
||||
def generate_plan(niche: str, posts_per_day: int, weeks: int, start_date: datetime) -> list:
|
||||
plans = []
|
||||
angle_idx = 0
|
||||
time_idx = 0
|
||||
|
||||
for week in range(weeks):
|
||||
week_start = start_date + timedelta(weeks=week)
|
||||
week_end = week_start + timedelta(days=6)
|
||||
|
||||
week_plan = WeekPlan(
|
||||
week_number=week + 1,
|
||||
start_date=week_start.strftime("%Y-%m-%d"),
|
||||
end_date=week_end.strftime("%Y-%m-%d"),
|
||||
focus_theme=TOPIC_ANGLES[week % len(TOPIC_ANGLES)],
|
||||
)
|
||||
|
||||
for day in range(7):
|
||||
current = week_start + timedelta(days=day)
|
||||
day_name = current.strftime("%A")
|
||||
is_weekend = day >= 5
|
||||
|
||||
times = OPTIMAL_TIMES["weekend" if is_weekend else "weekday"]
|
||||
actual_posts = max(1, posts_per_day - (1 if is_weekend else 0))
|
||||
|
||||
day_plan = DayPlan(
|
||||
date=current.strftime("%Y-%m-%d"),
|
||||
day_of_week=day_name,
|
||||
engagement_target="15 min reply session" if is_weekend else "30 min reply session",
|
||||
)
|
||||
|
||||
for p in range(actual_posts):
|
||||
# Determine format based on day position
|
||||
if day in [1, 3] and p == 0: # Tue/Thu first slot = thread
|
||||
fmt = "thread"
|
||||
elif p == actual_posts - 1 and not is_weekend:
|
||||
fmt = "question" # Last post = engagement driver
|
||||
elif day == 4 and p == 0: # Friday first = quote tweet
|
||||
fmt = "quote_tweet"
|
||||
else:
|
||||
fmt = "atomic_tweet"
|
||||
|
||||
angle = TOPIC_ANGLES[angle_idx % len(TOPIC_ANGLES)]
|
||||
angle_idx += 1
|
||||
|
||||
slot = PostSlot(
|
||||
time=times[p % len(times)],
|
||||
format=fmt,
|
||||
topic_angle=angle,
|
||||
topic_suggestion=f"{angle} about {niche}",
|
||||
notes="Pin if performs well" if fmt == "thread" else "",
|
||||
)
|
||||
day_plan.posts.append(asdict(slot))
|
||||
|
||||
if fmt == "thread":
|
||||
week_plan.thread_count += 1
|
||||
week_plan.total_posts += 1
|
||||
|
||||
week_plan.days.append(asdict(day_plan))
|
||||
|
||||
plans.append(asdict(week_plan))
|
||||
|
||||
return plans
|
||||
|
||||
|
||||
def print_plan(plans: list, niche: str):
|
||||
print(f"\n{'='*70}")
|
||||
print(f" X/TWITTER CONTENT PLAN — {niche.upper()}")
|
||||
print(f"{'='*70}")
|
||||
|
||||
for week in plans:
|
||||
print(f"\n WEEK {week['week_number']} ({week['start_date']} to {week['end_date']})")
|
||||
print(f" Theme: {week['focus_theme']}")
|
||||
print(f" Posts: {week['total_posts']} | Threads: {week['thread_count']}")
|
||||
print(f" {'─'*66}")
|
||||
|
||||
for day in week['days']:
|
||||
print(f"\n {day['day_of_week']:9} {day['date']}")
|
||||
for post in day['posts']:
|
||||
fmt_icon = {
|
||||
"thread": "🧵",
|
||||
"atomic_tweet": "💬",
|
||||
"question": "❓",
|
||||
"quote_tweet": "🔄",
|
||||
"reply_session": "💬",
|
||||
}.get(post['format'], "📝")
|
||||
|
||||
print(f" {fmt_icon} {post['time']:12} [{post['format']:<14}] {post['topic_angle']}")
|
||||
if post['notes']:
|
||||
print(f" ℹ️ {post['notes']}")
|
||||
|
||||
print(f" 📊 Engagement: {day['engagement_target']}")
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print(f" WEEKLY TARGETS")
|
||||
print(f" • Reply to 10+ accounts in your niche daily")
|
||||
print(f" • Quote tweet 2-3 relevant posts per week")
|
||||
print(f" • Update pinned tweet if a thread outperforms current pin")
|
||||
print(f" • Review analytics every Sunday — double down on what works")
|
||||
print(f"{'='*70}\n")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate X/Twitter content calendars",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
|
||||
parser.add_argument("--niche", required=True, help="Your content niche")
|
||||
parser.add_argument("--frequency", type=int, default=3, help="Posts per day (default: 3)")
|
||||
parser.add_argument("--weeks", type=int, default=2, help="Weeks to plan (default: 2)")
|
||||
parser.add_argument("--start", default="", help="Start date YYYY-MM-DD (default: next Monday)")
|
||||
parser.add_argument("--json", action="store_true", help="Output JSON")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.start:
|
||||
start = datetime.strptime(args.start, "%Y-%m-%d")
|
||||
else:
|
||||
today = datetime.now()
|
||||
days_until_monday = (7 - today.weekday()) % 7
|
||||
if days_until_monday == 0:
|
||||
days_until_monday = 7
|
||||
start = today + timedelta(days=days_until_monday)
|
||||
|
||||
plans = generate_plan(args.niche, args.frequency, args.weeks, start)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(plans, indent=2))
|
||||
else:
|
||||
print_plan(plans, args.niche)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
259
marketing-skill/x-twitter-growth/scripts/growth_tracker.py
Normal file
259
marketing-skill/x-twitter-growth/scripts/growth_tracker.py
Normal file
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
X/Twitter Growth Tracker — Track and analyze account growth over time.
|
||||
|
||||
Stores periodic snapshots of account metrics and calculates growth trends,
|
||||
engagement patterns, and milestone projections.
|
||||
|
||||
Usage:
|
||||
python3 growth_tracker.py --record --handle @user --followers 5200 --eng-rate 2.1
|
||||
python3 growth_tracker.py --report --handle @user
|
||||
python3 growth_tracker.py --report --handle @user --period 30d --json
|
||||
python3 growth_tracker.py --milestone --handle @user --target 10000
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".growth-data")
|
||||
|
||||
|
||||
def get_data_file(handle: str) -> str:
|
||||
clean = handle.lstrip("@").lower()
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
return os.path.join(DATA_DIR, f"{clean}.jsonl")
|
||||
|
||||
|
||||
def record_snapshot(handle: str, followers: int, following: int = 0,
|
||||
eng_rate: float = 0, posts_week: float = 0, notes: str = ""):
|
||||
entry = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"handle": handle,
|
||||
"followers": followers,
|
||||
"following": following,
|
||||
"engagement_rate": eng_rate,
|
||||
"posts_per_week": posts_week,
|
||||
"notes": notes,
|
||||
}
|
||||
|
||||
filepath = get_data_file(handle)
|
||||
with open(filepath, "a") as f:
|
||||
f.write(json.dumps(entry) + "\n")
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
def load_snapshots(handle: str, period_days: int = 0) -> list:
|
||||
filepath = get_data_file(handle)
|
||||
if not os.path.exists(filepath):
|
||||
return []
|
||||
|
||||
entries = []
|
||||
cutoff = None
|
||||
if period_days > 0:
|
||||
cutoff = datetime.now() - timedelta(days=period_days)
|
||||
|
||||
with open(filepath) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
entry = json.loads(line)
|
||||
if cutoff:
|
||||
ts = datetime.fromisoformat(entry["timestamp"])
|
||||
if ts < cutoff:
|
||||
continue
|
||||
entries.append(entry)
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def generate_report(handle: str, entries: list) -> dict:
|
||||
if not entries:
|
||||
return {"handle": handle, "error": "No data found"}
|
||||
|
||||
report = {
|
||||
"handle": handle,
|
||||
"data_points": len(entries),
|
||||
"first_record": entries[0]["timestamp"],
|
||||
"last_record": entries[-1]["timestamp"],
|
||||
"current_followers": entries[-1]["followers"],
|
||||
}
|
||||
|
||||
if len(entries) >= 2:
|
||||
first = entries[0]
|
||||
last = entries[-1]
|
||||
|
||||
follower_change = last["followers"] - first["followers"]
|
||||
days_span = (datetime.fromisoformat(last["timestamp"]) -
|
||||
datetime.fromisoformat(first["timestamp"])).days
|
||||
days_span = max(days_span, 1)
|
||||
|
||||
report["follower_change"] = follower_change
|
||||
report["days_tracked"] = days_span
|
||||
report["daily_growth"] = round(follower_change / days_span, 1)
|
||||
report["weekly_growth"] = round((follower_change / days_span) * 7, 1)
|
||||
report["monthly_projection"] = round((follower_change / days_span) * 30)
|
||||
|
||||
if first["followers"] > 0:
|
||||
pct_change = ((last["followers"] - first["followers"]) / first["followers"]) * 100
|
||||
report["growth_percent"] = round(pct_change, 1)
|
||||
|
||||
# Engagement trend
|
||||
eng_rates = [e["engagement_rate"] for e in entries if e.get("engagement_rate", 0) > 0]
|
||||
if len(eng_rates) >= 2:
|
||||
mid = len(eng_rates) // 2
|
||||
first_half_avg = sum(eng_rates[:mid]) / mid
|
||||
second_half_avg = sum(eng_rates[mid:]) / (len(eng_rates) - mid)
|
||||
report["engagement_trend"] = "improving" if second_half_avg > first_half_avg else "declining"
|
||||
report["avg_engagement_rate"] = round(sum(eng_rates) / len(eng_rates), 2)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def project_milestone(handle: str, entries: list, target: int) -> dict:
|
||||
if len(entries) < 2:
|
||||
return {"error": "Need at least 2 data points for projection"}
|
||||
|
||||
current = entries[-1]["followers"]
|
||||
if current >= target:
|
||||
return {"handle": handle, "target": target, "status": "Already reached!"}
|
||||
|
||||
first = entries[0]
|
||||
last = entries[-1]
|
||||
days_span = (datetime.fromisoformat(last["timestamp"]) -
|
||||
datetime.fromisoformat(first["timestamp"])).days
|
||||
days_span = max(days_span, 1)
|
||||
|
||||
daily_growth = (last["followers"] - first["followers"]) / days_span
|
||||
|
||||
if daily_growth <= 0:
|
||||
return {"handle": handle, "target": target, "status": "Not growing — can't project",
|
||||
"daily_growth": round(daily_growth, 1)}
|
||||
|
||||
remaining = target - current
|
||||
days_needed = remaining / daily_growth
|
||||
target_date = datetime.now() + timedelta(days=days_needed)
|
||||
|
||||
return {
|
||||
"handle": handle,
|
||||
"current": current,
|
||||
"target": target,
|
||||
"remaining": remaining,
|
||||
"daily_growth": round(daily_growth, 1),
|
||||
"days_needed": round(days_needed),
|
||||
"projected_date": target_date.strftime("%Y-%m-%d"),
|
||||
}
|
||||
|
||||
|
||||
def print_report(report: dict):
|
||||
print(f"\n{'='*60}")
|
||||
print(f" GROWTH REPORT — {report['handle']}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
if "error" in report:
|
||||
print(f"\n ⚠️ {report['error']}")
|
||||
print(f" Record data first: python3 growth_tracker.py --record --handle {report['handle']} --followers N")
|
||||
print()
|
||||
return
|
||||
|
||||
print(f"\n Current followers: {report['current_followers']:,}")
|
||||
print(f" Data points: {report['data_points']}")
|
||||
print(f" Tracking since: {report['first_record'][:10]}")
|
||||
|
||||
if "follower_change" in report:
|
||||
change_icon = "📈" if report["follower_change"] > 0 else "📉" if report["follower_change"] < 0 else "➡️"
|
||||
print(f"\n {change_icon} Change: {report['follower_change']:+,} followers over {report['days_tracked']} days")
|
||||
print(f" Daily avg: {report.get('daily_growth', 0):+.1f}/day")
|
||||
print(f" Weekly avg: {report.get('weekly_growth', 0):+.1f}/week")
|
||||
print(f" 30-day projection: {report.get('monthly_projection', 0):+,}")
|
||||
|
||||
if "growth_percent" in report:
|
||||
print(f" Growth rate: {report['growth_percent']:+.1f}%")
|
||||
|
||||
if "engagement_trend" in report:
|
||||
trend_icon = "📈" if report["engagement_trend"] == "improving" else "📉"
|
||||
print(f" Engagement: {trend_icon} {report['engagement_trend']} (avg {report['avg_engagement_rate']}%)")
|
||||
|
||||
print(f"\n{'='*60}\n")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Track X/Twitter account growth over time",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
|
||||
parser.add_argument("--record", action="store_true", help="Record a new snapshot")
|
||||
parser.add_argument("--report", action="store_true", help="Generate growth report")
|
||||
parser.add_argument("--milestone", action="store_true", help="Project when target will be reached")
|
||||
|
||||
parser.add_argument("--handle", required=True, help="X handle")
|
||||
parser.add_argument("--followers", type=int, default=0, help="Current follower count")
|
||||
parser.add_argument("--following", type=int, default=0, help="Current following count")
|
||||
parser.add_argument("--eng-rate", type=float, default=0, help="Current engagement rate (pct)")
|
||||
parser.add_argument("--posts-week", type=float, default=0, help="Posts per week")
|
||||
parser.add_argument("--notes", default="", help="Notes for this snapshot")
|
||||
parser.add_argument("--period", default="all", help="Report period: 7d, 30d, 90d, all")
|
||||
parser.add_argument("--target", type=int, default=0, help="Follower milestone target")
|
||||
parser.add_argument("--json", action="store_true", help="Output JSON")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.handle.startswith("@"):
|
||||
args.handle = f"@{args.handle}"
|
||||
|
||||
if args.record:
|
||||
if args.followers <= 0:
|
||||
print("Error: --followers required for recording", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
entry = record_snapshot(args.handle, args.followers, args.following,
|
||||
args.eng_rate, args.posts_week, args.notes)
|
||||
if args.json:
|
||||
print(json.dumps(entry, indent=2))
|
||||
else:
|
||||
print(f" ✅ Recorded: {args.handle} — {args.followers:,} followers")
|
||||
print(f" File: {get_data_file(args.handle)}")
|
||||
|
||||
elif args.report:
|
||||
period_days = 0
|
||||
if args.period != "all":
|
||||
period_days = int(args.period.rstrip("d"))
|
||||
entries = load_snapshots(args.handle, period_days)
|
||||
report = generate_report(args.handle, entries)
|
||||
if args.json:
|
||||
print(json.dumps(report, indent=2))
|
||||
else:
|
||||
print_report(report)
|
||||
|
||||
elif args.milestone:
|
||||
if args.target <= 0:
|
||||
print("Error: --target required for milestone projection", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
entries = load_snapshots(args.handle)
|
||||
result = project_milestone(args.handle, entries, args.target)
|
||||
if args.json:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
if "error" in result:
|
||||
print(f" ⚠️ {result['error']}")
|
||||
elif "status" in result and "days_needed" not in result:
|
||||
print(f" 🎉 {result['status']}")
|
||||
else:
|
||||
print(f"\n 🎯 Milestone Projection: {result['handle']}")
|
||||
print(f" Current: {result['current']:,}")
|
||||
print(f" Target: {result['target']:,}")
|
||||
print(f" Gap: {result['remaining']:,}")
|
||||
print(f" Growth: {result['daily_growth']:+.1f}/day")
|
||||
print(f" ETA: {result['projected_date']} (~{result['days_needed']} days)")
|
||||
print()
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
294
marketing-skill/x-twitter-growth/scripts/profile_auditor.py
Normal file
294
marketing-skill/x-twitter-growth/scripts/profile_auditor.py
Normal file
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
X/Twitter Profile Auditor — Audit any X profile for growth readiness.
|
||||
|
||||
Checks bio quality, pinned tweet, posting patterns, and provides
|
||||
actionable recommendations. Works without API access by analyzing
|
||||
profile data you provide or scraping public info via web search.
|
||||
|
||||
Usage:
|
||||
python3 profile_auditor.py --handle @username
|
||||
python3 profile_auditor.py --handle @username --json
|
||||
python3 profile_auditor.py --bio "current bio text" --followers 5000 --posts-per-week 10
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProfileData:
|
||||
handle: str = ""
|
||||
bio: str = ""
|
||||
followers: int = 0
|
||||
following: int = 0
|
||||
posts_per_week: float = 0
|
||||
reply_ratio: float = 0 # % of posts that are replies
|
||||
thread_ratio: float = 0 # % of posts that are threads
|
||||
has_pinned: bool = False
|
||||
pinned_age_days: int = 0
|
||||
has_link: bool = False
|
||||
has_newsletter: bool = False
|
||||
avg_engagement_rate: float = 0 # likes+replies+rts / followers
|
||||
|
||||
|
||||
@dataclass
|
||||
class AuditFinding:
|
||||
area: str
|
||||
status: str # GOOD, WARN, CRITICAL
|
||||
message: str
|
||||
fix: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class AuditReport:
|
||||
handle: str
|
||||
score: int = 0
|
||||
max_score: int = 100
|
||||
grade: str = ""
|
||||
findings: list = field(default_factory=list)
|
||||
recommendations: list = field(default_factory=list)
|
||||
|
||||
|
||||
def audit_bio(profile: ProfileData) -> list:
|
||||
findings = []
|
||||
bio = profile.bio.strip()
|
||||
|
||||
if not bio:
|
||||
findings.append(AuditFinding("Bio", "CRITICAL", "No bio provided for audit",
|
||||
"Provide bio text with --bio flag"))
|
||||
return findings
|
||||
|
||||
# Length check
|
||||
if len(bio) < 30:
|
||||
findings.append(AuditFinding("Bio", "WARN", f"Bio too short ({len(bio)} chars)",
|
||||
"Aim for 100-160 characters with clear value prop"))
|
||||
elif len(bio) > 160:
|
||||
findings.append(AuditFinding("Bio", "WARN", f"Bio may be too long ({len(bio)} chars)",
|
||||
"Keep under 160 chars for readability"))
|
||||
else:
|
||||
findings.append(AuditFinding("Bio", "GOOD", f"Bio length OK ({len(bio)} chars)"))
|
||||
|
||||
# Hashtag check
|
||||
hashtags = re.findall(r'#\w+', bio)
|
||||
if hashtags:
|
||||
findings.append(AuditFinding("Bio", "WARN", f"Hashtags in bio ({', '.join(hashtags)})",
|
||||
"Remove hashtags — signals amateur. Use plain text."))
|
||||
else:
|
||||
findings.append(AuditFinding("Bio", "GOOD", "No hashtags in bio"))
|
||||
|
||||
# Buzzword check
|
||||
buzzwords = ['entrepreneur', 'guru', 'ninja', 'rockstar', 'visionary', 'hustler',
|
||||
'thought leader', 'serial entrepreneur', 'dreamer', 'doer']
|
||||
found = [bw for bw in buzzwords if bw.lower() in bio.lower()]
|
||||
if found:
|
||||
findings.append(AuditFinding("Bio", "WARN", f"Buzzwords detected: {', '.join(found)}",
|
||||
"Replace with specific, concrete descriptions of what you do"))
|
||||
|
||||
# Specificity check — pipes and slashes often signal unfocused bios
|
||||
if bio.count('|') >= 3 or bio.count('/') >= 3:
|
||||
findings.append(AuditFinding("Bio", "WARN", "Bio may lack focus (too many roles/identities)",
|
||||
"Lead with ONE clear identity. What's the #1 thing you want to be known for?"))
|
||||
|
||||
# Social proof check
|
||||
proof_patterns = [r'\d+[kKmM]?\+?\s*(followers|subscribers|readers|users|customers)',
|
||||
r'(founder|ceo|cto|vp|head|director|lead)\s+(of|at|@)',
|
||||
r'(author|writer)\s+of', r'featured\s+in', r'ex-\w+']
|
||||
has_proof = any(re.search(p, bio, re.IGNORECASE) for p in proof_patterns)
|
||||
if has_proof:
|
||||
findings.append(AuditFinding("Bio", "GOOD", "Social proof detected"))
|
||||
else:
|
||||
findings.append(AuditFinding("Bio", "WARN", "No obvious social proof in bio",
|
||||
"Add a credential: title, metric, brand association, or achievement"))
|
||||
|
||||
# CTA/Link check
|
||||
if profile.has_link:
|
||||
findings.append(AuditFinding("Bio", "GOOD", "Profile has a link"))
|
||||
else:
|
||||
findings.append(AuditFinding("Bio", "WARN", "No link in profile",
|
||||
"Add a link to newsletter, product, or portfolio"))
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def audit_activity(profile: ProfileData) -> list:
|
||||
findings = []
|
||||
|
||||
# Posting frequency
|
||||
if profile.posts_per_week <= 0:
|
||||
findings.append(AuditFinding("Activity", "CRITICAL", "No posting data provided",
|
||||
"Provide --posts-per-week estimate"))
|
||||
elif profile.posts_per_week < 3:
|
||||
findings.append(AuditFinding("Activity", "CRITICAL",
|
||||
f"Very low posting ({profile.posts_per_week:.0f}/week)",
|
||||
"Minimum 7 posts/week (1/day). Aim for 14-21."))
|
||||
elif profile.posts_per_week < 7:
|
||||
findings.append(AuditFinding("Activity", "WARN",
|
||||
f"Low posting ({profile.posts_per_week:.0f}/week)",
|
||||
"Aim for 2-3 posts per day for consistent growth"))
|
||||
elif profile.posts_per_week < 21:
|
||||
findings.append(AuditFinding("Activity", "GOOD",
|
||||
f"Good posting cadence ({profile.posts_per_week:.0f}/week)"))
|
||||
else:
|
||||
findings.append(AuditFinding("Activity", "GOOD",
|
||||
f"High posting cadence ({profile.posts_per_week:.0f}/week)"))
|
||||
|
||||
# Reply ratio
|
||||
if profile.reply_ratio > 0:
|
||||
if profile.reply_ratio < 0.2:
|
||||
findings.append(AuditFinding("Activity", "WARN",
|
||||
f"Low reply ratio ({profile.reply_ratio:.0%})",
|
||||
"Aim for 30%+ replies. Engage with others, don't just broadcast."))
|
||||
elif profile.reply_ratio >= 0.3:
|
||||
findings.append(AuditFinding("Activity", "GOOD",
|
||||
f"Healthy reply ratio ({profile.reply_ratio:.0%})"))
|
||||
|
||||
# Follower/following ratio
|
||||
if profile.followers > 0 and profile.following > 0:
|
||||
ratio = profile.followers / profile.following
|
||||
if ratio < 0.5:
|
||||
findings.append(AuditFinding("Profile", "WARN",
|
||||
f"Low follower/following ratio ({ratio:.1f}x)",
|
||||
"Unfollow inactive accounts. Ratio should trend toward 2:1+"))
|
||||
elif ratio >= 2:
|
||||
findings.append(AuditFinding("Profile", "GOOD",
|
||||
f"Healthy follower/following ratio ({ratio:.1f}x)"))
|
||||
|
||||
# Pinned tweet
|
||||
if profile.has_pinned:
|
||||
if profile.pinned_age_days > 30:
|
||||
findings.append(AuditFinding("Profile", "WARN",
|
||||
f"Pinned tweet is {profile.pinned_age_days} days old",
|
||||
"Update pinned tweet monthly with your latest best content"))
|
||||
else:
|
||||
findings.append(AuditFinding("Profile", "GOOD", "Pinned tweet is recent"))
|
||||
else:
|
||||
findings.append(AuditFinding("Profile", "WARN", "No pinned tweet",
|
||||
"Pin your best-performing tweet or thread. It's your landing page."))
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def calculate_score(findings: list) -> tuple:
|
||||
total = len(findings)
|
||||
if total == 0:
|
||||
return 0, "F"
|
||||
|
||||
good = sum(1 for f in findings if f.status == "GOOD")
|
||||
score = int((good / total) * 100)
|
||||
|
||||
if score >= 90:
|
||||
grade = "A"
|
||||
elif score >= 75:
|
||||
grade = "B"
|
||||
elif score >= 60:
|
||||
grade = "C"
|
||||
elif score >= 40:
|
||||
grade = "D"
|
||||
else:
|
||||
grade = "F"
|
||||
|
||||
return score, grade
|
||||
|
||||
|
||||
def generate_recommendations(findings: list, profile: ProfileData) -> list:
|
||||
recs = []
|
||||
criticals = [f for f in findings if f.status == "CRITICAL"]
|
||||
warns = [f for f in findings if f.status == "WARN"]
|
||||
|
||||
for f in criticals:
|
||||
if f.fix:
|
||||
recs.append(f"🔴 {f.fix}")
|
||||
|
||||
for f in warns[:3]: # Top 3 warnings
|
||||
if f.fix:
|
||||
recs.append(f"🟡 {f.fix}")
|
||||
|
||||
# Stage-specific advice
|
||||
if profile.followers < 1000:
|
||||
recs.append("📈 Growth phase: Focus 70% on replies to larger accounts, 30% on your own posts")
|
||||
elif profile.followers < 10000:
|
||||
recs.append("📈 Momentum phase: 2-3 threads/week + daily engagement. Start a recurring series.")
|
||||
else:
|
||||
recs.append("📈 Scale phase: Leverage audience with cross-platform repurposing + newsletter growth")
|
||||
|
||||
return recs
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Audit an X/Twitter profile for growth readiness",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s --handle @rezarezvani --bio "CTO building AI products" --followers 5000
|
||||
%(prog)s --bio "Entrepreneur | Dreamer | Hustle" --followers 200 --posts-per-week 3
|
||||
%(prog)s --handle @example --followers 50000 --posts-per-week 21 --reply-ratio 0.4 --json
|
||||
""")
|
||||
|
||||
parser.add_argument("--handle", default="@unknown", help="X handle")
|
||||
parser.add_argument("--bio", default="", help="Current bio text")
|
||||
parser.add_argument("--followers", type=int, default=0, help="Follower count")
|
||||
parser.add_argument("--following", type=int, default=0, help="Following count")
|
||||
parser.add_argument("--posts-per-week", type=float, default=0, help="Average posts per week")
|
||||
parser.add_argument("--reply-ratio", type=float, default=0, help="Fraction of posts that are replies (0-1)")
|
||||
parser.add_argument("--has-pinned", action="store_true", help="Has a pinned tweet")
|
||||
parser.add_argument("--pinned-age-days", type=int, default=0, help="Age of pinned tweet in days")
|
||||
parser.add_argument("--has-link", action="store_true", help="Has link in profile")
|
||||
parser.add_argument("--json", action="store_true", help="Output JSON")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
profile = ProfileData(
|
||||
handle=args.handle,
|
||||
bio=args.bio,
|
||||
followers=args.followers,
|
||||
following=args.following,
|
||||
posts_per_week=args.posts_per_week,
|
||||
reply_ratio=args.reply_ratio,
|
||||
has_pinned=args.has_pinned,
|
||||
pinned_age_days=args.pinned_age_days,
|
||||
has_link=args.has_link,
|
||||
)
|
||||
|
||||
findings = audit_bio(profile) + audit_activity(profile)
|
||||
score, grade = calculate_score(findings)
|
||||
recs = generate_recommendations(findings, profile)
|
||||
|
||||
report = AuditReport(
|
||||
handle=profile.handle,
|
||||
score=score,
|
||||
grade=grade,
|
||||
findings=[asdict(f) for f in findings],
|
||||
recommendations=recs,
|
||||
)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(asdict(report), indent=2))
|
||||
else:
|
||||
print(f"\n{'='*60}")
|
||||
print(f" X PROFILE AUDIT — {report.handle}")
|
||||
print(f"{'='*60}")
|
||||
print(f"\n Score: {report.score}/100 (Grade: {report.grade})\n")
|
||||
|
||||
for f in findings:
|
||||
icon = {"GOOD": "✅", "WARN": "⚠️", "CRITICAL": "🔴"}.get(f.status, "❓")
|
||||
print(f" {icon} [{f.area}] {f.message}")
|
||||
if f.fix and f.status != "GOOD":
|
||||
print(f" → {f.fix}")
|
||||
|
||||
if recs:
|
||||
print(f"\n {'─'*56}")
|
||||
print(f" TOP RECOMMENDATIONS\n")
|
||||
for i, r in enumerate(recs, 1):
|
||||
print(f" {i}. {r}")
|
||||
|
||||
print(f"\n{'='*60}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
290
marketing-skill/x-twitter-growth/scripts/tweet_composer.py
Normal file
290
marketing-skill/x-twitter-growth/scripts/tweet_composer.py
Normal file
@@ -0,0 +1,290 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tweet Composer — Generate structured tweets and threads with proven hook patterns.
|
||||
|
||||
Provides templates, character counting, thread formatting, and hook generation
|
||||
for different content types. No API required — pure content scaffolding.
|
||||
|
||||
Usage:
|
||||
python3 tweet_composer.py --type tweet --topic "AI in healthcare"
|
||||
python3 tweet_composer.py --type thread --topic "lessons from scaling" --tweets 8
|
||||
python3 tweet_composer.py --type hooks --topic "startup mistakes" --count 10
|
||||
python3 tweet_composer.py --validate "your tweet text here"
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import textwrap
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import Optional
|
||||
|
||||
MAX_TWEET_CHARS = 280
|
||||
|
||||
HOOK_PATTERNS = {
|
||||
"listicle": [
|
||||
"{n} {topic} that changed how I {verb}:",
|
||||
"The {n} biggest mistakes in {topic}:",
|
||||
"{n} {topic} most people don't know about:",
|
||||
"I spent {time} studying {topic}. Here are {n} lessons:",
|
||||
"{n} signs your {topic} needs work:",
|
||||
],
|
||||
"contrarian": [
|
||||
"Unpopular opinion: {claim}",
|
||||
"Hot take: {claim}",
|
||||
"Everyone says {common_belief}. They're wrong.",
|
||||
"Stop {common_action}. Here's what to do instead:",
|
||||
"The {topic} advice you keep hearing is backwards.",
|
||||
],
|
||||
"story": [
|
||||
"I {did_thing} and it completely changed my {outcome}.",
|
||||
"Last {timeframe}, I made a mistake with {topic}. Here's what happened:",
|
||||
"3 years ago I was {before_state}. Now I'm {after_state}. Here's the playbook:",
|
||||
"I almost {near_miss}. Then I discovered {topic}.",
|
||||
"The best {topic} advice I ever got came from {unexpected_source}.",
|
||||
],
|
||||
"observation": [
|
||||
"{topic} is underrated. Here's why:",
|
||||
"Nobody talks about this part of {topic}:",
|
||||
"The gap between {thing_a} and {thing_b} is where the money is.",
|
||||
"If you're struggling with {topic}, you're probably {mistake}.",
|
||||
"The secret to {topic} isn't what you think.",
|
||||
],
|
||||
"framework": [
|
||||
"The {name} framework for {topic} (save this):",
|
||||
"How to {outcome} in {timeframe} (step by step):",
|
||||
"{topic} explained in 60 seconds:",
|
||||
"The only {n} things that matter for {topic}:",
|
||||
"A simple system for {topic} that actually works:",
|
||||
],
|
||||
"question": [
|
||||
"What's the most underrated {topic}?",
|
||||
"If you could only {do_one_thing} for {topic}, what would it be?",
|
||||
"What {topic} advice would you give your younger self?",
|
||||
"Real question: why do most people {common_mistake}?",
|
||||
"What's one {topic} that completely changed your perspective?",
|
||||
],
|
||||
}
|
||||
|
||||
THREAD_STRUCTURE = """
|
||||
Thread Outline: {topic}
|
||||
{'='*50}
|
||||
|
||||
Tweet 1 (HOOK — most important):
|
||||
Pattern: {hook_pattern}
|
||||
Draft: {hook_draft}
|
||||
Chars: {hook_chars}/280
|
||||
|
||||
Tweet 2 (CONTEXT):
|
||||
Purpose: Set up why this matters
|
||||
Suggestion: "Here's what most people get wrong about {topic}:"
|
||||
OR: "I spent [time] learning this. Here's the breakdown:"
|
||||
|
||||
Tweets 3-{n} (BODY — one idea per tweet):
|
||||
{body_suggestions}
|
||||
|
||||
Tweet {n_plus_1} (CLOSE):
|
||||
Purpose: Summarize + CTA
|
||||
Suggestion: "TL;DR:\\n\\n[3 bullet summary]\\n\\nFollow @handle for more on {topic}"
|
||||
|
||||
Reply to Tweet 1 (ENGAGEMENT BAIT):
|
||||
Purpose: Resurface the thread
|
||||
Suggestion: "What's your experience with {topic}? Drop it below 👇"
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class TweetDraft:
|
||||
text: str
|
||||
char_count: int
|
||||
over_limit: bool
|
||||
warnings: list = field(default_factory=list)
|
||||
|
||||
|
||||
def validate_tweet(text: str) -> TweetDraft:
|
||||
"""Validate a tweet and return analysis."""
|
||||
char_count = len(text)
|
||||
over_limit = char_count > MAX_TWEET_CHARS
|
||||
warnings = []
|
||||
|
||||
if over_limit:
|
||||
warnings.append(f"Over limit by {char_count - MAX_TWEET_CHARS} characters")
|
||||
|
||||
# Check for links in body
|
||||
import re
|
||||
if re.search(r'https?://\S+', text):
|
||||
warnings.append("Contains URL — consider moving link to reply (hurts reach)")
|
||||
|
||||
# Check for hashtags
|
||||
hashtags = re.findall(r'#\w+', text)
|
||||
if len(hashtags) > 2:
|
||||
warnings.append(f"Too many hashtags ({len(hashtags)}) — max 1-2, ideally 0")
|
||||
elif len(hashtags) > 0:
|
||||
warnings.append(f"Has {len(hashtags)} hashtag(s) — consider removing for cleaner look")
|
||||
|
||||
# Check for @mentions at start
|
||||
if text.startswith('@'):
|
||||
warnings.append("Starts with @ — will be treated as reply, not shown in timeline")
|
||||
|
||||
# Readability
|
||||
lines = text.strip().split('\n')
|
||||
long_lines = [l for l in lines if len(l) > 70]
|
||||
if long_lines:
|
||||
warnings.append("Long unbroken lines — add line breaks for mobile readability")
|
||||
|
||||
return TweetDraft(text=text, char_count=char_count, over_limit=over_limit, warnings=warnings)
|
||||
|
||||
|
||||
def generate_hooks(topic: str, count: int = 10) -> list:
|
||||
"""Generate hook variations for a topic."""
|
||||
hooks = []
|
||||
for pattern_type, patterns in HOOK_PATTERNS.items():
|
||||
for p in patterns:
|
||||
hook = p.replace("{topic}", topic).replace("{n}", "7").replace(
|
||||
"{time}", "6 months").replace("{timeframe}", "month").replace(
|
||||
"{claim}", f"{topic} is overrated").replace(
|
||||
"{common_belief}", f"{topic} is simple").replace(
|
||||
"{common_action}", f"overthinking {topic}").replace(
|
||||
"{outcome}", "approach").replace("{verb}", "think").replace(
|
||||
"{name}", "3-Step").replace("{did_thing}", f"changed my {topic} strategy").replace(
|
||||
"{before_state}", "stuck").replace("{after_state}", "thriving").replace(
|
||||
"{near_miss}", f"gave up on {topic}").replace(
|
||||
"{unexpected_source}", "a complete beginner").replace(
|
||||
"{thing_a}", "theory").replace("{thing_b}", "execution").replace(
|
||||
"{mistake}", "overcomplicating it").replace(
|
||||
"{common_mistake}", f"ignore {topic}").replace(
|
||||
"{do_one_thing}", "change one thing").replace(
|
||||
"{common_action}", f"overthinking {topic}")
|
||||
hooks.append({"type": pattern_type, "hook": hook, "chars": len(hook)})
|
||||
if len(hooks) >= count:
|
||||
return hooks
|
||||
return hooks[:count]
|
||||
|
||||
|
||||
def generate_thread_outline(topic: str, num_tweets: int = 8) -> str:
|
||||
"""Generate a thread structure outline."""
|
||||
hooks = generate_hooks(topic, 3)
|
||||
best_hook = hooks[0]["hook"] if hooks else f"Everything I know about {topic}:"
|
||||
|
||||
body = []
|
||||
suggestions = [
|
||||
"Key insight or surprising fact",
|
||||
"Common mistake people make",
|
||||
"The counterintuitive truth",
|
||||
"A practical example or case study",
|
||||
"The framework or system",
|
||||
"Implementation steps",
|
||||
"Results or evidence",
|
||||
"The nuance most people miss",
|
||||
]
|
||||
|
||||
for i, s in enumerate(suggestions[:num_tweets - 3], 3):
|
||||
body.append(f" Tweet {i}: [{s}]")
|
||||
|
||||
body_text = "\n".join(body)
|
||||
|
||||
return f"""
|
||||
{'='*60}
|
||||
THREAD OUTLINE: {topic}
|
||||
{'='*60}
|
||||
|
||||
Tweet 1 (HOOK):
|
||||
"{best_hook}"
|
||||
Chars: {len(best_hook)}/280
|
||||
|
||||
Tweet 2 (CONTEXT):
|
||||
"Here's what most people get wrong about {topic}:"
|
||||
|
||||
{body_text}
|
||||
|
||||
Tweet {num_tweets - 1} (CLOSE):
|
||||
"TL;DR:
|
||||
|
||||
• [Key takeaway 1]
|
||||
• [Key takeaway 2]
|
||||
• [Key takeaway 3]
|
||||
|
||||
Follow for more on {topic}"
|
||||
|
||||
Reply to Tweet 1 (BOOST):
|
||||
"What's your biggest challenge with {topic}? 👇"
|
||||
|
||||
{'='*60}
|
||||
RULES:
|
||||
- Each tweet must stand alone (people read out of order)
|
||||
- Max 3-4 lines per tweet (mobile readability)
|
||||
- No filler tweets — cut anything that doesn't add value
|
||||
- Hook tweet determines 90%% of thread performance
|
||||
{'='*60}
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate tweets, threads, and hooks with proven patterns",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
|
||||
parser.add_argument("--type", choices=["tweet", "thread", "hooks", "validate"],
|
||||
default="hooks", help="Content type to generate")
|
||||
parser.add_argument("--topic", default="", help="Topic for content generation")
|
||||
parser.add_argument("--tweets", type=int, default=8, help="Number of tweets in thread")
|
||||
parser.add_argument("--count", type=int, default=10, help="Number of hooks to generate")
|
||||
parser.add_argument("--validate", nargs="?", const="", help="Tweet text to validate")
|
||||
parser.add_argument("--json", action="store_true", help="Output JSON")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.type == "validate" or args.validate is not None:
|
||||
text = args.validate or args.topic
|
||||
if not text:
|
||||
print("Error: provide tweet text to validate", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
result = validate_tweet(text)
|
||||
if args.json:
|
||||
print(json.dumps(asdict(result), indent=2))
|
||||
else:
|
||||
icon = "🔴" if result.over_limit else "✅"
|
||||
print(f"\n {icon} {result.char_count}/{MAX_TWEET_CHARS} characters")
|
||||
if result.warnings:
|
||||
for w in result.warnings:
|
||||
print(f" ⚠️ {w}")
|
||||
else:
|
||||
print(" No issues found.")
|
||||
print()
|
||||
|
||||
elif args.type == "hooks":
|
||||
if not args.topic:
|
||||
print("Error: --topic required for hook generation", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
hooks = generate_hooks(args.topic, args.count)
|
||||
if args.json:
|
||||
print(json.dumps(hooks, indent=2))
|
||||
else:
|
||||
print(f"\n{'='*60}")
|
||||
print(f" HOOK IDEAS: {args.topic}")
|
||||
print(f"{'='*60}\n")
|
||||
for i, h in enumerate(hooks, 1):
|
||||
print(f" {i:2d}. [{h['type']:<12}] {h['hook']}")
|
||||
print(f" ({h['chars']} chars)")
|
||||
print()
|
||||
|
||||
elif args.type == "thread":
|
||||
if not args.topic:
|
||||
print("Error: --topic required for thread generation", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
outline = generate_thread_outline(args.topic, args.tweets)
|
||||
print(outline)
|
||||
|
||||
elif args.type == "tweet":
|
||||
if not args.topic:
|
||||
print("Error: --topic required", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
hooks = generate_hooks(args.topic, 5)
|
||||
print(f"\n 5 tweet drafts for: {args.topic}\n")
|
||||
for i, h in enumerate(hooks, 1):
|
||||
print(f" {i}. {h['hook']}")
|
||||
print(f" ({h['chars']} chars)\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,6 +1,6 @@
|
||||
site_name: Claude Code Skills & Plugins
|
||||
site_url: https://alirezarezvani.github.io/claude-skills
|
||||
site_description: "170 production-ready skills, 14 agents, and 14 commands for Claude Code, OpenAI Codex, Gemini CLI, and OpenClaw. Reusable expertise for engineering, product, marketing, compliance, and more."
|
||||
site_description: "172 production-ready skills, 14 agents, and 14 commands for Claude Code, OpenAI Codex, Gemini CLI, and OpenClaw. Reusable expertise for engineering, product, marketing, compliance, and more."
|
||||
site_author: Alireza Rezvani
|
||||
repo_url: https://github.com/alirezarezvani/claude-skills
|
||||
repo_name: alirezarezvani/claude-skills
|
||||
@@ -241,6 +241,7 @@ nav:
|
||||
- "Social Content": skills/marketing-skill/social-content.md
|
||||
- "Social Media Analyzer": skills/marketing-skill/social-media-analyzer.md
|
||||
- "Social Media Manager": skills/marketing-skill/social-media-manager.md
|
||||
- "X/Twitter Growth": skills/marketing-skill/x-twitter-growth.md
|
||||
- Project Management:
|
||||
- Overview: skills/project-management/index.md
|
||||
- "Atlassian Admin": skills/project-management/atlassian-admin.md
|
||||
|
||||
Reference in New Issue
Block a user