From 2441ea64a3f5bfe87309b01e015f37528b15ee2e Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 2 May 2026 19:00:28 -0400 Subject: [PATCH] conductor(checkpoint): Checkpoint end of Phase 4 --- src/app_controller.py | 23 +++++++++++++-- src/gui_2.py | 50 +++++++++++++++++++++++++++++++-- tests/test_system_prompt_sim.py | 12 ++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/app_controller.py b/src/app_controller.py index a66f65e..a96fa53 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -235,6 +235,7 @@ class AppController: self.ui_global_system_prompt: str = "" self.ui_base_system_prompt: str = "" self.ui_use_default_base_prompt: bool = True + self._show_base_prompt_diff_modal: bool = False self.ui_project_context_marker: str = "" self.ui_agent_tools: Dict[str, bool] = {} self.available_models: List[str] = [] @@ -358,6 +359,7 @@ class AppController: 'project_system_prompt': 'ui_project_system_prompt', 'base_system_prompt': 'ui_base_system_prompt', 'use_default_base_prompt': 'ui_use_default_base_prompt', + 'show_base_prompt_diff_modal': '_show_base_prompt_diff_modal', 'global_preset_name': 'ui_global_preset_name', 'project_preset_name': 'ui_project_preset_name', 'ui_active_tool_preset': 'ui_active_tool_preset', @@ -414,6 +416,7 @@ class AppController: 'project_system_prompt': 'ui_project_system_prompt', 'base_system_prompt': 'ui_base_system_prompt', 'use_default_base_prompt': 'ui_use_default_base_prompt', + 'show_base_prompt_diff_modal': '_show_base_prompt_diff_modal', 'global_preset_name': 'ui_global_preset_name', 'project_preset_name': 'ui_project_preset_name', 'ui_active_tool_preset': 'ui_active_tool_preset', @@ -515,6 +518,8 @@ class AppController: 'btn_approve_mma_step': lambda: self._handle_mma_respond(approved=True), 'btn_approve_spawn': lambda: self._handle_mma_respond(approved=True), 'btn_prune_logs': self.cb_prune_logs, + 'btn_reset_base_prompt': self._cb_reset_base_prompt, + 'btn_show_base_prompt_diff': self._cb_show_base_prompt_diff, } self._predefined_callbacks: dict[str, Callable[..., Any]] = { '_test_callback_func_write_to_file': self._test_callback_func_write_to_file, @@ -1413,10 +1418,12 @@ class AppController: # Clear response area for new turn self.ai_response = "" csp = filter(bool, [self.ui_global_system_prompt.strip(), self.ui_project_system_prompt.strip()]) - ai_client.set_custom_system_prompt("\n\n".join(csp)) + custom_prompt = "\n\n".join(csp) + ai_client.set_custom_system_prompt(custom_prompt) ai_client.set_base_system_prompt(self.ui_base_system_prompt) ai_client.set_use_default_base_prompt(self.ui_use_default_base_prompt) ai_client.set_project_context_marker(self.ui_project_context_marker) + self.last_resolved_system_prompt = ai_client.get_combined_system_prompt() ai_client.set_model_params(self.temperature, self.max_tokens, self.history_trunc_limit, self.top_p) ai_client.set_agent_tools(self.ui_agent_tools) # Force update adapter path right before send to bypass potential duplication issues self._update_gcli_adapter(self.ui_gemini_cli_path) @@ -1971,6 +1978,13 @@ class AppController: models.save_config(self.config) self._set_status("config saved") + def _cb_reset_base_prompt(self, user_data=None) -> None: + self.ui_base_system_prompt = ai_client._SYSTEM_PROMPT + self.ui_use_default_base_prompt = False + + def _cb_show_base_prompt_diff(self, user_data=None) -> None: + self._show_base_prompt_diff_modal = True + def _cb_disc_create(self) -> None: nm = self.ui_disc_new_name_input.strip() if nm: @@ -2547,7 +2561,11 @@ class AppController: discussion_text = aggregate.build_discussion_text(history) csp = filter(bool, [self.ui_global_system_prompt.strip(), self.ui_project_system_prompt.strip()]) - self.last_resolved_system_prompt = "\n\n".join(csp) + ai_client.set_custom_system_prompt("\n\n".join(csp)) + ai_client.set_base_system_prompt(self.ui_base_system_prompt) + ai_client.set_use_default_base_prompt(self.ui_use_default_base_prompt) + ai_client.set_project_context_marker(self.ui_project_context_marker) + self.last_resolved_system_prompt = ai_client.get_combined_system_prompt() self.last_aggregate_markdown = full_md return full_md, path, file_items, stable_md, discussion_text @@ -2894,3 +2912,4 @@ class AppController: ) project_manager.save_track_state(self.active_track.id, state, self.active_project_root) + diff --git a/src/gui_2.py b/src/gui_2.py index 2cc9ab0..c6a1aa0 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -32,6 +32,7 @@ from src import bg_shader from src import thinking_parser from src import thinking_parser import re +import difflib import subprocess if sys.platform == "win32": import win32gui @@ -587,6 +588,7 @@ class App: self._tool_log_dirty = True self._render_track_proposal_modal() self._render_patch_modal() + self._render_base_prompt_diff_modal() self._render_save_preset_modal() self._render_preset_manager_window() self._render_tool_preset_manager_window() @@ -1113,6 +1115,41 @@ class App: if self.perf_profiling_enabled: self.perf_monitor.end_component("_gui_func") + def _render_base_prompt_diff_modal(self) -> None: + if not getattr(self.controller, "_show_base_prompt_diff_modal", False): + return + + imgui.open_popup("Base Prompt Diff") + if imgui.begin_popup_modal("Base Prompt Diff", True, imgui.WindowFlags_.always_auto_resize)[0]: + imgui.text_colored(C_IN, "Difference between Default and Custom Base System Prompt") + imgui.separator() + + default_lines = ai_client._SYSTEM_PROMPT.splitlines(keepends=True) + custom_lines = self.ui_base_system_prompt.splitlines(keepends=True) + + diff = list(difflib.unified_diff(default_lines, custom_lines, fromfile='Default', tofile='Custom')) + + if not diff: + imgui.text("No differences found.") + else: + imgui.begin_child("base_prompt_diff_scroll", imgui.ImVec2(800, 500), True) + for line in diff: + if line.startswith("+++") or line.startswith("---") or line.startswith("@@"): + imgui.text_colored(vec4(77, 178, 255), line.rstrip()) + elif line.startswith("+"): + imgui.text_colored(vec4(51, 230, 51), line.rstrip()) + elif line.startswith("-"): + imgui.text_colored(vec4(230, 51, 51), line.rstrip()) + else: + imgui.text(line.rstrip()) + imgui.end_child() + + imgui.separator() + if imgui.button("Close", imgui.ImVec2(120, 0)): + self.controller._show_base_prompt_diff_modal = False + imgui.close_current_popup() + imgui.end_popup() + def _render_save_preset_modal(self) -> None: if not self._show_save_preset_modal: return imgui.open_popup("Save Layout Preset") @@ -4277,9 +4314,16 @@ def hello(): imgui.separator() _, self.ui_use_default_base_prompt = imgui.checkbox("Use Default Base System Prompt", self.ui_use_default_base_prompt) imgui.same_line() - if imgui.button("Reset to Default"): - self.ui_base_system_prompt = ai_client._SYSTEM_PROMPT - self.ui_use_default_base_prompt = False + if imgui.button("Reset to Default##btn_reset_base_prompt"): + self.controller._cb_reset_base_prompt() + imgui.same_line() + if imgui.button("Show Diff##btn_show_base_prompt_diff"): + self.controller._cb_show_base_prompt_diff() + imgui.set_item_tooltip("Compare current base prompt with the default.") + + imgui.same_line() + imgui.text_disabled("(?)") + imgui.set_item_tooltip("The Base System Prompt contains foundational instructions for the AI, including its role as a coding assistant and safety guidelines. You can override it here if needed.") header_flags = imgui.TreeNodeFlags_.default_open if not self.ui_use_default_base_prompt else 0 if imgui.collapsing_header("Base System Prompt (foundational instructions)", header_flags): diff --git a/tests/test_system_prompt_sim.py b/tests/test_system_prompt_sim.py index 5a734b9..e61570e 100644 --- a/tests/test_system_prompt_sim.py +++ b/tests/test_system_prompt_sim.py @@ -59,3 +59,15 @@ def test_system_prompt_sim(live_gui): # It should be back to ai_client._SYSTEM_PROMPT current_prompt = client.get_value('base_system_prompt') assert current_prompt == ai_client._SYSTEM_PROMPT, f"Prompt not reset. Got: {current_prompt[:50]}..." + + # 9. Verify 'Show Diff' modal + initial_modal = client.get_value('show_base_prompt_diff_modal') + assert initial_modal is False + + client.click('btn_show_base_prompt_diff') + time.sleep(0.5) + assert client.get_value('show_base_prompt_diff_modal') is True + + # Close it + client.set_value('show_base_prompt_diff_modal', False) + assert client.get_value('show_base_prompt_diff_modal') is False