a digital person for bluesky

Create distinct tools for posting vs replying

- Add new reply_to_bluesky_post tool with required URI parameters
- Rename post_to_bluesky to create_new_bluesky_post
- Add explicit descriptions to prevent tool confusion

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+75 -2
+17 -2
tools/post.py
··· 6 6 text: str = Field(..., description="The text content to post (max 300 characters)") 7 7 8 8 9 - def post_to_bluesky(text: str) -> str: 10 - """Post a message to Bluesky.""" 9 + def create_new_bluesky_post(text: str) -> str: 10 + """ 11 + Create a NEW standalone post on Bluesky. This tool creates independent posts that 12 + start new conversations. 13 + 14 + IMPORTANT: This tool is ONLY for creating new posts. To reply to an existing post, 15 + use reply_to_bluesky_post instead. 16 + 17 + Args: 18 + text: The post content (max 300 characters) 19 + 20 + Returns: 21 + Success message with post URL 22 + 23 + Raises: 24 + Exception: If the post fails 25 + """ 11 26 import os 12 27 import requests 13 28 from datetime import datetime, timezone
+58
tools/reply.py
··· 1 + """Reply to Bluesky posts tool.""" 2 + 3 + from typing import Optional 4 + from pydantic import BaseModel, Field 5 + from atproto import Client 6 + from tools.base_tool import get_bsky_client, log 7 + 8 + class ReplyArgs(BaseModel): 9 + """Arguments for replying to a Bluesky post.""" 10 + text: str = Field(..., description="The reply text content (max 300 characters)") 11 + reply_to_uri: str = Field(..., description="The AT URI of the post being replied to (required)") 12 + reply_to_cid: str = Field(..., description="The CID of the post being replied to (required)") 13 + 14 + def reply_to_bluesky_post(text: str, reply_to_uri: str, reply_to_cid: str) -> str: 15 + """ 16 + Reply to an existing Bluesky post. This tool creates a threaded reply to a specific post. 17 + 18 + IMPORTANT: This tool is ONLY for replying to existing posts. To create a new standalone post, 19 + use create_new_bluesky_post instead. 20 + 21 + Args: 22 + text: The reply text content (max 300 characters) 23 + reply_to_uri: The AT URI of the post being replied to (required) 24 + reply_to_cid: The CID of the post being replied to (required) 25 + 26 + Returns: 27 + Success message with post URI or error message 28 + 29 + Raises: 30 + Exception: If the reply fails 31 + """ 32 + if len(text) > 300: 33 + raise ValueError(f"Reply text too long: {len(text)} characters (max 300)") 34 + 35 + if not reply_to_uri: 36 + raise ValueError("reply_to_uri is required for replies") 37 + 38 + if not reply_to_cid: 39 + raise ValueError("reply_to_cid is required for replies") 40 + 41 + try: 42 + client = get_bsky_client() 43 + 44 + # Create the reply reference 45 + reply_ref = { 46 + "root": {"uri": reply_to_uri, "cid": reply_to_cid}, 47 + "parent": {"uri": reply_to_uri, "cid": reply_to_cid} 48 + } 49 + 50 + # Send the reply 51 + response = client.send_post(text=text, reply_to=reply_ref) 52 + 53 + log.info(f"Successfully replied to post {reply_to_uri}") 54 + return f"Successfully posted reply: {response.uri}" 55 + 56 + except Exception as e: 57 + log.error(f"Failed to reply to post: {str(e)}") 58 + raise