From 87068339df407653d1fdc28ab573b2cdff17529e Mon Sep 17 00:00:00 2001 From: starmen1265 Date: Wed, 11 Mar 2026 18:51:50 +0800 Subject: [PATCH] feat(04-02): use user context for project selection in generic commands - Generic commands now use project from user context first - Fall back to context param or default if no user context - Add users_root parameter to Orchestrator for test isolation - Update integration tests to use tmp_users_root fixture Co-Authored-By: Claude Opus 4.6 --- gsd-openclaw/src/gsd_openclaw/orchestrator.py | 21 ++++++++++++++----- gsd-openclaw/tests/test_orchestrator.py | 5 +++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/gsd-openclaw/src/gsd_openclaw/orchestrator.py b/gsd-openclaw/src/gsd_openclaw/orchestrator.py index ce06e04..3fc2406 100644 --- a/gsd-openclaw/src/gsd_openclaw/orchestrator.py +++ b/gsd-openclaw/src/gsd_openclaw/orchestrator.py @@ -10,7 +10,10 @@ This module provides the central command routing system that: from __future__ import annotations import re -from typing import Any, Dict, Optional, Tuple +from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING + +if TYPE_CHECKING: + from pathlib import Path from gsd_openclaw.tmux_session import TmuxSession, TmuxSessionManager from gsd_openclaw.command_executor import CommandExecutor, CommandResult @@ -47,7 +50,8 @@ class Orchestrator: def __init__( self, tmux_manager: Optional[TmuxSessionManager] = None, - formatter: Optional[FeishuResponseFormatter] = None + formatter: Optional[FeishuResponseFormatter] = None, + users_root: Optional["Path"] = None ): """Initialize Orchestrator. @@ -56,6 +60,7 @@ class Orchestrator: If not provided, a new one will be created. formatter: Optional FeishuResponseFormatter instance. If not provided, a new one will be created. + users_root: Optional custom root for user contexts (useful for testing). """ self.tmux_manager = tmux_manager or TmuxSessionManager() self.formatter = formatter or FeishuResponseFormatter() @@ -63,6 +68,7 @@ class Orchestrator: self.approval_handler = ApprovalHandler(self.formatter) self.progress_monitor = ProgressMonitor(self.formatter) self.user_context_managers: Dict[str, UserContextManager] = {} + self._users_root = users_root def _get_user_context_manager(self, user_id: str) -> UserContextManager: """Get or create UserContextManager for a user. @@ -74,7 +80,7 @@ class Orchestrator: UserContextManager instance for the user. """ if user_id not in self.user_context_managers: - self.user_context_managers[user_id] = UserContextManager(user_id) + self.user_context_managers[user_id] = UserContextManager(user_id, self._users_root) return self.user_context_managers[user_id] def _handle_new_project(self, user_id: str, project_name: str) -> Dict[str, Any]: @@ -249,8 +255,13 @@ class Orchestrator: if command == 'use': return self._handle_use_project(user_id, args) - # Get project name from context - project_name = context.get("project", "default") + # Generic commands - get project from user context first + user_ctx = self._get_user_context_manager(user_id) + project_name = user_ctx.get_current_project() + + # Fall back to context param or default + if not project_name: + project_name = context.get("project", "default") # Get or create session (auto-create per CONTEXT.md) session = self.tmux_manager.get_session(project_name) diff --git a/gsd-openclaw/tests/test_orchestrator.py b/gsd-openclaw/tests/test_orchestrator.py index bc2d27d..71ab572 100644 --- a/gsd-openclaw/tests/test_orchestrator.py +++ b/gsd-openclaw/tests/test_orchestrator.py @@ -345,7 +345,7 @@ class TestIntegrationFlows: """Integration-style tests for complete flows.""" @pytest.fixture - def orchestrator_with_real_components(self): + def orchestrator_with_real_components(self, tmp_users_root): """Create orchestrator with real components (mock only tmux).""" from gsd_openclaw.orchestrator import Orchestrator from gsd_openclaw.response_formatter import FeishuResponseFormatter @@ -359,7 +359,8 @@ class TestIntegrationFlows: orchestrator = Orchestrator( tmux_manager=mock_tmux_manager, - formatter=formatter + formatter=formatter, + users_root=tmp_users_root ) orchestrator.approval_handler = approval_handler orchestrator.executor = MagicMock()