feat(03-03): implement Orchestrator class

- Add Orchestrator class with handle_message() and handle_callback() entry points
- Command parsing via regex: /gsd:* pattern extraction
- Auto-session-creation per CONTEXT.md decision
- Error handling with try/except returning format_error() cards
- Approval detection integration with ApprovalHandler
- Output truncation to 2000 chars for Feishu card limits
- 18 tests passing with full coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 16:20:24 +08:00
parent 086be891b9
commit 6d9bd902ea
2 changed files with 250 additions and 0 deletions
+25
View File
@@ -116,6 +116,8 @@ class TestHandleMessage:
"""handle_message should execute command and return success card."""
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
mock_tmux_session.tmux_session = MagicMock()
mock_tmux_session.tmux_session.active_pane = MagicMock()
mock_orchestrator.tmux_manager.get_session.return_value = mock_tmux_session
mock_orchestrator.executor.execute.return_value = CommandResult(
@@ -125,6 +127,8 @@ class TestHandleMessage:
error_type=None
)
# Ensure no approval is detected
mock_orchestrator.approval_handler.detect_approval_request.return_value = None
mock_orchestrator.formatter.format_success.return_value = {"card": "success"}
result = mock_orchestrator.handle_message(
@@ -142,6 +146,9 @@ class TestHandleMessage:
def test_handle_message_returns_error_card_when_command_fails(self, mock_orchestrator):
"""handle_message should return error card when command fails."""
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
mock_tmux_session.tmux_session = MagicMock()
mock_tmux_session.tmux_session.active_pane = MagicMock()
mock_orchestrator.tmux_manager.get_session.return_value = mock_tmux_session
mock_orchestrator.executor.execute.return_value = CommandResult(
@@ -151,6 +158,8 @@ class TestHandleMessage:
error_type="error"
)
# Ensure no approval is detected
mock_orchestrator.approval_handler.detect_approval_request.return_value = None
mock_orchestrator.formatter.format_error.return_value = {"card": "error"}
result = mock_orchestrator.handle_message(
@@ -166,6 +175,7 @@ class TestHandleMessage:
def test_handle_message_returns_approval_card_when_approval_detected(self, mock_orchestrator):
"""handle_message should return approval card when approval is detected."""
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
mock_orchestrator.tmux_manager.get_session.return_value = mock_tmux_session
mock_orchestrator.executor.execute.return_value = CommandResult(
@@ -196,6 +206,9 @@ class TestHandleMessage:
def test_handle_message_truncates_output_to_2000_chars(self, mock_orchestrator):
"""handle_message should truncate output to 2000 chars for card limits."""
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
mock_tmux_session.tmux_session = MagicMock()
mock_tmux_session.tmux_session.active_pane = MagicMock()
mock_orchestrator.tmux_manager.get_session.return_value = mock_tmux_session
# Create long output
@@ -207,6 +220,8 @@ class TestHandleMessage:
error_type=None
)
# Ensure no approval is detected
mock_orchestrator.approval_handler.detect_approval_request.return_value = None
mock_orchestrator.formatter.format_success.return_value = {"card": "success"}
result = mock_orchestrator.handle_message(
@@ -384,6 +399,9 @@ class TestIntegrationFlows:
orch = orchestrator_with_real_components
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
mock_tmux_session.tmux_session = MagicMock()
mock_tmux_session.tmux_session.active_pane = MagicMock()
orch.tmux_manager.get_session.return_value = mock_tmux_session
orch.executor.execute.return_value = CommandResult(
@@ -409,6 +427,7 @@ class TestIntegrationFlows:
orch = orchestrator_with_real_components
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
orch.tmux_manager.get_session.return_value = mock_tmux_session
orch.executor.execute.return_value = CommandResult(
@@ -437,6 +456,9 @@ class TestIntegrationFlows:
orch = orchestrator_with_real_components
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
mock_tmux_session.tmux_session = MagicMock()
mock_tmux_session.tmux_session.active_pane = MagicMock()
orch.tmux_manager.get_session.return_value = mock_tmux_session
# First create an approval via handle_message
@@ -482,6 +504,9 @@ class TestIntegrationFlows:
orch = orchestrator_with_real_components
mock_tmux_session = MagicMock(spec=TmuxSession)
mock_tmux_session.session_id = "test-123"
mock_tmux_session.tmux_session = MagicMock()
mock_tmux_session.tmux_session.active_pane = MagicMock()
orch.tmux_manager.get_session.return_value = mock_tmux_session
# First create an approval via handle_message