📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-06-06 16:01:57 +00:00
parent 0b1862e551
commit 772a1da63c
293 changed files with 25299 additions and 369 deletions
@@ -0,0 +1,82 @@
"""
generate_calendar.py — LinkedIn Content Calendar Prompt Builder
Usage:
python generate_calendar.py --niche "<niche>" [--days <n>] [--frequency "<freq>"] [--goal <goal>]
Goal: awareness | engagement | leads | authority | growth
"""
import argparse
import sys
import os
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRIPT_DIR)
from utils import get_base_prompt_context
GOAL_GUIDE = {
"awareness": "Maximise reach. Focus on shareable, relatable, trending content. Heavy on carousels and controversial takes.",
"engagement": "Maximise comments. Focus on opinion posts, polls, questions, and storytelling.",
"leads": "Generate DMs. Mix educational value posts with authority-building and clear CTAs to contact.",
"authority": "Position as expert. Deep insights, data-backed posts, newsletter content, thought leadership.",
"growth": "Grow followers fast. Mix viral formats (carousels, lists, contrarian) with high-value education.",
}
FORMAT_MIX = {
"Text Post": "Pure conversational text — personal story or insight",
"Carousel": "Multi-slide document — educational or list-based",
"Poll": "LinkedIn poll with 2-4 options — quick engagement spike",
"Newsletter Link": "Teaser post linking to your newsletter edition",
"Video Script": "Script outline for a talking-head video",
"Image + Caption": "Strong visual with punchy caption",
}
def main():
parser = argparse.ArgumentParser(description="Generate a LinkedIn Content Calendar prompt")
parser.add_argument("--niche", required=True)
parser.add_argument("--days", required=False, type=int, default=30)
parser.add_argument("--frequency", required=False, default="3 times a week")
parser.add_argument("--goal", required=False, default="growth", choices=list(GOAL_GUIDE.keys()))
args = parser.parse_args()
goal_instruction = GOAL_GUIDE.get(args.goal, GOAL_GUIDE["growth"])
formats_list = "\n".join([f" - **{k}**: {v}" for k, v in FORMAT_MIX.items()])
context = get_base_prompt_context(args.niche, "LinkedIn Content Calendar")
prompt = f"""{context}
<TASK>
Generate a {args.days}-day LinkedIn Content Calendar for the "{args.niche}" niche.
**Posting Frequency**: {args.frequency}
**Primary Goal**: {args.goal.upper()}{goal_instruction}
Available formats (use a strategic mix):
{formats_list}
For each post entry provide a Markdown table row:
| # | Day | Format | Topic / Angle | Hook (First Line) | CTA |
Calendar rules:
1. Never repeat the same format two days in a row
2. For every 4 posts: 2 educational, 1 personal/story, 1 opinion/controversial
3. Include at least 2 polls per month
4. Space carousels and newsletters evenly across the month
5. End each week with a reflection or motivational post
After the calendar table, provide:
- 📌 **Monthly Theme**: One overarching narrative tying the month together
- 🔑 **Top 5 SEO Keywords** to embed naturally across posts
- 📊 **Format Breakdown**: e.g., "8 Text Posts, 5 Carousels, 3 Polls..."
Output as a clean Markdown table. Ready to copy into Notion or Google Sheets.
</TASK>"""
print(prompt)
if __name__ == "__main__":
main()
@@ -0,0 +1,69 @@
"""
generate_carousel.py — LinkedIn Carousel Prompt Builder
Usage:
python generate_carousel.py --topic "<topic>" --niche "<niche>" [--slides <n>] [--style <style>]
Style: how-to | listicle | myth-busting | framework | story-arc
"""
import argparse
import sys
import os
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRIPT_DIR)
from utils import get_base_prompt_context
CAROUSEL_STYLES = {
"how-to": "Step-by-step guide. Slide 1 = problem, slides 2-N = steps, last = result/CTA.",
"listicle": "Curated list. Each slide = one item with bold title + 1-2 sentence explanation.",
"myth-busting": "Each slide = one myth debunked. Format: 'MYTH: [belief]''TRUTH: [reality]'.",
"framework": "Introduce a proprietary framework. Each slide = one component of the framework.",
"story-arc": "Transformation story. Slide 1 = before, middle = journey, last = after + CTA.",
}
def main():
parser = argparse.ArgumentParser(description="Generate a LinkedIn Carousel prompt")
parser.add_argument("--topic", required=True)
parser.add_argument("--niche", required=True)
parser.add_argument("--slides", required=False, type=int, default=7)
parser.add_argument("--style", required=False, default="listicle", choices=list(CAROUSEL_STYLES.keys()))
args = parser.parse_args()
slides = max(3, min(args.slides, 12))
style_instruction = CAROUSEL_STYLES.get(args.style, CAROUSEL_STYLES["listicle"])
context = get_base_prompt_context(args.niche, "LinkedIn Carousel")
prompt = f"""{context}
<TASK>
Generate a complete LinkedIn Carousel with exactly {slides} slides.
**Topic**: {args.topic}
**Niche**: {args.niche}
**Style**: {args.style.upper()}{style_instruction}
Slide structure:
- Slide 1 (Cover): Massive hook headline (max 8 words) + optional 1-sentence sub-headline
- Slides 2{slides-1}: Follow the "{args.style}" style. Bold Title + 2-3 lines per slide.
- Slide {slides} (CTA): One clear action (e.g., "Follow for more", "Save this for later")
After the slides, provide:
---
📝 LinkedIn Caption:
- Hook line (different wording from Slide 1, same energy)
- 2-3 lines of teaser context
- "Swipe to see all {slides}"
- 3-5 hashtags
Output slides numbered clearly. No extra commentary.
</TASK>"""
print(prompt)
if __name__ == "__main__":
main()
@@ -0,0 +1,64 @@
"""
generate_newsletter.py — LinkedIn Newsletter Prompt Builder
Usage:
python generate_newsletter.py --topic "<topic>" --niche "<niche>" [--title "<title>"] [--length <length>]
Length: short (~700w) | medium (~1200w) | long (~2000w)
"""
import argparse
import sys
import os
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRIPT_DIR)
from utils import get_base_prompt_context
LENGTH_GUIDE = {
"short": "~600-800 words. Quick, punchy, skimmable. 2-3 sections max.",
"medium": "~1000-1400 words. Balanced depth and readability. 3-4 sections.",
"long": "~1800-2500 words. Deep dive. 4-6 sections with subsections.",
}
def main():
parser = argparse.ArgumentParser(description="Generate a LinkedIn Newsletter prompt")
parser.add_argument("--topic", required=True)
parser.add_argument("--niche", required=True)
parser.add_argument("--title", required=False, default="")
parser.add_argument("--length", required=False, default="medium", choices=list(LENGTH_GUIDE.keys()))
args = parser.parse_args()
length_instruction = LENGTH_GUIDE.get(args.length, LENGTH_GUIDE["medium"])
title_line = f"**Newsletter Title**: {args.title}" if args.title else "**Newsletter Title**: Generate a compelling SEO-optimised headline."
context = get_base_prompt_context(args.niche, "LinkedIn Newsletter Article")
prompt = f"""{context}
<TASK>
Generate a complete LinkedIn Newsletter edition.
**Topic**: {args.topic}
**Niche**: {args.niche}
{title_line}
**Length**: {args.length.upper()}{length_instruction}
Required structure:
1. Headline (H1) — catchy, SEO-optimised, keyword-rich
2. Opening Hook — personal anecdote, surprising statistic, or bold claim (2-3 sentences)
3. Body Sections (H2 subheadings) — background, insights, examples, data
4. Key Takeaways — bulleted list (3-5 items)
5. Action Step — 1 specific thing to do this week
6. Engagement Question — ask 1 question to spark comments
Formatting: Markdown (H1, H2, H3, bold, bullets). Short paragraphs only.
Output ONLY the final newsletter. No commentary.
</TASK>"""
print(prompt)
if __name__ == "__main__":
main()
@@ -0,0 +1,77 @@
"""
generate_post.py — LinkedIn Post Prompt Builder
Usage:
python generate_post.py --topic "<topic>" --niche "<niche>" [--tone <tone>] [--style <style>]
Tone: professional | storytelling | controversial | educational | motivational
Style: text-only | list-based | storytelling | data-driven | contrarian
"""
import argparse
import sys
import os
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRIPT_DIR)
from utils import get_base_prompt_context
TONE_GUIDE = {
"professional": "Write with authority and expertise. Clear, polished, data-backed where possible.",
"storytelling": "Lead with a personal story or narrative. Make the reader feel something before delivering the insight.",
"controversial": "Take a bold, contrarian stance. Challenge the conventional wisdom in the niche. Prepare for debate.",
"educational": "Break down a complex concept simply. Use analogies, numbered steps, or mini-frameworks.",
"motivational": "Inspire and energize. Use strong action verbs. Make the reader feel capable and driven.",
}
STYLE_GUIDE = {
"text-only": "Write as flowing text paragraphs. No bullet points. Pure conversational prose.",
"list-based": "Structure the core value as a numbered or bulleted list. Maximum 7 items.",
"storytelling":"Write as a narrative arc: Setup → Conflict → Resolution → Lesson.",
"data-driven": "Anchor every key point with a statistic, study, or concrete example.",
"contrarian": "Start by stating what everyone believes, then flip it. Use 'But here's what they miss:' or similar.",
}
def main():
parser = argparse.ArgumentParser(description="Generate a LinkedIn Post prompt")
parser.add_argument("--topic", required=True)
parser.add_argument("--niche", required=True)
parser.add_argument("--tone", required=False, default="professional", choices=list(TONE_GUIDE.keys()))
parser.add_argument("--style", required=False, default="list-based", choices=list(STYLE_GUIDE.keys()))
args = parser.parse_args()
tone_instruction = TONE_GUIDE.get(args.tone, TONE_GUIDE["professional"])
style_instruction = STYLE_GUIDE.get(args.style, STYLE_GUIDE["list-based"])
context = get_base_prompt_context(args.niche, "LinkedIn Text Post")
prompt = f"""{context}
<TASK>
Generate a single, ready-to-publish LinkedIn post.
**Topic**: {args.topic}
**Niche**: {args.niche}
**Tone**: {tone_instruction}
**Style**: {style_instruction}
Mandatory output structure:
1. Hook (2 lines — scroll-stopping)
2. [blank line]
3. Body (follow tone + style instructions)
4. [blank line]
5. Key Takeaway (1-2 punchy sentences)
6. [blank line]
7. CTA (specific, value-driven)
8. [blank line]
9. Hashtags (3-5 only)
Output ONLY the final post. No preamble. Ready to paste into LinkedIn.
</TASK>"""
print(prompt)
if __name__ == "__main__":
main()
@@ -0,0 +1,49 @@
# LinkedIn Content Memory
This file is the reinforcement learning database for the LinkedIn Content Skill.
It is automatically read by every generator script to personalise your content.
Use `/feedback` to update it. Use `/show-memory` to review it.
---
## 🧠 Core Identity & Tone
- **Primary Niche:** (Update this — e.g. "AI & Technology", "Marketing", "SaaS")
- **Tone:** Professional, insightful, concise, and story-driven.
- **Voice:** First-person. Confident but humble. Write to one person, not an audience.
- **Formatting Preference:** Short paragraphs (1-2 sentences). Aggressive line breaks. Bullet points over dense paragraphs.
- **Emojis:** Use sparingly — 2-3 max per post, only where they genuinely add value.
- **CTA Style:** Specific and value-driven. Never "like and share" — always give a reason.
---
## 🎯 Successful Hooks
> Add hooks that received high engagement here.
- (Empty — use `/feedback` to add your first successful hook)
---
## 📈 Top Performing Formats
> Note which content formats get the best reactions.
- (Empty — use `/feedback` to log your best performing format)
---
## 🔑 High-Performing Topics
> Track which topics resonate most with your audience.
- (Empty — use `/feedback` to log topics that hit well)
---
## 🚫 What to Avoid
> Patterns, phrases, or formats that underperformed.
- Avoid cliché openers like "In today's fast-paced world..."
- Avoid posting without a clear CTA
- Avoid hashtag stuffing (max 5)
---
## 📝 Positive Feedback Log
### [2026-06-01 21:58] — test-01
- **What worked:** Great hook!
- **Tags:** `hook`
@@ -0,0 +1,134 @@
"""
memory_manager.py — Reinforcement Learning Memory Manager for LinkedIn Content Skill.
Resolves memory.md relative to this script's location (inside .claude/skills/scripts/).
Commands:
python memory_manager.py add --id <id> --feedback <text> [--tags <tags>]
python memory_manager.py read
python memory_manager.py clear
"""
import argparse
import json
import os
import sys
from datetime import datetime
# ─── Configuration ────────────────────────────────────────────────────────────
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
MEMORY_FILE = os.path.join(SCRIPT_DIR, "memory.md")
MEMORY_TEMPLATE = """# LinkedIn Content Memory
This file is the reinforcement learning database for the LinkedIn Content Skill.
It is automatically read by every generator script to personalise your content.
Use `/feedback` to update it. Use `/show-memory` to review it.
---
## 🧠 Core Identity & Tone
- **Primary Niche:** (Update this — e.g. "AI & Technology", "Marketing", "SaaS")
- **Tone:** Professional, insightful, concise, and story-driven.
- **Voice:** First-person. Confident but humble. Write to one person, not an audience.
- **Formatting Preference:** Short paragraphs (1-2 sentences). Aggressive line breaks. Bullet points over dense paragraphs.
- **Emojis:** Use sparingly — 2-3 max per post, only where they genuinely add value.
- **CTA Style:** Specific and value-driven. Never "like and share" — always give a reason.
---
## 🎯 Successful Hooks
> Add hooks that received high engagement here.
- (Empty — use `/feedback` to add your first successful hook)
---
## 📈 Top Performing Formats
> Note which content formats get the best reactions.
- (Empty — use `/feedback` to log your best performing format)
---
## 🔑 High-Performing Topics
> Track which topics resonate most with your audience.
- (Empty — use `/feedback` to log topics that hit well)
---
## 🚫 What to Avoid
> Patterns, phrases, or formats that underperformed.
- Avoid cliché openers like "In today's fast-paced world..."
- Avoid posting without a clear CTA
- Avoid hashtag stuffing (max 5)
---
## 📝 Positive Feedback Log
"""
def ensure_memory_exists():
if not os.path.exists(MEMORY_FILE):
with open(MEMORY_FILE, "w", encoding="utf-8") as f:
f.write(MEMORY_TEMPLATE)
def read_memory() -> str:
ensure_memory_exists()
with open(MEMORY_FILE, "r", encoding="utf-8") as f:
return f.read()
def append_feedback(content_id: str, feedback_text: str, tags: str = ""):
ensure_memory_exists()
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
entry = f"\n### [{timestamp}] — {content_id}\n"
entry += f"- **What worked:** {feedback_text}\n"
if tags:
entry += f"- **Tags:** `{tags.strip()}`\n"
with open(MEMORY_FILE, "a", encoding="utf-8") as f:
f.write(entry)
print(json.dumps({
"status": "success",
"message": f"✅ Memory updated in {MEMORY_FILE}",
"entry_id": content_id,
"timestamp": timestamp,
"instruction": "This feedback will now be injected into all future content generation prompts."
}, indent=2))
def clear_memory():
with open(MEMORY_FILE, "w", encoding="utf-8") as f:
f.write(MEMORY_TEMPLATE)
print(json.dumps({
"status": "success",
"message": "✅ Memory has been cleared and reset to defaults."
}, indent=2))
def main():
parser = argparse.ArgumentParser(description="LinkedIn Content Skill — Memory Manager")
subparsers = parser.add_subparsers(dest="command", required=True)
add_parser = subparsers.add_parser("add", help="Save positive feedback to memory")
add_parser.add_argument("--id", required=True)
add_parser.add_argument("--feedback", required=True)
add_parser.add_argument("--tags", required=False, default="")
subparsers.add_parser("read", help="Display current memory")
subparsers.add_parser("clear", help="Reset memory to defaults")
args = parser.parse_args()
if args.command == "add":
append_feedback(args.id, args.feedback, args.tags)
elif args.command == "read":
print(read_memory())
elif args.command == "clear":
clear_memory()
if __name__ == "__main__":
main()
@@ -0,0 +1,96 @@
"""
utils.py — Shared prompt-building utilities for the LinkedIn Content Skill.
Reads the user's reinforcement learning memory from memory.md (same directory)
and constructs richly engineered system prompts for Claude to consume.
"""
import os
import sys
# Always resolve paths relative to THIS script's location (inside scripts/)
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
MEMORY_FILE = os.path.join(SCRIPT_DIR, "memory.md")
# ─── LinkedIn SEO Rules ───────────────────────────────────────────────────────
LINKEDIN_SEO_RULES = """
## LinkedIn SEO & Content Rules (MANDATORY — Follow Exactly)
### Hook Engineering (Most Critical)
- Line 1 MUST be a scroll-stopping hook. Use one of these proven formats:
a) Bold contrarian statement: "Most LinkedIn advice is wrong. Here's why."
b) Surprising statistic: "95% of LinkedIn posts get fewer than 100 views. Here's the 5% secret."
c) Provocative question: "What if everything you knew about personal branding was backwards?"
d) Personal story opener: "3 years ago, I had 47 LinkedIn followers. Here's what changed."
- Line 2 MUST create a pattern interrupt — force the reader to click "see more"
- NEVER start with: "In today's...", "I am excited to...", "Happy to share...", "Thrilled to announce..."
### Content Structure
- Hook (2 lines, must not trigger "see more" cutoff)
- [blank line]
- Context/Problem (2-3 short sentences max)
- [blank line]
- Core Value (use numbered lists or bullets — max 7 items)
- [blank line]
- Key Takeaway (1-2 punchy sentences)
- [blank line]
- Call to Action (1 specific, non-generic CTA)
- [blank line]
- Hashtags (3-5 only — mix broad + niche)
### Readability Rules
- Maximum 2 sentences per paragraph
- Use line breaks aggressively — white space wins on LinkedIn
- Bold sparingly, only for truly critical points
- Sentences: short, punchy, declarative. Vary rhythm.
- Reading level: Grade 8 or below
### Tone & Voice
- Write like you're talking to ONE person, not an audience
- Use "you" and "I" — personal, not corporate
- Confident, not arrogant. Helpful, not preachy.
- Zero jargon unless explaining it is the point
### Hashtag Strategy
- 1 broad hashtag (#AI, #Marketing, #Leadership)
- 2 niche hashtags (#AIAgents, #ContentMarketing, #StartupLife)
- 1-2 community hashtags (#LinkedInTips, #PersonalBranding)
- Total: NEVER more than 5
"""
def read_memory() -> str:
"""Read and return the full contents of memory.md."""
if not os.path.exists(MEMORY_FILE):
return "No memory found. Use /feedback to start building personalised memory."
with open(MEMORY_FILE, "r", encoding="utf-8") as f:
return f.read()
def get_base_prompt_context(niche: str, content_type: str) -> str:
"""
Build a complete system prompt context for the AI.
Injects LinkedIn SEO rules + the user's personal reinforcement learning memory.
"""
memory_context = read_memory()
prompt = f"""<SYSTEM_INSTRUCTION>
You are an elite LinkedIn Content Strategist and Copywriter working for a specific user.
Your task is to generate a world-class {content_type} for the niche: "{niche}".
{LINKEDIN_SEO_RULES}
## User's Personal Memory & Preferences (HIGHEST PRIORITY)
The following reinforcement learning memory reflects what has worked for this user.
You MUST prioritize and replicate these patterns in your output:
<MEMORY>
{memory_context}
</MEMORY>
If memory contains specific hooks, tones, or formats that worked well — USE THEM as inspiration.
If memory is empty — default to the SEO rules above and high-performing LinkedIn best practices.
</SYSTEM_INSTRUCTION>"""
return prompt