docs(conductor): Synchronize docs for track 'Context Composition Presets'
This commit is contained in:
@@ -17,7 +17,7 @@ For deep implementation details when planning or implementing tracks, consult `d
|
||||
## Primary Use Cases
|
||||
|
||||
- **Full Control over Vendor APIs:** Exposing detailed API metrics and configuring deep agent capabilities directly within the GUI.
|
||||
- **Context & Memory Management:** Better visualization and management of token usage and context memory. Features an independent **Context Composition** panel decoupled from the project whitelist, with directory-grouped listings and per-file **View Modes** (Full, Summary, Skeleton, Outline, None). Includes a **Visual Slice Editor** for creating fuzzy-anchored line ranges with **Annotations** (tags and comments), and **View Presets** for saving named configurations of view settings. Features a dedicated **'Context' role** for manual injections and **Context Presets** for saving and loading complete compositions. Allows assigning specific context presets to MMA agent personas for granular cognitive load isolation.
|
||||
- **Context & Memory Management:** Better visualization and management of token usage and context memory. Features an independent **Context Composition** panel decoupled from the project whitelist, with directory-grouped listings and per-file **View Modes** (Full, Summary, Skeleton, Outline, None). Includes a **Visual Slice Editor** for creating fuzzy-anchored line ranges with **Annotations** (tags and comments), and **View Presets** for saving named configurations of view settings. Features a dedicated **'Context' role** for manual injections and **Context Presets** for saving and loading complete compositions with **File Existence Validation**. Includes a high-fidelity **Context Preview** window providing a scrollable Markdown view of the final aggregated context and real-time token estimation before dispatching to agents. Allows assigning specific context presets to MMA agent personas for granular cognitive load isolation.
|
||||
- **Manual "Vibe Coding" Assistant:** Serving as an auxiliary, multi-provider assistant that natively interacts with the codebase via sandboxed PowerShell scripts and MCP-like file tools, emphasizing manual developer oversight and explicit confirmation.
|
||||
- **State-Preserving Hot Reload:** Supports selective, manual hot-reloading of Python modules (including the main GUI logic) via a delegation-based architecture. This allows for rapid UI iteration without losing application state or restarting the session.
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
|
||||
- **src/personas.py:** Implements `PersonaManager` for high-performance CRUD operations on unified agent personas stored in TOML format (`personas.toml`, `project_personas.toml`). Handles consolidation of model settings, prompts, and tool biases.
|
||||
|
||||
- **src/context_presets.py:** Implements `ContextPresetManager` for managing complex context compositions (files, view modes, screenshots) within the project configuration. Supports validation of file existence and relative path mapping.
|
||||
|
||||
- **src/tool_bias.py:** Implements the `ToolBiasEngine` for semantic tool description nudging and dynamic tooling strategy generation.
|
||||
|
||||
- **src/tool_presets.py:** Extends `ToolPresetManager` to handle nested `Tool` models, weights, and global `BiasProfile` persistence within `tool_presets.toml`.
|
||||
|
||||
+100
-28
@@ -135,11 +135,31 @@ class App:
|
||||
self.controller._predefined_callbacks['load_context_preset'] = self.controller.load_context_preset
|
||||
self.controller._predefined_callbacks['set_ui_file_paths'] = lambda p: setattr(self, 'ui_file_paths', p)
|
||||
self.controller._predefined_callbacks['set_ui_screenshot_paths'] = lambda p: setattr(self, 'ui_screenshot_paths', p)
|
||||
self.controller._predefined_callbacks['set_context_files_for_test'] = lambda files: setattr(self, 'context_files', [models.FileItem(path=f) for f in files])
|
||||
self.controller._predefined_callbacks['set_screenshots_for_test'] = lambda ss: setattr(self, 'screenshots', ss)
|
||||
|
||||
def _save_context_preset_force(name: str):
|
||||
if not name: return
|
||||
preset_files = []
|
||||
for f in self.context_files:
|
||||
p = f.path if hasattr(f, 'path') else str(f)
|
||||
vm = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm))
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(self.screenshots))
|
||||
self.controller.save_context_preset(preset)
|
||||
self.ui_new_context_preset_name = ""
|
||||
self.show_missing_files_modal = False
|
||||
|
||||
self.controller._predefined_callbacks['save_context_preset_force'] = _save_context_preset_force
|
||||
self.controller._predefined_callbacks['set_ui_attr'] = lambda k, v: setattr(self, k, v)
|
||||
self.controller._predefined_callbacks['set_context_files'] = self._set_context_files
|
||||
self.controller._predefined_callbacks['simulate_save_preset'] = self._simulate_save_preset
|
||||
self.controller._clickable_actions.update({
|
||||
'btn_undo': self._handle_undo,
|
||||
'btn_redo': self._handle_redo,
|
||||
'btn_open_external_editor': self._open_patch_in_external_editor,
|
||||
'Save##ctx': self._handle_save_ctx_click,
|
||||
'Save Anyway': self._handle_save_anyway_click,
|
||||
})
|
||||
# --- UI Component State ---
|
||||
self.text_viewer_wrap = True
|
||||
@@ -156,6 +176,11 @@ class App:
|
||||
# --- Preset & Profile Management State ---
|
||||
self.ui_active_context_preset = ""
|
||||
self.ui_new_context_preset_name = ""
|
||||
self._pending_save_ctx_click = False
|
||||
self._pending_save_anyway_click = False
|
||||
self.show_missing_files_modal = False
|
||||
self.missing_context_files = []
|
||||
self.target_context_preset_name = ""
|
||||
self._new_preset_name = ""
|
||||
self._editing_preset_name = ""
|
||||
self._editing_preset_system_prompt = ""
|
||||
@@ -258,6 +283,10 @@ class App:
|
||||
self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8}
|
||||
self._hot_reload_error: Optional[str] = None
|
||||
|
||||
def _set_context_files(self, paths: list[str]) -> None:
|
||||
from src import models
|
||||
self.context_files = [models.FileItem(path=p) for p in paths]
|
||||
|
||||
def _simulate_save_preset(self, name: str) -> None:
|
||||
from src import models
|
||||
item = models.FileItem(path='test.py')
|
||||
@@ -269,6 +298,12 @@ class App:
|
||||
"""UI-level wrapper for approving a pending tool execution ask."""
|
||||
self.controller._handle_approve_ask()
|
||||
|
||||
def _handle_save_ctx_click(self) -> None:
|
||||
self._pending_save_ctx_click = True
|
||||
|
||||
def _handle_save_anyway_click(self) -> None:
|
||||
self._pending_save_anyway_click = True
|
||||
|
||||
def _post_init(self) -> None:
|
||||
theme.apply_current()
|
||||
|
||||
@@ -553,27 +588,6 @@ class App:
|
||||
pass
|
||||
self.controller.shutdown()
|
||||
|
||||
def save_context_preset(self, name: str) -> None:
|
||||
"""
|
||||
[C: tests/test_context_presets.py:test_save_context_preset]
|
||||
"""
|
||||
sys.stderr.write(f"[DEBUG] save_context_preset called with: {name}\n")
|
||||
sys.stderr.flush()
|
||||
if 'context_presets' not in self.controller.project:
|
||||
self.controller.project['context_presets'] = {}
|
||||
|
||||
preset_files = []
|
||||
for f in self.context_files:
|
||||
path = f.path if hasattr(f, 'path') else str(f)
|
||||
view_mode = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
preset_files.append(models.ContextFileEntry(path=path, view_mode=view_mode))
|
||||
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(self.screenshots))
|
||||
self.controller.project['context_presets'][name] = preset.to_dict()
|
||||
self.controller._save_active_project()
|
||||
sys.stderr.write(f"[DEBUG] save_context_preset finished. Project keys: {list(self.controller.project.keys())}\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
def load_context_preset(self, name: str) -> None:
|
||||
"""
|
||||
[C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset]
|
||||
@@ -1203,6 +1217,7 @@ def render_main_interface(app: App) -> None:
|
||||
# Modals / Popups
|
||||
render_approve_script_modal(app)
|
||||
render_mma_modals(app)
|
||||
render_context_modals(app)
|
||||
|
||||
def render_custom_title_bar(app: App) -> None:
|
||||
# Obsolete, removed since it renders behind the full screen dock space.
|
||||
@@ -2845,12 +2860,12 @@ def render_context_composition_panel(app: App) -> None:
|
||||
total_lines, total_ast = app._update_context_file_stats()
|
||||
render_context_batch_actions(app, total_lines, total_ast)
|
||||
render_context_files_table(app)
|
||||
|
||||
|
||||
imgui.separator()
|
||||
render_context_presets(app)
|
||||
imgui.separator()
|
||||
if imgui.collapsing_header("Screenshots"):
|
||||
render_context_screenshots(app)
|
||||
imgui.separator()
|
||||
render_context_presets(app)
|
||||
|
||||
def render_ast_inspector_modal(app: App) -> None:
|
||||
"""
|
||||
@@ -3165,10 +3180,34 @@ def render_context_presets(app: App) -> None:
|
||||
changed, new_name = imgui.input_text("##new_preset", getattr(app, "ui_new_context_preset_name", ""))
|
||||
if changed: app.ui_new_context_preset_name = new_name
|
||||
imgui.same_line()
|
||||
if imgui.button("Save##ctx"):
|
||||
if getattr(app, "ui_new_context_preset_name", "").strip():
|
||||
app.save_context_preset(app.ui_new_context_preset_name.strip())
|
||||
app.ui_new_context_preset_name = ""
|
||||
if imgui.button("Save##ctx") or getattr(app, "_pending_save_ctx_click", False):
|
||||
app._pending_save_ctx_click = False
|
||||
name = getattr(app, "ui_new_context_preset_name", "").strip()
|
||||
if name:
|
||||
missing = []
|
||||
root = app.controller.active_project_root
|
||||
for f in app.context_files:
|
||||
path = f.path if hasattr(f, "path") else str(f)
|
||||
if not os.path.isabs(path):
|
||||
full_path = os.path.join(root, path)
|
||||
else:
|
||||
full_path = path
|
||||
if not os.path.exists(full_path):
|
||||
missing.append(path)
|
||||
|
||||
if missing:
|
||||
app.missing_context_files = missing
|
||||
app.show_missing_files_modal = True
|
||||
app.target_context_preset_name = name
|
||||
else:
|
||||
preset_files = []
|
||||
for f in app.context_files:
|
||||
p = f.path if hasattr(f, 'path') else str(f)
|
||||
vm = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm))
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(app.screenshots))
|
||||
app.controller.save_context_preset(preset)
|
||||
app.ui_new_context_preset_name = ""
|
||||
imgui.same_line()
|
||||
if imgui.button("Delete##ctx"):
|
||||
if getattr(app, "ui_active_context_preset", ""):
|
||||
@@ -5197,3 +5236,36 @@ def render_mma_focus_selector(app: App) -> None:
|
||||
if app.ui_focus_agent and imgui.button("x##clear_focus"): app.ui_focus_agent = None
|
||||
|
||||
#endregion: MMA
|
||||
|
||||
def render_context_modals(app: App) -> None:
|
||||
if app.show_missing_files_modal:
|
||||
imgui.open_popup("Missing Files Warning")
|
||||
app.show_missing_files_modal = False
|
||||
|
||||
if imgui.begin_popup_modal("Missing Files Warning", True, imgui.WindowFlags_.always_auto_resize)[0]:
|
||||
imgui.text("The following files are missing from disk:")
|
||||
imgui.separator()
|
||||
imgui.begin_child("missing_files_list", imgui.ImVec2(0, 150), True)
|
||||
for f in app.missing_context_files:
|
||||
imgui.text_colored(imgui.ImVec4(1, 0.4, 0.4, 1), f)
|
||||
imgui.end_child()
|
||||
imgui.separator()
|
||||
|
||||
if imgui.button("Save Anyway") or getattr(app, "_pending_save_anyway_click", False):
|
||||
app._pending_save_anyway_click = False
|
||||
name = app.target_context_preset_name
|
||||
preset_files = []
|
||||
for f in app.context_files:
|
||||
p = f.path if hasattr(f, 'path') else str(f)
|
||||
vm = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm))
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(app.screenshots))
|
||||
app.controller.save_context_preset(preset)
|
||||
app.ui_new_context_preset_name = ""
|
||||
imgui.close_current_popup()
|
||||
|
||||
imgui.same_line()
|
||||
if imgui.button("Cancel"):
|
||||
imgui.close_current_popup()
|
||||
|
||||
imgui.end_popup()
|
||||
|
||||
@@ -11,7 +11,20 @@ def test_gui_context_preset_save_load(live_gui) -> None:
|
||||
test_files = ["test.py"]
|
||||
test_screenshots = ["test.png"]
|
||||
|
||||
client.push_event("custom_callback", {"callback": "simulate_save_preset", "args": [preset_name]})
|
||||
# Switch to Context Composition tab to ensure it's rendered
|
||||
client.push_event("select_tab", {"tab": "Context Composition"})
|
||||
time.sleep(1.0)
|
||||
|
||||
# Inject context state directly
|
||||
client.push_event("custom_callback", {"callback": "set_context_files_for_test", "args": [test_files]})
|
||||
client.push_event("custom_callback", {"callback": "set_screenshots_for_test", "args": [test_screenshots]})
|
||||
client.push_event("custom_callback", {"callback": "set_ui_attr", "args": ["ui_new_context_preset_name", preset_name]})
|
||||
time.sleep(1.0)
|
||||
# Trigger Save (which will trigger validation, and since test.py doesn't exist, it opens a modal)
|
||||
client.push_event("custom_callback", {"callback": "set_ui_attr", "args": ["_pending_save_ctx_click", True]})
|
||||
time.sleep(1.0)
|
||||
# The "Missing Files Warning" modal should be open. Trigger "Save Anyway".
|
||||
client.push_event("custom_callback", {"callback": "set_ui_attr", "args": ["_pending_save_anyway_click", True]})
|
||||
time.sleep(1.5)
|
||||
|
||||
project_data = client.get_project()
|
||||
@@ -25,6 +38,11 @@ def test_gui_context_preset_save_load(live_gui) -> None:
|
||||
assert preset_files == test_files
|
||||
assert preset_entry.get("screenshots", []) == test_screenshots
|
||||
|
||||
# Clear current state
|
||||
client.push_event("custom_callback", {"callback": "set_context_files_for_test", "args": [[]]})
|
||||
client.push_event("custom_callback", {"callback": "set_screenshots_for_test", "args": [[]]})
|
||||
time.sleep(1.0)
|
||||
|
||||
# Load the preset
|
||||
client.push_event("custom_callback", {"callback": "load_context_preset", "args": [preset_name]})
|
||||
time.sleep(1.0)
|
||||
|
||||
Reference in New Issue
Block a user