301 lines
10 KiB
Markdown
301 lines
10 KiB
Markdown
---
|
|
phase: 02-integration-layer
|
|
plan: 02
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- src/gsd_openclaw/response_formatter.py
|
|
- tests/test_response_formatter.py
|
|
autonomous: true
|
|
requirements:
|
|
- OCLW-04
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Response formatter creates valid Feishu Card JSON 2.0 structure"
|
|
- "Success cards have green theme"
|
|
- "Error cards have red theme with guidance buttons"
|
|
- "Long messages are paginated with [N/M] markers"
|
|
- "Cards support interactive buttons with callback values"
|
|
artifacts:
|
|
- path: "src/gsd_openclaw/response_formatter.py"
|
|
provides: "Feishu Card message formatting"
|
|
exports: ["FeishuResponseFormatter", "CardTheme", "CardButton"]
|
|
- path: "tests/test_response_formatter.py"
|
|
provides: "Tests for response formatting"
|
|
exports: ["TestFeishuResponseFormatter"]
|
|
key_links:
|
|
- from: "FeishuResponseFormatter"
|
|
to: "Feishu API"
|
|
via: "card JSON structure"
|
|
pattern: '"schema": "2.0"'
|
|
---
|
|
|
|
<objective>
|
|
Create FeishuResponseFormatter class that formats responses as Feishu Card JSON 2.0 messages with three-zone structure (header/content/actions).
|
|
|
|
Purpose: Provide channel-adaptive response formatting for Feishu. The formatter converts GSD output into structured Feishu Card messages with proper theming, pagination, and interactive elements.
|
|
|
|
Output:
|
|
- src/gsd_openclaw/response_formatter.py - Response formatter implementation
|
|
- tests/test_response_formatter.py - Formatter tests
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
|
|
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/02-integration-layer/02-CONTEXT.md
|
|
@.planning/phases/02-integration-layer/02-RESEARCH.md
|
|
|
|
<interfaces>
|
|
<!-- Feishu Card JSON 2.0 Structure (from RESEARCH.md) -->
|
|
|
|
Three-zone layout:
|
|
1. header: Title + status color (blue=info, green=success, red=error)
|
|
2. elements: Main content area (div, markdown, note components)
|
|
3. actions: Interactive buttons (at card bottom)
|
|
|
|
Card JSON structure:
|
|
```json
|
|
{
|
|
"schema": "2.0",
|
|
"config": {"wide_screen_mode": true},
|
|
"header": {
|
|
"title": {"tag": "plain_text", "content": "..."},
|
|
"template": "green"
|
|
},
|
|
"elements": [
|
|
{"tag": "div", "text": {"tag": "lark_md", "content": "..."}}
|
|
]
|
|
}
|
|
```
|
|
|
|
Button structure:
|
|
```json
|
|
{
|
|
"tag": "action",
|
|
"actions": [
|
|
{
|
|
"tag": "button",
|
|
"text": {"tag": "plain_text", "content": "Confirm"},
|
|
"type": "primary",
|
|
"value": {"action": "approve", "project": "my-auth"}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Use `lark_md` tag for markdown content (Feishu's built-in markdown support).
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Create test scaffold for FeishuResponseFormatter</name>
|
|
<files>tests/test_response_formatter.py</files>
|
|
<behavior>
|
|
- Test 1: format_success returns valid card with green theme
|
|
- Test 2: format_error returns valid card with red theme
|
|
- Test 3: format_info returns valid card with blue theme
|
|
- Test 4: Card with buttons includes action element
|
|
- Test 5: format_paginated adds [N/M] marker
|
|
- Test 6: Card has schema 2.0 and wide_screen_mode config
|
|
</behavior>
|
|
<action>
|
|
Create tests/test_response_formatter.py with test class TestFeishuResponseFormatter:
|
|
|
|
1. test_format_success_returns_valid_card():
|
|
- Create FeishuResponseFormatter()
|
|
- Call format_success("Test Title", "Test content")
|
|
- Assert card["schema"] == "2.0"
|
|
- Assert card["header"]["template"] == "green"
|
|
- Assert card["header"]["title"]["content"] == "Test Title"
|
|
|
|
2. test_format_error_returns_red_theme():
|
|
- Call format_error("Error", "Something went wrong")
|
|
- Assert card["header"]["template"] == "red"
|
|
|
|
3. test_format_info_returns_blue_theme():
|
|
- Call format_info("Info", "Status update")
|
|
- Assert card["header"]["template"] == "blue"
|
|
|
|
4. test_format_with_buttons_includes_actions():
|
|
- Create CardButton("OK", {"action": "ok"})
|
|
- Call format_success("Test", "Content", buttons=[button])
|
|
- Assert elements contains action with tag="action"
|
|
- Assert button text is "OK"
|
|
|
|
5. test_format_paginated_adds_marker():
|
|
- Call format_paginated("Title", "Content", page=1, total_pages=3)
|
|
- Assert "[1/3]" appears in content
|
|
|
|
6. test_card_has_required_structure():
|
|
- Call format_success("Test", "Content")
|
|
- Assert "config" key exists
|
|
- Assert config["wide_screen_mode"] == True
|
|
- Assert elements[0]["tag"] == "div"
|
|
|
|
Import from gsd_openclaw.response_formatter: FeishuResponseFormatter, CardTheme, CardButton
|
|
|
|
Follow test pattern from tests/test_state_manager.py.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_response_formatter.py -v</automated>
|
|
</verify>
|
|
<done>
|
|
- tests/test_response_formatter.py exists with 6 tests
|
|
- All tests fail initially (no implementation yet)
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Implement CardTheme enum and CardButton dataclass</name>
|
|
<files>src/gsd_openclaw/response_formatter.py</files>
|
|
<behavior>
|
|
- Test: CardButton can be created with text, value, button_type
|
|
- Test: CardTheme enum has SUCCESS, ERROR, INFO, WARNING values
|
|
</behavior>
|
|
<action>
|
|
Create src/gsd_openclaw/response_formatter.py with:
|
|
|
|
1. Imports:
|
|
- from dataclasses import dataclass
|
|
- from typing import Dict, Any, List, Optional
|
|
- from enum import Enum
|
|
|
|
2. CardTheme(Enum):
|
|
- SUCCESS = "green"
|
|
- ERROR = "red"
|
|
- INFO = "blue"
|
|
- WARNING = "orange"
|
|
|
|
3. @dataclass CardButton:
|
|
- text: str
|
|
- value: Dict[str, Any]
|
|
- button_type: str = "primary" # primary, default, danger
|
|
|
|
Keep implementation minimal - just these data structures.
|
|
Tests should now pass for CardTheme and CardButton.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_response_formatter.py::TestFeishuResponseFormatter::test_format_success_returns_valid_card -v 2>&1 | head -20 || echo "Expected: tests still fail (formatter not implemented)"</automated>
|
|
</verify>
|
|
<done>
|
|
- CardTheme enum exists with SUCCESS, ERROR, INFO, WARNING
|
|
- CardButton dataclass exists with text, value, button_type
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 3: Implement FeishuResponseFormatter class</name>
|
|
<files>src/gsd_openclaw/response_formatter.py</files>
|
|
<behavior>
|
|
- Test: All 6 tests in test_response_formatter.py pass
|
|
</behavior>
|
|
<action>
|
|
Add FeishuResponseFormatter class to response_formatter.py:
|
|
|
|
class FeishuResponseFormatter:
|
|
"""Format responses as Feishu Card messages."""
|
|
|
|
def format_success(self, title: str, content: str,
|
|
buttons: Optional[List[CardButton]] = None) -> dict:
|
|
"""Format a success response card with green theme."""
|
|
return self._build_card(title, content, CardTheme.SUCCESS, buttons)
|
|
|
|
def format_error(self, title: str, content: str,
|
|
buttons: Optional[List[CardButton]] = None) -> dict:
|
|
"""Format an error response card with red theme."""
|
|
return self._build_card(title, content, CardTheme.ERROR, buttons)
|
|
|
|
def format_info(self, title: str, content: str,
|
|
buttons: Optional[List[CardButton]] = None) -> dict:
|
|
"""Format an info response card with blue theme."""
|
|
return self._build_card(title, content, CardTheme.INFO, buttons)
|
|
|
|
def format_paginated(self, title: str, content: str,
|
|
page: int, total_pages: int) -> dict:
|
|
"""Format a paginated response with [page/total] marker."""
|
|
footer = f"\n\n---\n[{page}/{total_pages}]"
|
|
return self._build_card(title, content + footer, CardTheme.INFO)
|
|
|
|
def _build_card(self, title: str, content: str, theme: CardTheme,
|
|
buttons: Optional[List[CardButton]] = None) -> dict:
|
|
"""Build complete card JSON structure."""
|
|
card = {
|
|
"schema": "2.0",
|
|
"config": {"wide_screen_mode": True},
|
|
"header": {
|
|
"title": {"tag": "plain_text", "content": title},
|
|
"template": theme.value
|
|
},
|
|
"elements": [
|
|
{"tag": "div", "text": {"tag": "lark_md", "content": content}}
|
|
]
|
|
}
|
|
if buttons:
|
|
card["elements"].append(self._build_actions(buttons))
|
|
return card
|
|
|
|
def _build_actions(self, buttons: List[CardButton]) -> dict:
|
|
"""Build actions element from button list."""
|
|
return {
|
|
"tag": "action",
|
|
"actions": [
|
|
{
|
|
"tag": "button",
|
|
"text": {"tag": "plain_text", "content": btn.text},
|
|
"type": btn.button_type,
|
|
"value": btn.value
|
|
}
|
|
for btn in buttons
|
|
]
|
|
}
|
|
|
|
User decision (LOCKED):
|
|
- Three-zone structure: header + elements + actions
|
|
- Error cards use red theme
|
|
- Success cards use green theme
|
|
- Pagination uses [N/M] format
|
|
- Interactive buttons support callback values
|
|
|
|
All 6 tests should now pass.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_response_formatter.py -v</automated>
|
|
</verify>
|
|
<done>
|
|
- FeishuResponseFormatter class implements all format methods
|
|
- All 6 tests pass
|
|
- Card JSON uses schema 2.0 with three-zone structure
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- Run pytest tests/test_response_formatter.py -v (all tests pass)
|
|
- Verify card structure matches Feishu Card JSON 2.0 specification
|
|
- Verify theme colors are correct (green=success, red=error, blue=info)
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
1. FeishuResponseFormatter class exists in src/gsd_openclaw/response_formatter.py
|
|
2. format_success, format_error, format_info, format_paginated methods work
|
|
3. Card JSON has schema "2.0" with proper header/elements structure
|
|
4. Buttons are rendered as action elements with callback values
|
|
5. All 6 tests in tests/test_response_formatter.py pass
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/02-integration-layer/02-02-SUMMARY.md`
|
|
</output>
|