Add 12 card management tools: - trello_create_card — create a card on a list - trello_card_details — view card with members, checklists, comments - trello_update_card — update title, description, due date - trello_move_card — move card to a different list - trello_archive_card — archive a card - trello_assign_member / trello_remove_member — member management - trello_add_comment / trello_delete_comment — comments - trello_add_checklist_item / trello_toggle_checklist_item / trello_delete_checklist_item Also adds _delete() helper on TrelloClient for DELETE verbs. 73 total tests — all passing. Issue: #4
554 lines
13 KiB
Python
554 lines
13 KiB
Python
"""
|
|
Trello Plugin — Hermes Agent tool definitions.
|
|
|
|
Each function decorated with ``@tool`` registers itself as a Hermes tool
|
|
that the agent can invoke during a conversation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from typing import Any
|
|
|
|
from trello_plugin.client import TrelloClient
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Module-level client (lazily initialised so env vars can be overridden)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_client: TrelloClient | None = None
|
|
|
|
|
|
def _get_client() -> TrelloClient:
|
|
global _client # noqa: PLW0603
|
|
api_key = os.environ.get("TRELLO_API_KEY", "")
|
|
token = os.environ.get("TRELLO_TOKEN", "")
|
|
if _client is None or _client.api_key != api_key or _client.token != token:
|
|
_client = TrelloClient(api_key=api_key, token=token)
|
|
return _client
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tool helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _respond(data: dict[str, Any]) -> str:
|
|
"""Wrap a result dict in a JSON string, the Hermes tool contract."""
|
|
return json.dumps(data, indent=2)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tools
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def trello_verify_credentials() -> str:
|
|
"""Verify that the Trello API key and token are valid.
|
|
|
|
Checks the ``TRELLO_API_KEY`` and ``TRELLO_TOKEN`` environment variables
|
|
and attempts a test call to the Trello API. Returns the authenticated
|
|
user's Trello username on success.
|
|
"""
|
|
client = _get_client()
|
|
result = client.verify_credentials()
|
|
return _respond(result)
|
|
|
|
|
|
def trello_list_boards() -> str:
|
|
"""List all Trello boards accessible to the authenticated user.
|
|
|
|
Requires ``TRELLO_API_KEY`` and ``TRELLO_TOKEN`` to be set.
|
|
Returns board id, name, URL, and whether the board is closed/starred.
|
|
"""
|
|
client = _get_client()
|
|
result = client.list_boards()
|
|
return _respond(result)
|
|
|
|
|
|
def trello_disconnect() -> str:
|
|
"""Disconnect from Trello by clearing credentials from memory.
|
|
|
|
This does NOT revoke the Trello token — invalidate it via
|
|
Trello's account settings if full revocation is desired.
|
|
"""
|
|
global _client # noqa: PLW0603
|
|
_client = None
|
|
return _respond({"success": True, "message": "Trello credentials cleared from memory. Next tool call will re-read TRELLO_API_KEY and TRELLO_TOKEN from environment and reconnect automatically."})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tools — Board Management
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def trello_create_board(name: str, default_lists: bool = True) -> str: # noqa: FBT001, FBT002
|
|
"""Create a new Trello board.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name for the new board.
|
|
default_lists : bool, optional
|
|
Whether to create default lists (default: True).
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with board id, name, and URL on success.
|
|
"""
|
|
client = _get_client()
|
|
result = client.create_board(name=name, default_lists=default_lists)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_rename_board(board_id: str, name: str) -> str:
|
|
"""Rename an existing Trello board.
|
|
|
|
Parameters
|
|
----------
|
|
board_id : str
|
|
Board ID or name.
|
|
name : str
|
|
The new name for the board.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with updated board details on success.
|
|
"""
|
|
client = _get_client()
|
|
result = client.rename_board(board_id=board_id, name=name)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_archive_board(board_id: str) -> str:
|
|
"""Close/archive a Trello board.
|
|
|
|
Parameters
|
|
----------
|
|
board_id : str
|
|
Board ID or name.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.archive_board(board_id=board_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_open_board(board_id: str) -> str:
|
|
"""Re-open a closed/archived Trello board.
|
|
|
|
Parameters
|
|
----------
|
|
board_id : str
|
|
Board ID or name.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.open_board(board_id=board_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_board_details(board_id: str) -> str:
|
|
"""View details of a Trello board, including its lists and members.
|
|
|
|
Parameters
|
|
----------
|
|
board_id : str
|
|
Board ID or name.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with board details including lists and members.
|
|
"""
|
|
client = _get_client()
|
|
result = client.board_details(board_id=board_id)
|
|
return _respond(result)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tools — List Management
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def trello_create_list(name: str, board_id: str, pos: str = "bottom") -> str:
|
|
"""Create a new list on a Trello board.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name for the new list.
|
|
board_id : str
|
|
Board ID or name.
|
|
pos : str, optional
|
|
Position: ``"top"``, ``"bottom"``, or a number (default: ``"bottom"``).
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with list details on success.
|
|
"""
|
|
client = _get_client()
|
|
result = client.create_list(name=name, board_id=board_id, pos=pos)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_rename_list(list_id: str, name: str) -> str:
|
|
"""Rename an existing list.
|
|
|
|
Parameters
|
|
----------
|
|
list_id : str
|
|
Trello list ID.
|
|
name : str
|
|
The new name.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with updated list details on success.
|
|
"""
|
|
client = _get_client()
|
|
result = client.rename_list(list_id=list_id, name=name)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_archive_list(list_id: str) -> str:
|
|
"""Archive a list.
|
|
|
|
Parameters
|
|
----------
|
|
list_id : str
|
|
Trello list ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.archive_list(list_id=list_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_move_list(list_id: str, pos: str = "bottom") -> str:
|
|
"""Move a list to a different position on the board.
|
|
|
|
Parameters
|
|
----------
|
|
list_id : str
|
|
Trello list ID.
|
|
pos : str, optional
|
|
Position: ``"top"``, ``"bottom"``, or a number (default: ``"bottom"``).
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.move_list(list_id=list_id, pos=pos)
|
|
return _respond(result)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tools — Card Management
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def trello_create_card(name: str, list_id: str, desc: str = "", due: str | None = None) -> str:
|
|
"""Create a new card on a Trello list.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
Card title.
|
|
list_id : str
|
|
List ID.
|
|
desc : str, optional
|
|
Card description.
|
|
due : str, optional
|
|
Due date in ISO 8601 format.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with card details.
|
|
"""
|
|
client = _get_client()
|
|
result = client.create_card(name=name, list_id=list_id, desc=desc, due=due)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_card_details(card_id: str) -> str:
|
|
"""View detailed information about a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with full card details including members, checklists, and comments.
|
|
"""
|
|
client = _get_client()
|
|
result = client.card_details(card_id=card_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_update_card(card_id: str, name: str | None = None, desc: str | None = None, due: str | None = None) -> str:
|
|
"""Update a card's core attributes (title, description, due date).
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
name : str, optional
|
|
New title.
|
|
desc : str, optional
|
|
New description.
|
|
due : str, optional
|
|
New due date ISO 8601.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with updated card details.
|
|
"""
|
|
client = _get_client()
|
|
result = client.update_card(card_id=card_id, name=name, desc=desc, due=due)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_move_card(card_id: str, list_id: str) -> str:
|
|
"""Move a card to a different list.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
list_id : str
|
|
Target list ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.move_card(card_id=card_id, list_id=list_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_archive_card(card_id: str) -> str:
|
|
"""Archive a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.archive_card(card_id=card_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_assign_member(card_id: str, member_id: str) -> str:
|
|
"""Add a member to a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
member_id : str
|
|
Trello member ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.assign_member(card_id=card_id, member_id=member_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_remove_member(card_id: str, member_id: str) -> str:
|
|
"""Remove a member from a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
member_id : str
|
|
Trello member ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.remove_member(card_id=card_id, member_id=member_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_add_comment(card_id: str, text: str) -> str:
|
|
"""Add a comment to a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
text : str
|
|
Comment text.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with comment ID.
|
|
"""
|
|
client = _get_client()
|
|
result = client.add_comment(card_id=card_id, text=text)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_delete_comment(card_id: str, comment_id: str) -> str:
|
|
"""Delete a comment from a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
comment_id : str
|
|
Comment/action ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.delete_comment(card_id=card_id, comment_id=comment_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_add_checklist_item(card_id: str, name: str, checklist_id: str | None = None) -> str:
|
|
"""Add a checklist item to a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
name : str
|
|
Checklist item text.
|
|
checklist_id : str, optional
|
|
Specific checklist ID (auto-discovers the first if omitted).
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with item details.
|
|
"""
|
|
client = _get_client()
|
|
result = client.add_checklist_item(card_id=card_id, name=name, checklist_id=checklist_id)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_toggle_checklist_item(card_id: str, item_id: str, checked: bool) -> str: # noqa: FBT001
|
|
"""Mark a checklist item as complete or incomplete.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
item_id : str
|
|
Checklist item ID.
|
|
checked : bool
|
|
True = complete, False = incomplete.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with updated item state.
|
|
"""
|
|
client = _get_client()
|
|
result = client.toggle_checklist_item(card_id=card_id, item_id=item_id, checked=checked)
|
|
return _respond(result)
|
|
|
|
|
|
def trello_delete_checklist_item(card_id: str, item_id: str) -> str:
|
|
"""Remove a checklist item from a Trello card.
|
|
|
|
Parameters
|
|
----------
|
|
card_id : str
|
|
Card ID.
|
|
item_id : str
|
|
Checklist item ID.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
JSON with success message.
|
|
"""
|
|
client = _get_client()
|
|
result = client.delete_checklist_item(card_id=card_id, item_id=item_id)
|
|
return _respond(result)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Plugin metadata (used by Hermes plugin loader)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
PLUGIN_NAME = "trello-plugin"
|
|
PLUGIN_DESCRIPTION = "Hermes Agent plugin for Trello board integration"
|
|
PLUGIN_VERSION = "0.1.0"
|
|
PLUGIN_TOOLS = [
|
|
trello_verify_credentials,
|
|
trello_list_boards,
|
|
trello_disconnect,
|
|
trello_create_board,
|
|
trello_rename_board,
|
|
trello_archive_board,
|
|
trello_open_board,
|
|
trello_board_details,
|
|
trello_create_list,
|
|
trello_rename_list,
|
|
trello_archive_list,
|
|
trello_move_list,
|
|
trello_create_card,
|
|
trello_card_details,
|
|
trello_update_card,
|
|
trello_move_card,
|
|
trello_archive_card,
|
|
trello_assign_member,
|
|
trello_remove_member,
|
|
trello_add_comment,
|
|
trello_delete_comment,
|
|
trello_add_checklist_item,
|
|
trello_toggle_checklist_item,
|
|
trello_delete_checklist_item,
|
|
]
|
|
PLUGIN_REQUIRES_ENV = ["TRELLO_API_KEY", "TRELLO_TOKEN"]
|
|
|
|
|
|
def check_requirements() -> bool:
|
|
"""Return True when both Trello env vars are present."""
|
|
return TrelloClient.check_requirements() |