From 23943443e3809634836c531f6dc8eccad4b4368b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 12 Mar 2026 18:15:38 -0400 Subject: [PATCH] stuff that was not comitted. --- conductor/tracks.md | 4 +- .../external_mcp_support_20260308/plan.md | 62 ++++++++-------- .../tracks/gui_path_config_20260308/plan.md | 24 +++---- .../project_conductor_dir_20260308/plan.md | 16 ++--- tests/test_project_paths.py | 71 ++++++++++++++----- 5 files changed, 105 insertions(+), 72 deletions(-) diff --git a/conductor/tracks.md b/conductor/tracks.md index c56266a..80762fb 100644 --- a/conductor/tracks.md +++ b/conductor/tracks.md @@ -105,11 +105,11 @@ This file tracks all major tracks for the project. Each track has its own detail ### Path Configuration -1. [ ] **Track: Project-Specific Conductor Directory** +1. [x] **Track: Project-Specific Conductor Directory** *Link: [./tracks/project_conductor_dir_20260308/](./tracks/project_conductor_dir_20260308/)* *Goal: Make conductor directory per-project. Each project TOML can specify custom conductor dir for isolated track/state management.* -2. [ ] **Track: GUI Path Configuration in Context Hub** +2. [x] **Track: GUI Path Configuration in Context Hub** *Link: [./tracks/gui_path_config_20260308/](./tracks/gui_path_config_20260308/)* *Goal: Add path configuration UI to Context Hub. Allow users to view and edit configurable paths directly from the GUI.* diff --git a/conductor/tracks/external_mcp_support_20260308/plan.md b/conductor/tracks/external_mcp_support_20260308/plan.md index 0f98609..09f73a3 100644 --- a/conductor/tracks/external_mcp_support_20260308/plan.md +++ b/conductor/tracks/external_mcp_support_20260308/plan.md @@ -1,42 +1,42 @@ # Implementation Plan: External MCP Server Support -## Phase 1: Configuration & Data Modeling +## Phase 1: Configuration & Data Modeling [checkpoint: 4ba1bd9] - [x] Task: Define the schema for external MCP server configuration. [1c863f0] - [x] Update `src/models.py` to include `MCPServerConfig` and `MCPConfiguration` classes. - [x] Implement logic to load `mcp_config.json` from global and project-specific paths. - [x] Task: Integrate configuration loading into `AppController`. [c09e0f5] - [x] Ensure the MCP config path is correctly resolved from `config.toml` and `manual_slop.toml`. - [x] Task: Write unit tests for configuration loading and validation. [c09e0f5] -- [ ] Task: Conductor - User Manual Verification 'Phase 1: Configuration & Data Modeling' (Protocol in workflow.md) +- [x] Task: Conductor - User Manual Verification 'Phase 1: Configuration & Data Modeling' [4ba1bd9] -## Phase 2: MCP Client Extension -- [ ] Task: Implement `ExternalMCPManager` in `src/mcp_client.py`. - - [ ] Add support for managing multiple MCP server sessions. - - [ ] Implement the `StdioMCPClient` for local subprocess communication. - - [ ] Implement the `RemoteMCPClient` for SSE/WebSocket communication. -- [ ] Task: Update Tool Discovery. - - [ ] Implement `list_external_tools()` to aggregate tools from all active external servers. -- [ ] Task: Update Tool Dispatch. - - [ ] Modify `mcp_client.dispatch()` and `mcp_client.async_dispatch()` to route tool calls to either native tools or the appropriate external server. -- [ ] Task: Write integration tests for stdio and remote MCP client communication (using mock servers). -- [ ] Task: Conductor - User Manual Verification 'Phase 2: MCP Client Extension' (Protocol in workflow.md) +## Phase 2: MCP Client Extension [checkpoint: 828fadf] +- [x] Task: Implement `ExternalMCPManager` in `src/mcp_client.py`. [828fadf] + - [x] Add support for managing multiple MCP server sessions. + - [x] Implement the `StdioMCPClient` for local subprocess communication. + - [x] Implement the `RemoteMCPClient` for SSE/WebSocket communication (stub). +- [x] Task: Update Tool Discovery. [828fadf] + - [x] Implement `list_external_tools()` to aggregate tools from all active external servers. +- [x] Task: Update Tool Dispatch. [828fadf] + - [x] Modify `mcp_client.dispatch()` and `mcp_client.async_dispatch()` to route tool calls to either native tools or the appropriate external server. +- [x] Task: Write integration tests for stdio and remote MCP client communication (using mock servers). [828fadf] +- [x] Task: Conductor - User Manual Verification 'Phase 2: MCP Client Extension' [828fadf] -## Phase 3: GUI Integration & Lifecycle -- [ ] Task: Update the **Operations** panel in `src/gui_2.py`. - - [ ] Create a new "External Tools" section. - - [ ] List discovered tools from active external servers. - - [ ] Add a "Refresh External MCPs" button to reload configuration and rediscover tools. -- [ ] Task: Implement Lifecycle Management. - - [ ] Add the "Auto-start on Project Load" logic to start servers when a project is initialized. - - [ ] Add status indicators (e.g., color-coded dots) for each external server in the GUI. -- [ ] Task: Write visual regression tests or simulation scripts to verify the updated Operations panel. -- [ ] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Lifecycle' (Protocol in workflow.md) +## Phase 3: GUI Integration & Lifecycle [checkpoint: 3b2588a] +- [x] Task: Update the **Operations** panel in `src/gui_2.py`. [3b2588a] + - [x] Create a new "External Tools" section. + - [x] List discovered tools from active external servers. + - [x] Add a "Refresh External MCPs" button to reload configuration and rediscover tools. +- [x] Task: Implement Lifecycle Management. [3b2588a] + - [x] Add the "Auto-start on Project Load" logic to start servers when a project is initialized. + - [x] Add status indicators (e.g., color-coded dots) for each external server in the GUI. +- [x] Task: Write visual regression tests or simulation scripts to verify the updated Operations panel. [3b2588a] +- [x] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Lifecycle' [3b2588a] -## Phase 4: Agent Integration & HITL -- [ ] Task: Update AI tool declarations. - - [ ] Ensure `ai_client.py` includes external tools in the tool definitions sent to Gemini/Anthropic. -- [ ] Task: Verify HITL Approval Flow. - - [ ] Ensure that calling an external tool correctly triggers the `ConfirmDialog` modal. - - [ ] Verify that approved external tool results are correctly returned to the AI. -- [ ] Task: Perform a final end-to-end verification with a real external MCP server. -- [ ] Task: Conductor - User Manual Verification 'Phase 4: Agent Integration & HITL' (Protocol in workflow.md) +## Phase 4: Agent Integration & HITL [checkpoint: f4c5a0b] +- [x] Task: Update AI tool declarations. [f4c5a0b] + - [x] Ensure `ai_client.py` includes external tools in the tool definitions sent to Gemini/Anthropic. +- [x] Task: Verify HITL Approval Flow. [f4c5a0b] + - [x] Ensure that calling an external tool correctly triggers the `ConfirmDialog` modal. + - [x] Verify that approved external tool results are correctly returned to the AI. +- [x] Task: Perform a final end-to-end verification with a real external MCP server. [f4c5a0b] +- [x] Task: Conductor - User Manual Verification 'Phase 4: Agent Integration & HITL' [f4c5a0b] diff --git a/conductor/tracks/gui_path_config_20260308/plan.md b/conductor/tracks/gui_path_config_20260308/plan.md index 4f94ce7..417c9b8 100644 --- a/conductor/tracks/gui_path_config_20260308/plan.md +++ b/conductor/tracks/gui_path_config_20260308/plan.md @@ -3,13 +3,13 @@ ## Phase 1: Path Info Display Focus: Show current path resolution in GUI -- [ ] Task 1.1: Add path info functions to paths.py +- [x] Task 1.1: Add path info functions to paths.py [d237d3b] - WHERE: src/paths.py - WHAT: Add functions to get path resolution source (default/env/config) - HOW: Return tuple of (resolved_path, source) - SAFETY: New functions, no modifications -- [ ] Task 1.2: Create path display helper +- [x] Task 1.2: Create path display helper [d237d3b] - WHERE: src/paths.py - WHAT: Function to get all paths with resolution info - HOW: Returns dict of path_name -> (resolved, source) @@ -18,25 +18,25 @@ Focus: Show current path resolution in GUI ## Phase 2: Context Hub Panel Focus: Add Path Configuration panel to GUI -- [ ] Task 2.1: Add Paths tab to Context Hub +- [x] Task 2.1: Add Paths tab to Context Hub [d237d3b] - WHERE: src/gui_2.py (Context Hub section) - WHAT: New tab/section for path configuration - HOW: Add ImGui tab item, follow existing panel patterns - SAFETY: New panel, no modifications to existing -- [ ] Task 2.2: Display current paths +- [x] Task 2.2: Display current paths [d237d3b] - WHERE: src/gui_2.py (new paths panel) - WHAT: Show resolved paths and their sources - HOW: Call paths.py functions, display in read-only text - SAFETY: New code -- [ ] Task 2.3: Add path text inputs +- [x] Task 2.3: Add path text inputs [d237d3b] - WHERE: src/gui_2.py (paths panel) - WHAT: Editable text inputs for each path - HOW: ImGui input_text for conductor_dir, logs_dir, scripts_dir - SAFETY: New code -- [ ] Task 2.4: Add browse buttons +- [x] Task 2.4: Add browse buttons [d237d3b] - WHERE: src/gui_2.py (paths panel) - WHAT: File dialog buttons to browse for directories - HOW: Use existing file dialog patterns in gui_2.py @@ -45,19 +45,19 @@ Focus: Add Path Configuration panel to GUI ## Phase 3: Persistence Focus: Save path changes to config.toml -- [ ] Task 3.1: Add config write function +- [x] Task 3.1: Add config write function [d237d3b] - WHERE: src/gui_2.py or new utility - WHAT: Write [paths] section to config.toml - HOW: Read existing config, update paths section, write back - SAFETY: Backup before write, handle errors -- [ ] Task 3.2: Add Apply button +- [x] Task 3.2: Add Apply button [d237d3b] - WHERE: src/gui_2.py (paths panel) - WHAT: Button to save changes - HOW: Call config write function, show success/error message - SAFETY: Confirmation dialog -- [ ] Task 3.3: Add Reset button +- [x] Task 3.3: Add Reset button [d237d3b] - WHERE: src/gui_2.py (paths panel) - WHAT: Reset paths to defaults - HOW: Clear custom values, show confirmation @@ -66,13 +66,13 @@ Focus: Save path changes to config.toml ## Phase 4: UX Polish Focus: Improve user experience -- [ ] Task 4.1: Add restart warning +- [x] Task 4.1: Add restart warning [d237d3b] - WHERE: src/gui_2.py (paths panel) - WHAT: Show warning that changes require restart - HOW: Text label after Apply - SAFETY: New code -- [ ] Task 4.2: Add tooltips +- [x] Task 4.2: Add tooltips [d237d3b] - WHERE: src/gui_2.py (paths panel) - WHAT: Explain each path and resolution order - HOW: ImGui set_tooltip on hover @@ -81,7 +81,7 @@ Focus: Improve user experience ## Phase 5: Tests Focus: Verify GUI path configuration -- [ ] Task 5.1: Test path display +- [x] Task 5.1: Test path display [d237d3b] - WHERE: tests/test_gui_paths.py (new file) - WHAT: Verify paths panel shows correct values - HOW: Mock paths.py, verify display diff --git a/conductor/tracks/project_conductor_dir_20260308/plan.md b/conductor/tracks/project_conductor_dir_20260308/plan.md index 2f75d34..c307bbd 100644 --- a/conductor/tracks/project_conductor_dir_20260308/plan.md +++ b/conductor/tracks/project_conductor_dir_20260308/plan.md @@ -3,13 +3,13 @@ ## Phase 1: Extend paths.py Focus: Add project-specific path resolution -- [ ] Task 1.1: Add project-aware conductor path functions +- [x] Task 1.1: Add project-aware conductor path functions [48e2ed8] - WHERE: src/paths.py - WHAT: Add optional project_path parameter to get_conductor_dir, get_tracks_dir, get_track_state_dir - HOW: If project_path provided, resolve relative to project root; otherwise use global - SAFETY: Maintain backward compatibility with no-arg calls -- [ ] Task 1.2: Add project conductor path resolution +- [x] Task 1.2: Add project conductor path resolution [48e2ed8] - WHERE: src/paths.py - WHAT: New function `_resolve_project_conductor_dir(project_path)` that reads from project TOML - HOW: Load project TOML, check `[conductor].dir` key @@ -18,18 +18,18 @@ Focus: Add project-specific path resolution ## Phase 2: Update project_manager.py Focus: Use project-specific paths for track operations -- [ ] Task 2.1: Update save_track_state to use project conductor dir +- [x] Task 2.1: Update save_track_state to use project conductor dir [3999e9c] - WHERE: src/project_manager.py (around line 240) - WHAT: Pass project base_dir to paths.get_track_state_dir() - HOW: Get base_dir from project_path, call paths with project_path param - SAFETY: Maintain existing function signature compatibility -- [ ] Task 2.2: Update load_track_state to use project conductor dir +- [x] Task 2.2: Update load_track_state to use project conductor dir [3999e9c] - WHERE: src/project_manager.py (around line 252) - WHAT: Load track state from project-specific directory - HOW: Same as above -- [ ] Task 2.3: Update get_all_tracks to use project conductor dir +- [x] Task 2.3: Update get_all_tracks to use project conductor dir [3999e9c] - WHERE: src/project_manager.py (around line 297) - WHAT: List tracks from project-specific directory - HOW: Accept optional project_path param @@ -37,7 +37,7 @@ Focus: Use project-specific paths for track operations ## Phase 3: Update app_controller.py Focus: Pass project path to track operations -- [ ] Task 3.1: Update track creation to use project conductor dir +- [x] Task 3.1: Update track creation to use project conductor dir [3999e9c] - WHERE: src/app_controller.py (around line 1907, 1937) - WHAT: Pass active_project_path to track path functions - HOW: Get active_project_path, pass to paths.get_tracks_dir() @@ -46,13 +46,13 @@ Focus: Pass project path to track operations ## Phase 4: Tests Focus: Verify project-specific behavior -- [ ] Task 4.1: Write test for project-specific conductor dir +- [x] Task 4.1: Write test for project-specific conductor dir [48e2ed8] - WHERE: tests/test_project_paths.py (new file) - WHAT: Create mock project with custom conductor dir, verify tracks saved there - HOW: Mock project_manager, verify path resolution - SAFETY: New test file -- [ ] Task 4.2: Test backward compatibility +- [x] Task 4.2: Test backward compatibility [3999e9c] - WHERE: tests/test_project_paths.py - WHAT: Verify global paths still work without project_path - HOW: Call functions without project_path, verify defaults diff --git a/tests/test_project_paths.py b/tests/test_project_paths.py index d6f7672..b8de62e 100644 --- a/tests/test_project_paths.py +++ b/tests/test_project_paths.py @@ -1,25 +1,17 @@ import os import pytest -import tomllib +import json import tomli_w from pathlib import Path from src import paths +from src import project_manager def test_get_conductor_dir_default(): paths.reset_resolved() - # Should return default "conductor" relative to root + # Should return absolute path to "conductor" in project root expected = Path(__file__).resolve().parent.parent / "conductor" assert paths.get_conductor_dir() == expected -def test_get_conductor_dir_project_specific_no_toml(tmp_path): - paths.reset_resolved() - project_root = tmp_path / "my_project" - project_root.mkdir() - - # Should default to project_root / "conductor" if no manual_slop.toml - res = paths.get_conductor_dir(project_path=str(project_root)) - assert res == project_root / "conductor" - def test_get_conductor_dir_project_specific_with_toml(tmp_path): paths.reset_resolved() project_root = tmp_path / "my_project" @@ -38,18 +30,59 @@ def test_get_conductor_dir_project_specific_with_toml(tmp_path): res = paths.get_conductor_dir(project_path=str(project_root)) assert res == project_root / "custom_tracks" -def test_get_tracks_dir_project_specific(tmp_path): +def test_get_all_tracks_project_specific(tmp_path): paths.reset_resolved() project_root = tmp_path / "my_project" project_root.mkdir() - res = paths.get_tracks_dir(project_path=str(project_root)) - assert res == project_root / "conductor" / "tracks" + # Custom conductor dir + custom_dir = project_root / "my_conductor" + custom_dir.mkdir() + tracks_dir = custom_dir / "tracks" + tracks_dir.mkdir() + + # Create a dummy track + track_dir = tracks_dir / "test_track_20260312" + track_dir.mkdir() + with open(track_dir / "metadata.json", "w") as f: + json.dump({"id": "test_track", "title": "Test Track"}, f) + + # Setup manual_slop.toml + toml_path = project_root / "manual_slop.toml" + config = {"conductor": {"dir": "my_conductor"}} + with open(toml_path, "wb") as f: + f.write(tomli_w.dumps(config).encode()) + + # project_manager.get_all_tracks(base_dir) should now find it + tracks = project_manager.get_all_tracks(str(project_root)) + assert len(tracks) == 1 + assert tracks[0]["title"] == "Test Track" -def test_get_track_state_dir_project_specific(tmp_path): +def test_get_all_tracks_global_fallback(tmp_path): paths.reset_resolved() - project_root = tmp_path / "my_project" - project_root.mkdir() - res = paths.get_track_state_dir("track-123", project_path=str(project_root)) - assert res == project_root / "conductor" / "tracks" / "track-123" + # Create a directory without manual_slop.toml + empty_dir = tmp_path / "empty_project" + empty_dir.mkdir() + + # Setup a fake global conductor + global_conductor = tmp_path / "global_conductor" + global_conductor.mkdir() + global_tracks = global_conductor / "tracks" + global_tracks.mkdir() + + track_dir = global_tracks / "global_track" + track_dir.mkdir() + with open(track_dir / "metadata.json", "w") as f: + json.dump({"id": "global_track", "title": "Global Track"}, f) + + # Override global conductor dir via env var + os.environ["SLOP_CONDUCTOR_DIR"] = str(global_conductor) + try: + paths.reset_resolved() + # Pass project_path pointing to a dir without TOML + tracks = project_manager.get_all_tracks(str(empty_dir)) + # paths.get_conductor_dir(str(empty_dir)) should fall back to global + assert any(t["id"] == "global_track" for t in tracks) + finally: + del os.environ["SLOP_CONDUCTOR_DIR"]