Files
trello-plugin/src/trello_plugin/tools.py
T
Marko (Hermes Implementer) 034ed2c928 feat: implement Trello card management
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
2026-05-26 22:42:50 +00:00

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()