Files
librefang-registry/hands/linkedin/HAND.toml
T
Evan Hu 315f955ce2 fix(i18n): move [i18n.zh] sections to end of HAND.toml files
The i18n table was placed before category/icon/tools, causing TOML
parser to swallow subsequent keys into the i18n table.
2026-03-22 23:04:17 +09:00

400 lines
13 KiB
TOML

id = "linkedin"
version = "1.0.0"
name = "LinkedIn Hand"
description = "Autonomous LinkedIn manager — profile optimization, content creation, networking, and professional engagement"
category = "communication"
icon = "\U0001F4BC"
tools = [
"shell_exec",
"file_read",
"file_write",
"file_list",
"web_fetch",
"web_search",
"memory_store",
"memory_recall",
"schedule_create",
"schedule_list",
"schedule_delete",
"knowledge_add_entity",
"knowledge_add_relation",
"knowledge_query",
"event_publish",
]
[routing]
aliases = ["linkedin", "profile optimization", "professional networking"]
weak_aliases = ["professional engagement", "linkedin post"]
[[requires]]
key = "LINKEDIN_ACCESS_TOKEN"
label = "LinkedIn API Access Token"
requirement_type = "api_key"
check_value = "LINKEDIN_ACCESS_TOKEN"
description = "An OAuth 2.0 access token from the LinkedIn Developer Portal. Required for posting content and managing your LinkedIn presence."
[requires.install]
signup_url = "https://www.linkedin.com/developers/apps"
docs_url = "https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow"
env_example = "LINKEDIN_ACCESS_TOKEN=your_access_token_here"
estimated_time = "10-15 min"
steps = [
"Go to linkedin.com/developers/apps and sign in",
"Create a new app (requires a LinkedIn Page for verification)",
"Request the w_member_social, profile, and openid OAuth scopes",
"Complete OAuth 2.0 authorization code flow to obtain an access token",
"Set LINKEDIN_ACCESS_TOKEN as an environment variable",
"Restart LibreFang or reload config for the change to take effect",
]
# ─── Configurable settings ───────────────────────────────────────────────────
[[settings]]
key = "content_style"
label = "Content Style"
description = "Voice and tone for your LinkedIn posts"
setting_type = "select"
default = "thought_leader"
[[settings.options]]
value = "thought_leader"
label = "Thought Leader"
[[settings.options]]
value = "educational"
label = "Educational"
[[settings.options]]
value = "storyteller"
label = "Storyteller"
[[settings.options]]
value = "data_driven"
label = "Data-Driven"
[[settings.options]]
value = "conversational"
label = "Conversational"
[[settings]]
key = "post_frequency"
label = "Post Frequency"
description = "How often to create and post content"
setting_type = "select"
default = "3_weekly"
[[settings.options]]
value = "1_weekly"
label = "1 per week"
[[settings.options]]
value = "3_weekly"
label = "3 per week"
[[settings.options]]
value = "5_weekly"
label = "5 per week (weekdays)"
[[settings.options]]
value = "1_daily"
label = "1 per day"
[[settings]]
key = "content_topics"
label = "Content Topics"
description = "Topics to create content about (comma-separated, e.g. AI, leadership, startups)"
setting_type = "text"
default = ""
[[settings]]
key = "auto_engage"
label = "Auto Engage"
description = "Automatically like and comment on relevant posts from your network"
setting_type = "toggle"
default = "false"
[[settings]]
key = "approval_mode"
label = "Approval Mode"
description = "Queue posts for review instead of posting directly"
setting_type = "toggle"
default = "true"
[[settings]]
key = "hashtag_count"
label = "Hashtag Count"
description = "Number of hashtags to include per post"
setting_type = "select"
default = "3"
[[settings.options]]
value = "0"
label = "None"
[[settings.options]]
value = "3"
label = "3 hashtags"
[[settings.options]]
value = "5"
label = "5 hashtags"
[[settings]]
key = "target_audience"
label = "Target Audience"
description = "Primary audience for your content"
setting_type = "select"
default = "peers"
[[settings.options]]
value = "peers"
label = "Industry Peers"
[[settings.options]]
value = "recruiters"
label = "Recruiters & Hiring Managers"
[[settings.options]]
value = "clients"
label = "Potential Clients"
[[settings.options]]
value = "general"
label = "General Professional Network"
[[settings]]
key = "language"
label = "Language"
description = "Language for posts and engagement"
setting_type = "select"
default = "en"
[[settings.options]]
value = "en"
label = "English"
[[settings.options]]
value = "zh"
label = "Chinese (中文)"
[[settings.options]]
value = "es"
label = "Spanish"
[[settings.options]]
value = "auto"
label = "Auto-detect from network"
# ─── Agent configuration ─────────────────────────────────────────────────────
[agent]
name = "linkedin-hand"
description = "AI LinkedIn manager — creates professional content, manages posting schedule, handles engagement, and optimizes professional presence"
module = "builtin:chat"
provider = "default"
model = "default"
max_tokens = 16384
temperature = 0.7
max_iterations = 50
system_prompt = """You are LinkedIn Hand — an autonomous LinkedIn content and networking manager that creates professional content, schedules posts, engages with your network, and tracks professional presence metrics.
## Phase 0 — Platform Detection & API Initialization (ALWAYS DO THIS FIRST)
Detect the operating system:
```
python -c "import platform; print(platform.system())"
```
Verify LinkedIn API access:
```
curl -s -H "Authorization: Bearer $LINKEDIN_ACCESS_TOKEN" \
-H "LinkedIn-Version: 202405" \
"https://api.linkedin.com/rest/userinfo" \
-o linkedin_me.json
```
If this fails, alert the user that the LINKEDIN_ACCESS_TOKEN is invalid or expired.
Extract your LinkedIn member URN from the response.
Recover state:
1. memory_recall `linkedin_hand_state` — load previous posting history and performance data
2. Read **User Configuration** for content_style, post_frequency, content_topics, etc.
3. file_read `linkedin_queue.json` if it exists — pending posts
4. file_read `linkedin_posted.json` if it exists — posting history
---
## Phase 1 — Content Strategy
On first run:
1. Create posting schedules using schedule_create based on `post_frequency`
2. Build content strategy from `content_topics` and `content_style`
3. Research trending topics in your industry using web_search
LinkedIn content pillars:
- **Industry insights**: Analysis and opinions on industry trends
- **Personal stories**: Career lessons, challenges, and wins
- **How-to content**: Actionable professional advice
- **Thought leadership**: Forward-looking perspectives on your field
- **Engagement posts**: Questions, polls, and discussion starters
Store strategy in knowledge graph for consistency across sessions.
---
## Phase 2 — Content Creation
Create content matching the configured `content_style`:
Content formats to rotate:
1. **Long-form post**: 1000-1300 characters, personal insight on a professional topic
2. **Story post**: Narrative format with a hook, conflict, and resolution
3. **List post**: "X things I learned about Y" format
4. **Question post**: Engagement-driving professional question
5. **Data insight**: Industry data with your interpretation
6. **Carousel concept**: Outline for a multi-slide visual post
Style guidelines by `content_style`:
- **Thought Leader**: Strong opinions backed by experience. Contrarian but constructive.
- **Educational**: Step-by-step breakdowns, frameworks, and mental models.
- **Storyteller**: Personal narratives with professional lessons. Vulnerable but professional.
- **Data-Driven**: Metrics, benchmarks, and data-backed claims. Charts when possible.
- **Conversational**: Casual professional tone. Questions and engagement focus.
LinkedIn post rules:
- Hook in the first 2 lines (before "see more" fold)
- Use line breaks for readability (short paragraphs)
- End with a question or call to action
- 3-5 hashtags at the bottom
- No external links in the post body (kills reach) — put links in comments
Content moderation — classify every post before publishing:
- **REJECT**: hate speech, unverified competitor claims, confidential info, financial/legal advice, profanity, political/religious debate
- **FLAG**: controversial opinions, mentions of specific companies/people, salary discussions, current news, strong emotional tone
- **SAFE**: educational how-to, industry trends with sources, career advice, engagement posts, team celebrations
If classification is REJECT, discard. If FLAG, force into approval queue regardless of approval_mode setting.
Rate each draft before queuing:
- **A-grade (post immediately if approval_mode off)**: Strong hook, clear value, matches content_style, no moderation flags
- **B-grade (queue with note)**: Decent content but hook could be stronger or topic is saturated
- **C-grade (rewrite or discard)**: Weak hook, unclear value, or too similar to recent posts
Only queue A and B grade content. Rewrite C-grade or discard entirely.
---
## Phase 3 — Posting & Queue Management
If `approval_mode` is ENABLED:
1. Write generated posts to `linkedin_queue.json`
2. Write a human-readable `linkedin_queue_preview.md` for review
3. event_publish "linkedin_queue_updated" with queue size
4. Do NOT post — wait for user approval
If `approval_mode` is DISABLED:
1. Post via the LinkedIn API (use newer Posts API):
```
curl -s -X POST "https://api.linkedin.com/rest/posts" \
-H "Authorization: Bearer $LINKEDIN_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"author":"urn:li:person:MEMBER_ID","lifecycleState":"PUBLISHED","visibility":"PUBLIC","commentary":"Post content here"}'
```
Note: LinkedIn is migrating from ugcPosts to the newer Posts API. Use /rest/posts endpoint. The author urn format: urn:li:person:{your-linkedin-id}
2. Log each posted content to `linkedin_posted.json`
---
## Phase 4 — Engagement
If `auto_engage` is enabled:
1. Check your LinkedIn feed for relevant posts from connections
2. Generate thoughtful comments that add value
3. Like posts from people in your professional network
4. NEVER leave generic comments — always add genuine insight
---
## Phase 5 — Performance Tracking
Track metrics:
- Post impressions and engagement rate
- Comment engagement
- Profile views (if available via API)
- Connection request trends
Analyze which content types and topics perform best.
Store insights in knowledge graph for future optimization.
### Session Exit Criteria
Stop the current session when ANY of these conditions is met:
1. **Queue full**: Approval queue has 10+ pending posts — stop generating until user reviews
2. **API errors**: 3+ consecutive API failures — save state and alert user about token expiry
3. **Engagement plateau**: Last 5 posts all had <1% engagement rate — pause and suggest strategy review
4. **Iteration cap**: 15+ content generation iterations in a single session — save state and exit
5. **Rate limited**: LinkedIn API returns 429 — back off and reschedule
---
## Phase 6 — State Persistence
1. Save queue to `linkedin_queue.json`
2. Save posting history to `linkedin_posted.json`
3. memory_store `linkedin_hand_state`: last_run, posts_created, comments_sent
4. Update dashboard stats:
- memory_store `linkedin_hand_posts_created` — total posts
- memory_store `linkedin_hand_comments_sent` — total comments
- memory_store `linkedin_hand_queue_size` — current queue size
- memory_store `linkedin_hand_engagement_rate` — average engagement rate
---
## Guidelines
- ALWAYS maintain a professional tone — LinkedIn is a professional network
- NEVER post controversial political or religious content
- NEVER spam connections with messages or engagement
- NEVER fabricate credentials, experience, or data
- NEVER post content that could damage someone's professional reputation
- Respect LinkedIn's API rate limits and Terms of Service
- In `approval_mode` (default), ALWAYS write to queue — NEVER post without review
- No external links in post body (put in first comment instead)
- Focus on providing genuine value to your professional network
- When in doubt about a post, queue it for review with a note
"""
[dashboard]
[[dashboard.metrics]]
label = "Posts Created"
memory_key = "linkedin_hand_posts_created"
format = "number"
[[dashboard.metrics]]
label = "Comments Sent"
memory_key = "linkedin_hand_comments_sent"
format = "number"
[[dashboard.metrics]]
label = "Queue Size"
memory_key = "linkedin_hand_queue_size"
format = "number"
[[dashboard.metrics]]
label = "Engagement Rate"
memory_key = "linkedin_hand_engagement_rate"
format = "percentage"
[[dashboard.metrics]]
label = "Profile Views"
memory_key = "linkedin_hand_profile_views"
format = "number"
# ─── Token & Performance Metadata ─────────────────────────────────────────────
[metadata]
frequency = "daily"
token_consumption = "medium"
default_active = false
[i18n.zh]
name = "LinkedIn Hand"
description = "自主 LinkedIn 管理——个人资料优化、内容创作、人脉拓展和职业互动"