feat(sdm): inject structural dependency mapping tags across codebase

Adds [C: caller] tags to functions/methods and [M: mutation] / [U: usage] tags to class variables based on cross-module call analysis.
This commit is contained in:
2026-05-13 22:35:41 -04:00
parent 5bb3a9026f
commit b5e512f483
110 changed files with 1673 additions and 1008 deletions
+39 -27
View File
@@ -61,7 +61,10 @@ def resolve_paths(base_dir: Path, entry: str) -> list[Path]:
return sorted(filtered)
def group_files_by_dir(files: list[Any]) -> dict[str, list[Any]]:
"""Groups FileItem objects by their relative directory path."""
"""
Groups FileItem objects by their relative directory path.
[C: src/gui_2.py:App._render_context_files_table, tests/test_context_composition_phase3.py:test_group_files_by_dir]
"""
grouped = {}
for f in files:
path_str = f.path if hasattr(f, 'path') else str(f)
@@ -76,7 +79,10 @@ def group_files_by_dir(files: list[Any]) -> dict[str, list[Any]]:
return grouped
def compute_file_stats(abs_path: str) -> dict[str, int]:
"""Computes lines and basic AST stats for a file."""
"""
Computes lines and basic AST stats for a file.
[C: src/gui_2.py:App._stats_worker, tests/test_context_composition_phase3.py:test_compute_file_stats]
"""
stats = {"lines": 0, "ast_elements": 0}
try:
with open(abs_path, 'r', encoding='utf-8') as f:
@@ -97,8 +103,9 @@ def build_discussion_section(history: list[Any]) -> str:
"""
Builds a markdown section for discussion history.
Handles both legacy list[str] and new list[dict].
Builds a markdown section for discussion history.
Handles both legacy list[str] and new list[dict].
"""
sections = []
for i, entry in enumerate(history, start=1):
@@ -133,20 +140,21 @@ def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[
"""
Return a list of dicts describing each file, for use by ai_client when it
wants to upload individual files rather than inline everything as markdown.
Each dict has:
path : Path (resolved absolute path)
entry : str (original config entry string)
content : str (file text, or error string)
error : bool
mtime : float (last modification time, for skip-if-unchanged optimization)
tier : int | None (optional tier for context management)
auto_aggregate : bool
force_full : bool
view_mode : str (summary, full, skeleton, outline, none)
[C: src/app_controller.py:AppController._bg_task, src/orchestrator_pm.py:module, tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_aggregate_flags.py:test_force_full, tests/test_tiered_context.py:test_build_file_items_with_tiers]
Return a list of dicts describing each file, for use by ai_client when it
wants to upload individual files rather than inline everything as markdown.
Each dict has:
path : Path (resolved absolute path)
entry : str (original config entry string)
content : str (file text, or error string)
error : bool
mtime : float (last modification time, for skip-if-unchanged optimization)
tier : int | None (optional tier for context management)
auto_aggregate : bool
force_full : bool
view_mode : str (summary, full, skeleton, outline, none)
[C: src/app_controller.py:AppController._bg_task, src/orchestrator_pm.py:module, tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_aggregate_flags.py:test_force_full, tests/test_context_composition_phase6.py:test_view_mode_custom, tests/test_context_composition_phase6.py:test_view_mode_custom_empty_default_to_summary, tests/test_context_composition_phase6.py:test_view_mode_default_summary, tests/test_context_composition_phase6.py:test_view_mode_full, tests/test_context_composition_phase6.py:test_view_mode_none, tests/test_context_composition_phase6.py:test_view_mode_outline, tests/test_context_composition_phase6.py:test_view_mode_skeleton, tests/test_context_composition_phase6.py:test_view_mode_summary, tests/test_tiered_context.py:test_build_file_items_with_tiers, tests/test_tiered_context.py:test_build_files_section_with_dicts]
"""
with get_monitor().scope("build_file_items"):
items: list[dict[str, Any]] = []
@@ -245,8 +253,9 @@ def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[
def _build_files_section_from_items(file_items: list[dict[str, Any]]) -> str:
"""
Build the files markdown section from pre-read file items (avoids double I/O).
[C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_ui_summary_only_removal.py:test_aggregate_from_items_respects_auto_aggregate]
Build the files markdown section from pre-read file items (avoids double I/O).
[C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_context_composition_phase6.py:test_files_section_rendering, tests/test_tiered_context.py:test_build_files_section_with_dicts, tests/test_ui_summary_only_removal.py:test_aggregate_from_items_respects_auto_aggregate]
"""
sections = []
for item in file_items:
@@ -323,7 +332,8 @@ def build_markdown_from_items(file_items: list[dict[str, Any]], screenshot_base_
def build_markdown_no_history(file_items: list[dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], summary_only: bool = False, aggregation_strategy: str = "auto") -> str:
"""
Build markdown with only files + screenshots (no history). Used for stable caching.
Build markdown with only files + screenshots (no history). Used for stable caching.
[C: src/app_controller.py:AppController._do_generate, tests/test_history_management.py:test_aggregate_blacklist]
"""
return build_markdown_from_items(file_items, screenshot_base_dir, screenshots, history=[], summary_only=summary_only, aggregation_strategy=aggregation_strategy)
@@ -331,7 +341,8 @@ def build_markdown_no_history(file_items: list[dict[str, Any]], screenshot_base_
def build_discussion_text(history: list[str]) -> str:
"""
Build just the discussion history section text. Returns empty string if no history.
Build just the discussion history section text. Returns empty string if no history.
[C: src/app_controller.py:AppController._do_generate, tests/test_history_management.py:test_aggregate_includes_segregated_history]
"""
if not history:
@@ -342,9 +353,10 @@ def build_tier3_context(file_items: list[dict[str, Any]], screenshot_base_dir: P
"""
Tier 3 Context: Execution/Worker.
Full content for focus_files and files with tier=3, summaries/skeletons for others.
[C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_aggregate_flags.py:test_force_full, tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_tiered_context.py:test_build_tier3_context_ast_skeleton, tests/test_tiered_context.py:test_build_tier3_context_exists, tests/test_tiered_context.py:test_tiered_context_by_tier_field]
Tier 3 Context: Execution/Worker.
Full content for focus_files and files with tier=3, summaries/skeletons for others.
[C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_aggregate_flags.py:test_force_full, tests/test_ast_masking_core.py:test_ast_masking_gencpp_samples, tests/test_gencpp_full_suite.py:test_gencpp_full_suite, tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_tiered_context.py:test_build_tier3_context_ast_skeleton, tests/test_tiered_context.py:test_build_tier3_context_exists, tests/test_tiered_context.py:test_tiered_context_by_tier_field]
"""
with get_monitor().scope("build_tier3_context"):
focus_set = set(focus_files)
@@ -446,7 +458,7 @@ def build_markdown(base_dir: Path, files: list[str | dict[str, Any]], screenshot
def run(config: dict[str, Any], aggregation_strategy: str = "auto") -> tuple[str, Path, list[dict[str, Any]]]:
"""
[C: simulation/sim_base.py:run_sim, src/ai_client.py:_send_anthropic, src/ai_client.py:_send_deepseek, src/ai_client.py:_send_gemini, src/ai_client.py:_send_gemini_cli, src/ai_client.py:_send_minimax, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._start_track_logic, src/external_editor.py:_find_vscode_in_registry, src/gui_2.py:App._render_snapshot_tab, src/gui_2.py:App.run, src/gui_2.py:main, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/project_manager.py:get_git_log, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run]
[C: simulation/sim_base.py:run_sim, src/ai_client.py:_send_anthropic, src/ai_client.py:_send_deepseek, src/ai_client.py:_send_gemini, src/ai_client.py:_send_gemini_cli, src/ai_client.py:_send_minimax, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._start_track_logic, src/external_editor.py:_find_vscode_in_registry, src/gui_2.py:App._render_snapshot_tab, src/gui_2.py:App.run, src/gui_2.py:main, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run]
"""
namespace = config.get("project", {}).get("name")
if not namespace:
@@ -472,7 +484,7 @@ def run(config: dict[str, Any], aggregation_strategy: str = "auto") -> tuple[str
def main() -> None:
# Load global config to find active project
"""
[C: simulation/live_walkthrough.py:module, simulation/ping_pong.py:module, src/api_hooks.py:WebSocketServer._run_loop, src/gui_2.py:module, tests/mock_concurrent_mma.py:module, tests/mock_gemini_cli.py:module, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_cli_tool_bridge_mapping.py:module, tests/test_discussion_takes.py:module, tests/test_external_editor_gui.py:module, tests/test_headless_service.py:TestHeadlessStartup.test_headless_flag_triggers_run, tests/test_headless_service.py:TestHeadlessStartup.test_normal_startup_calls_app_run, tests/test_mma_skeleton.py:module, tests/test_orchestrator_pm.py:module, tests/test_orchestrator_pm_history.py:module, tests/test_post_process.py:module, tests/test_presets.py:module, tests/test_project_serialization.py:module, tests/test_run_worker_lifecycle_abort.py:module, tests/test_symbol_lookup.py:module, tests/test_system_prompt_exposure.py:module, tests/test_theme_nerv_fx.py:module]
[C: simulation/live_walkthrough.py:module, simulation/ping_pong.py:module, src/ai_server.py:module, src/api_hooks.py:WebSocketServer._run_loop, src/gui_2.py:module, tests/mock_concurrent_mma.py:module, tests/mock_gemini_cli.py:module, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_cli_tool_bridge_mapping.py:module, tests/test_discussion_takes.py:module, tests/test_external_editor_gui.py:module, tests/test_headless_service.py:TestHeadlessStartup.test_headless_flag_triggers_run, tests/test_headless_service.py:TestHeadlessStartup.test_normal_startup_calls_app_run, tests/test_mma_skeleton.py:module, tests/test_orchestrator_pm.py:module, tests/test_orchestrator_pm_history.py:module, tests/test_presets.py:module, tests/test_project_serialization.py:module, tests/test_run_worker_lifecycle_abort.py:module, tests/test_symbol_lookup.py:module, tests/test_system_prompt_exposure.py:module, tests/test_theme_nerv_fx.py:module]
"""
from src.paths import get_config_path
config_path = get_config_path()
+77 -46
View File
@@ -53,12 +53,18 @@ events: EventEmitter = EventEmitter()
class ProviderError(Exception):
def __init__(self, kind: str, provider: str, original: Exception) -> None:
"""
[C: src/api_hooks.py:HookServerInstance.__init__, src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.kind = kind
self.provider = provider
self.original = original
super().__init__(str(original))
def ui_message(self) -> str:
"""
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate]
"""
labels = {
"quota": "QUOTA EXHAUSTED",
"rate_limit": "RATE LIMITED",
@@ -74,8 +80,9 @@ class ProviderError(Exception):
def set_model_params(temp: float, max_tok: int, trunc_limit: int = 8000, top_p: float = 1.0) -> None:
"""
Sets global generation parameters like temperature and max tokens.
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate]
Sets global generation parameters like temperature and max tokens.
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate]
"""
global _temperature, _max_tokens, _history_trunc_limit, _top_p
_temperature = temp
@@ -130,21 +137,24 @@ _tool_approval_modes: dict[str, str] = {}
def get_current_tier() -> Optional[str]:
"""
Returns the current tier from thread-local storage.
Returns the current tier from thread-local storage.
[C: src/app_controller.py:AppController._on_tool_log, tests/test_ai_client_concurrency.py:intercepted_append]
"""
return getattr(_local_storage, "current_tier", None)
def set_current_tier(tier: Optional[str]) -> None:
"""
Sets the current tier in thread-local storage.
Sets the current tier in thread-local storage.
[C: src/app_controller.py:AppController._handle_request_event, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_mma_agent_focus_phase1.py:reset_tier, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2]
"""
_local_storage.current_tier = tier
def get_comms_log_callback() -> Optional[Callable[[dict[str, Any]], None]]:
"""
Returns the comms log callback (thread-local with global fallback).
Returns the comms log callback (thread-local with global fallback).
[C: src/multi_agent_conductor.py:run_worker_lifecycle]
"""
tl_cb = getattr(_local_storage, "comms_log_callback", None)
@@ -153,7 +163,8 @@ def get_comms_log_callback() -> Optional[Callable[[dict[str, Any]], None]]:
def set_comms_log_callback(cb: Optional[Callable[[dict[str, Any]], None]]) -> None:
"""
Sets the comms log callback (both global and thread-local).
Sets the comms log callback (both global and thread-local).
[C: src/app_controller.py:AppController._init_ai_and_hooks, src/multi_agent_conductor.py:run_worker_lifecycle]
"""
global comms_log_callback
@@ -196,29 +207,30 @@ _project_context_marker: str = ""
def set_custom_system_prompt(prompt: str) -> None:
"""
Sets a custom system prompt to be combined with the default instructions.
[C: simulation/user_agent.py:UserSimAgent.generate_response, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp]
Sets a custom system prompt to be combined with the default instructions.
[C: simulation/user_agent.py:UserSimAgent.generate_response, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp]
"""
global _custom_system_prompt
_custom_system_prompt = prompt
def set_base_system_prompt(prompt: str) -> None:
"""
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false]
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false]
"""
global _base_system_prompt_override
_base_system_prompt_override = prompt
def set_use_default_base_prompt(use_default: bool) -> None:
"""
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false]
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false]
"""
global _use_default_base_system_prompt
_use_default_base_system_prompt = use_default
def set_project_context_marker(marker: str) -> None:
"""
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate]
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate]
"""
global _project_context_marker
_project_context_marker = marker
@@ -228,7 +240,7 @@ def _get_context_marker() -> str:
def _get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optional[BiasProfile] = None) -> str:
"""
[C: tests/test_bias_efficacy.py:test_bias_efficacy_prompt_generation, tests/test_bias_integration.py:test_system_prompt_biasing, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false]
[C: tests/test_bias_efficacy.py:test_bias_efficacy_prompt_generation, tests/test_bias_integration.py:test_system_prompt_biasing, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false]
"""
if preset is None: preset = _active_tool_preset
if bias is None: bias = _active_bias_profile
@@ -246,7 +258,7 @@ def _get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optio
def get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optional[BiasProfile] = None) -> str:
"""
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event]
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event]
"""
return _get_combined_system_prompt(preset, bias)
@@ -260,7 +272,7 @@ COMMS_CLAMP_CHARS: int = 300
def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None:
"""
[C: tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2]
[C: tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2]
"""
entry: dict[str, Any] = {
"ts": datetime.datetime.now().strftime("%H:%M:%S"),
@@ -279,25 +291,25 @@ def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None:
def get_comms_log() -> list[dict[str, Any]]:
"""
[C: src/app_controller.py:AppController._bg_task, src/app_controller.py:AppController._recalculate_session_usage, src/app_controller.py:AppController._start_track_logic, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_token_usage.py:test_token_usage_tracking]
[C: src/app_controller.py:AppController._bg_task, src/app_controller.py:AppController._recalculate_session_usage, src/app_controller.py:AppController._start_track_logic, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_token_usage.py:test_token_usage_tracking]
"""
return list(_comms_log)
def clear_comms_log() -> None:
"""
[C: src/app_controller.py:AppController._handle_reset_session, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._show_menus, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_token_usage.py:test_token_usage_tracking]
[C: src/app_controller.py:AppController._handle_reset_session, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._show_menus, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_token_usage.py:test_token_usage_tracking]
"""
_comms_log.clear()
def get_credentials_path() -> Path:
"""
[C: src/mcp_client.py:_is_allowed]
[C: src/mcp_client.py:_is_allowed]
"""
return Path(os.environ.get("SLOP_CREDENTIALS", str(Path(__file__).parent.parent / "credentials.toml")))
def _load_credentials() -> dict[str, Any]:
"""
[C: tests/test_deepseek_infra.py:test_credentials_error_mentions_deepseek, tests/test_minimax_provider.py:test_minimax_credentials_template]
[C: src/ai_server.py:_send_anthropic, src/ai_server.py:_send_deepseek, src/ai_server.py:_send_gemini, src/ai_server.py:_send_minimax, src/ai_server.py:handle_command, tests/test_deepseek_infra.py:test_credentials_error_mentions_deepseek, tests/test_minimax_provider.py:test_minimax_credentials_template]
"""
cred_path = get_credentials_path()
try:
@@ -433,7 +445,8 @@ def _classify_minimax_error(exc: Exception) -> ProviderError:
def set_provider(provider: str, model: str) -> None:
"""
Updates the active LLM provider and model name.
Updates the active LLM provider and model name.
[C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.do_fetch, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_model_selection, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_minimax_provider.py:test_minimax_default_model, tests/test_minimax_provider.py:test_minimax_model_selection, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_rag_integration.py:test_rag_integration, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_tier4_interceptor.py:test_gemini_provider_passes_qa_callback_to_run_script, tests/test_token_usage.py:test_token_usage_tracking]
"""
global _provider, _model
@@ -459,14 +472,16 @@ def set_provider(provider: str, model: str) -> None:
def get_provider() -> str:
"""
Returns the current active provider name.
Returns the current active provider name.
[C: src/multi_agent_conductor.py:run_worker_lifecycle]
"""
return _provider
def cleanup() -> None:
"""
Performs cleanup operations like deleting server-side Gemini caches.
Performs cleanup operations like deleting server-side Gemini caches.
[C: src/app_controller.py:AppController.clear_cache, src/app_controller.py:AppController.shutdown, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking_cleanup, tests/test_log_registry.py:TestLogRegistry.tearDown, tests/test_project_serialization.py:TestProjectSerialization.tearDown]
"""
global _gemini_client, _gemini_cache, _gemini_cached_file_paths
@@ -479,8 +494,9 @@ def cleanup() -> None:
def reset_session() -> None:
"""
Clears conversation history and resets provider-specific session state.
[C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:run_worker_lifecycle, tests/conftest.py:live_gui, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_minimax_provider.py:test_minimax_history_bleed_stats, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_session_logger_reset.py:test_reset_session, tests/test_token_usage.py:test_token_usage_tracking]
Clears conversation history and resets provider-specific session state.
[C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:run_worker_lifecycle, tests/conftest.py:live_gui, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_session_logger_reset.py:test_reset_session, tests/test_token_usage.py:test_token_usage_tracking]
"""
global _gemini_client, _gemini_chat, _gemini_cache
global _gemini_cache_md_hash, _gemini_cache_created_at, _gemini_cached_file_paths
@@ -521,7 +537,7 @@ def reset_session() -> None:
def list_models(provider: str) -> list[str]:
"""
[C: src/app_controller.py:AppController.do_fetch, tests/test_agent_capabilities.py:test_agent_capabilities_listing, tests/test_ai_client_list_models.py:test_list_models_gemini_cli, tests/test_deepseek_infra.py:test_deepseek_model_listing, tests/test_minimax_provider.py:test_minimax_list_models]
[C: src/app_controller.py:AppController.do_fetch, tests/test_agent_capabilities.py:test_agent_capabilities_listing, tests/test_ai_client_list_models.py:test_list_models_gemini_cli, tests/test_deepseek_infra.py:test_deepseek_model_listing, tests/test_minimax_provider.py:test_minimax_list_models]
"""
creds = _load_credentials()
if provider == "gemini":
@@ -546,8 +562,9 @@ _agent_tools: dict[str, bool] = {}
def set_agent_tools(tools: dict[str, bool]) -> None:
"""
Configures which tools are enabled for the AI agent.
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, tests/test_agent_tools_wiring.py:test_build_anthropic_tools_conversion, tests/test_agent_tools_wiring.py:test_set_agent_tools, tests/test_tool_access_exclusion.py:test_build_anthropic_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_build_deepseek_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_gemini_tool_declaration_excludes_disabled, tests/test_tool_access_exclusion.py:test_set_agent_tools_clears_caches]
Configures which tools are enabled for the AI agent.
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate, tests/test_agent_tools_wiring.py:test_build_anthropic_tools_conversion, tests/test_agent_tools_wiring.py:test_set_agent_tools, tests/test_tool_access_exclusion.py:test_build_anthropic_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_build_deepseek_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_gemini_tool_declaration_excludes_disabled, tests/test_tool_access_exclusion.py:test_set_agent_tools_clears_caches]
"""
global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS
_agent_tools = tools
@@ -556,7 +573,8 @@ def set_agent_tools(tools: dict[str, bool]) -> None:
def set_tool_preset(preset_name: Optional[str]) -> None:
"""
Loads a tool preset and applies it via set_agent_tools.
Loads a tool preset and applies it via set_agent_tools.
[C: src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_persona_selector_panel, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_bias_integration.py:test_set_tool_preset_with_objects, tests/test_tool_preset_env.py:test_tool_preset_env_loading, tests/test_tool_preset_env.py:test_tool_preset_env_no_var, tests/test_tool_presets_execution.py:test_tool_ask_approval, tests/test_tool_presets_execution.py:test_tool_auto_approval, tests/test_tool_presets_execution.py:test_tool_rejection]
"""
global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS, _tool_approval_modes, _active_tool_preset
@@ -590,7 +608,8 @@ def set_tool_preset(preset_name: Optional[str]) -> None:
def set_bias_profile(profile_name: Optional[str]) -> None:
"""
Sets the active tool bias profile for tuning model behavior.
Sets the active tool bias profile for tuning model behavior.
[C: src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_agent_tools_panel, src/gui_2.py:App._render_persona_selector_panel, src/multi_agent_conductor.py:run_worker_lifecycle]
"""
global _active_bias_profile
@@ -613,7 +632,7 @@ def get_bias_profile() -> Optional[str]:
def _build_anthropic_tools() -> list[dict[str, Any]]:
"""
[C: tests/test_agent_tools_wiring.py:test_build_anthropic_tools_conversion, tests/test_tool_access_exclusion.py:test_build_anthropic_tools_excludes_disabled]
[C: tests/test_agent_tools_wiring.py:test_build_anthropic_tools_conversion, tests/test_tool_access_exclusion.py:test_build_anthropic_tools_excludes_disabled]
"""
raw_tools: list[dict[str, Any]] = []
for spec in mcp_client.get_tool_schemas():
@@ -654,7 +673,7 @@ _CACHED_ANTHROPIC_TOOLS: Optional[list[dict[str, Any]]] = None
def _get_anthropic_tools() -> list[dict[str, Any]]:
"""
[C: tests/test_bias_efficacy.py:test_bias_efficacy_prompt_generation, tests/test_bias_efficacy.py:test_bias_parameter_nudging, tests/test_bias_integration.py:test_tool_declaration_biasing_anthropic]
[C: tests/test_bias_efficacy.py:test_bias_efficacy_prompt_generation, tests/test_bias_efficacy.py:test_bias_parameter_nudging, tests/test_bias_integration.py:test_tool_declaration_biasing_anthropic]
"""
global _CACHED_ANTHROPIC_TOOLS
if _CACHED_ANTHROPIC_TOOLS is None:
@@ -663,7 +682,7 @@ def _get_anthropic_tools() -> list[dict[str, Any]]:
def _gemini_tool_declaration() -> Optional[types.Tool]:
"""
[C: tests/test_tool_access_exclusion.py:test_gemini_tool_declaration_excludes_disabled]
[C: tests/test_tool_access_exclusion.py:test_gemini_tool_declaration_excludes_disabled]
"""
raw_tools: list[dict[str, Any]] = []
for spec in mcp_client.get_tool_schemas():
@@ -733,8 +752,9 @@ async def _execute_tool_calls_concurrently(
) -> list[tuple[str, str, str, str]]: # tool_name, call_id, output, original_name
"""
Executes multiple tool calls concurrently using asyncio.gather.
Returns a list of (tool_name, call_id, output, original_name).
Executes multiple tool calls concurrently using asyncio.gather.
Returns a list of (tool_name, call_id, output, original_name).
[C: tests/test_async_tools.py:test_execute_tool_calls_concurrently_exception_handling, tests/test_async_tools.py:test_execute_tool_calls_concurrently_timing]
"""
monitor = performance_monitor.get_monitor()
@@ -783,7 +803,7 @@ async def _execute_single_tool_call_async(
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None
) -> tuple[str, str, str, str]:
"""
[C: tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call, tests/test_external_mcp_hitl.py:test_external_mcp_hitl_approval, tests/test_external_mcp_hitl.py:test_external_mcp_hitl_rejection, tests/test_tool_presets_execution.py:test_tool_ask_approval, tests/test_tool_presets_execution.py:test_tool_auto_approval, tests/test_tool_presets_execution.py:test_tool_rejection]
[C: tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call, tests/test_external_mcp_hitl.py:test_external_mcp_hitl_approval, tests/test_external_mcp_hitl.py:test_external_mcp_hitl_rejection, tests/test_tool_presets_execution.py:test_tool_ask_approval, tests/test_tool_presets_execution.py:test_tool_auto_approval, tests/test_tool_presets_execution.py:test_tool_rejection]
"""
if tier:
set_current_tier(tier)
@@ -921,7 +941,7 @@ def _build_file_diff_text(changed_items: list[dict[str, Any]]) -> str:
def _build_deepseek_tools() -> list[dict[str, Any]]:
"""
[C: tests/test_tool_access_exclusion.py:test_build_deepseek_tools_excludes_disabled]
[C: tests/test_tool_access_exclusion.py:test_build_deepseek_tools_excludes_disabled]
"""
raw_tools: list[dict[str, Any]] = []
for spec in mcp_client.get_tool_schemas():
@@ -1061,7 +1081,7 @@ def _strip_stale_file_refreshes(history: list[dict[str, Any]]) -> None:
def _chunk_text(text: str, chunk_size: int) -> list[str]:
"""
[C: src/rag_engine.py:RAGEngine._chunk_code, src/rag_engine.py:RAGEngine.index_file]
[C: src/rag_engine.py:RAGEngine._chunk_code, src/rag_engine.py:RAGEngine.index_file]
"""
return [text[i:i + chunk_size] for i in range(0, len(text), chunk_size)]
@@ -1178,6 +1198,9 @@ def _repair_anthropic_history(history: list[dict[str, Any]]) -> None:
})
def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_items: list[dict[str, Any]] | None = None, discussion_history: str = "", pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None, qa_callback: Optional[Callable[[str], str]] = None, stream_callback: Optional[Callable[[str], None]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
"""
[C: src/ai_server.py:_handle_send]
"""
monitor = performance_monitor.get_monitor()
if monitor.enabled: monitor.start_component("ai_client._send_anthropic")
try:
@@ -1358,7 +1381,7 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_item
def get_gemini_cache_stats() -> dict[str, Any]:
"""
[C: src/app_controller.py:AppController._recalculate_session_usage, src/app_controller.py:AppController._update_cached_stats, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client]
[C: src/app_controller.py:AppController._recalculate_session_usage, src/app_controller.py:AppController._update_cached_stats, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client]
"""
_ensure_gemini_client()
if not _gemini_client:
@@ -1398,7 +1421,7 @@ def _list_gemini_models(api_key: str) -> list[str]:
def _ensure_gemini_client() -> None:
"""
[C: src/rag_engine.py:GeminiEmbeddingProvider.embed]
[C: src/rag_engine.py:GeminiEmbeddingProvider.embed]
"""
global _gemini_client
if _gemini_client is None:
@@ -1424,7 +1447,7 @@ def _send_gemini(md_content: str, user_message: str, base_dir: str,
stream_callback: Optional[Callable[[str], None]] = None,
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
"""
[C: tests/test_tier4_interceptor.py:test_gemini_provider_passes_qa_callback_to_run_script]
[C: src/ai_server.py:_handle_send, tests/test_tier4_interceptor.py:test_gemini_provider_passes_qa_callback_to_run_script]
"""
global _gemini_chat, _gemini_cache, _gemini_cache_md_hash, _gemini_cache_created_at, _gemini_cached_file_paths
monitor = performance_monitor.get_monitor()
@@ -1656,6 +1679,9 @@ def _send_gemini_cli(md_content: str, user_message: str, base_dir: str,
qa_callback: Optional[Callable[[str], str]] = None,
stream_callback: Optional[Callable[[str], None]] = None,
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
"""
[C: src/ai_server.py:_handle_send]
"""
global _gemini_cli_adapter
try:
if _gemini_cli_adapter is None:
@@ -1802,6 +1828,9 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
qa_callback: Optional[Callable[[str], str]] = None,
stream_callback: Optional[Callable[[str], None]] = None,
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
"""
[C: src/ai_server.py:_handle_send]
"""
monitor = performance_monitor.get_monitor()
if monitor.enabled: monitor.start_component("ai_client._send_deepseek")
try:
@@ -2081,6 +2110,9 @@ def _send_minimax(md_content: str, user_message: str, base_dir: str,
qa_callback: Optional[Callable[[str], str]] = None,
stream_callback: Optional[Callable[[str], None]] = None,
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
"""
[C: src/ai_server.py:_handle_send]
"""
try:
mcp_client.configure(file_items or [], [base_dir])
creds = _load_credentials()
@@ -2297,7 +2329,6 @@ def _send_minimax(md_content: str, user_message: str, base_dir: str,
def run_tier4_analysis(stderr: str) -> str:
"""
[C: src/native_orchestrator.py:NativeOrchestrator.analyze_error]
"""
if not stderr or not stderr.strip():
return ""
@@ -2346,7 +2377,7 @@ def run_tier4_patch_callback(stderr: str, base_dir: str) -> Optional[str]:
def run_tier4_patch_generation(error: str, file_context: str) -> str:
"""
[C: src/gui_2.py:App.request_patch_from_tier4, src/native_orchestrator.py:NativeOrchestrator.run_tier4_patch, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_calls_ai, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_empty_error, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_returns_diff]
[C: src/gui_2.py:App.request_patch_from_tier4, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_calls_ai, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_empty_error, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_returns_diff]
"""
if not error or not error.strip():
return ""
@@ -2375,7 +2406,7 @@ def run_tier4_patch_generation(error: str, file_context: str) -> str:
def get_token_stats(md_content: str) -> dict[str, Any]:
"""
[C: src/app_controller.py:AppController._refresh_api_metrics]
[C: src/app_controller.py:AppController._refresh_api_metrics]
"""
global _provider, _gemini_client, _model, _CHARS_PER_TOKEN
total_tokens = 0
@@ -2424,7 +2455,7 @@ def send(
rag_engine: Optional[Any] = None,
) -> str:
"""
[C: simulation/user_agent.py:UserSimAgent.generate_response, src/api_hooks.py:WebSocketServer._handler, src/api_hooks.py:WebSocketServer.broadcast, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, src/native_orchestrator.py:NativeOrchestrator.execute_ticket, src/orchestrator_pm.py:generate_tracks, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_full_flow_integration, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_handles_tool_use_events, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_parses_jsonl_output, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_starts_subprocess_with_correct_args, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_parses_tool_calls_from_streaming_json, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_starts_subprocess_with_model, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_context_bleed_prevention, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_get_history_bleed_stats, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_token_usage.py:test_token_usage_tracking, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: simulation/user_agent.py:UserSimAgent.generate_response, src/api_hooks.py:WebSocketServer._handler, src/api_hooks.py:WebSocketServer.broadcast, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_full_flow_integration, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_handles_tool_use_events, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_parses_jsonl_output, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_starts_subprocess_with_correct_args, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_parses_tool_calls_from_streaming_json, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_starts_subprocess_with_model, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_context_bleed_prevention, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_token_usage.py:test_token_usage_tracking, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
monitor = performance_monitor.get_monitor()
if monitor.enabled: monitor.start_component("ai_client.send")
@@ -2473,7 +2504,7 @@ def send(
def _add_bleed_derived(d: dict[str, Any], sys_tok: int = 0, tool_tok: int = 0) -> dict[str, Any]:
"""
[C: tests/test_token_viz.py:test_add_bleed_derived_aliases, tests/test_token_viz.py:test_add_bleed_derived_breakdown, tests/test_token_viz.py:test_add_bleed_derived_headroom, tests/test_token_viz.py:test_add_bleed_derived_headroom_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_history_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_would_trim_false, tests/test_token_viz.py:test_add_bleed_derived_would_trim_true, tests/test_token_viz.py:test_would_trim_boundary_exact, tests/test_token_viz.py:test_would_trim_just_above_threshold, tests/test_token_viz.py:test_would_trim_just_below_threshold]
[C: tests/test_token_viz.py:test_add_bleed_derived_aliases, tests/test_token_viz.py:test_add_bleed_derived_breakdown, tests/test_token_viz.py:test_add_bleed_derived_headroom, tests/test_token_viz.py:test_add_bleed_derived_headroom_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_history_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_would_trim_false, tests/test_token_viz.py:test_add_bleed_derived_would_trim_true, tests/test_token_viz.py:test_would_trim_boundary_exact, tests/test_token_viz.py:test_would_trim_just_above_threshold, tests/test_token_viz.py:test_would_trim_just_below_threshold]
"""
cur = d.get("current", 0)
lim = d.get("limit", 0)
@@ -2500,7 +2531,7 @@ if os.environ.get("SLOP_TOOL_PRESET"):
def run_subagent_summarization(file_path: str, content: str, is_code: bool, outline: str) -> str:
"""
Performs a stateless summarization request using a sub-agent prompt. [C: src/summarize.py:summarise_file, tests/test_subagent_summarization.py:test_run_subagent_summarization_anthropic, tests/test_subagent_summarization.py:test_run_subagent_summarization_gemini]
[C: src/summarize.py:summarise_file, tests/test_subagent_summarization.py:test_run_subagent_summarization_anthropic, tests/test_subagent_summarization.py:test_run_subagent_summarization_gemini]
"""
prompt_tmpl = mma_prompts.TIER4_SUMMARIZE_CODE_PROMPT if is_code else mma_prompts.TIER4_SUMMARIZE_TEXT_PROMPT
prompt = prompt_tmpl.format(file_path=file_path, outline=outline, content=content)
@@ -2549,4 +2580,4 @@ def run_subagent_summarization(file_path: str, content: str, is_code: bool, outl
return resp_data.get("text", "")
return "ERROR: Unsupported provider for sub-agent summarization"
#endregion: Subagent Summarization
#endregion: Subagent Summarization
+88 -51
View File
@@ -39,14 +39,15 @@ from typing import Any
class ApiHookClient:
def __init__(self, base_url: str = "http://127.0.0.1:8999", api_key: str | None = None):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.base_url = base_url.rstrip('/')
self.api_key = api_key
def _make_request(self, method: str, path: str, data: dict | None = None, timeout: float = 5.0) -> dict[str, Any] | None:
"""
Helper to make HTTP requests to the hook server.
Helper to make HTTP requests to the hook server.
[C: tests/test_api_hook_client.py:test_unsupported_method_error]
"""
url = f"{self.base_url}{path}"
@@ -71,8 +72,9 @@ class ApiHookClient:
def wait_for_server(self, timeout: int = 15) -> bool:
"""
Polls the health endpoint until the server responds or timeout occurs.
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_base.py:BaseSimulation.setup, tests/smoke_status_hook.py:test_status_hook, tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_conductor_api_hook_integration.py:test_conductor_integrates_api_hook_client_for_verification, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_custom_callback_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_preset_windows_layout.py:test_api_hook_under_load, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_tool_management_layout.py:test_tool_management_gettable_fields, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
Polls the health endpoint until the server responds or timeout occurs.
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_base.py:BaseSimulation.setup, tests/smoke_status_hook.py:test_status_hook, tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_conductor_api_hook_integration.py:test_conductor_integrates_api_hook_client_for_verification, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_custom_callback_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_phase6_simulation.py:test_ast_inspector_modal_opens, tests/test_phase6_simulation.py:test_batch_operations_shift_click, tests/test_phase6_simulation.py:test_slice_editor_add_remove, tests/test_preset_windows_layout.py:test_api_hook_under_load, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_tool_management_layout.py:test_tool_management_gettable_fields, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_mma.py:test_visual_mma_components, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
"""
start = time.time()
while time.time() - start < timeout:
@@ -84,8 +86,9 @@ class ApiHookClient:
def get_status(self) -> dict[str, Any]:
"""
Checks the health of the hook server.
[C: tests/test_api_hook_client.py:test_get_status_success, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_hooks.py:test_live_hook_server_responses, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_preset_windows_layout.py:make_request, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls]
Checks the health of the hook server.
[C: tests/test_api_hook_client.py:test_get_status_success, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_hooks.py:test_live_hook_server_responses, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_phase6_simulation.py:test_ast_inspector_modal_opens, tests/test_phase6_simulation.py:test_batch_operations_shift_click, tests/test_phase6_simulation.py:test_slice_editor_add_remove, tests/test_preset_windows_layout.py:make_request, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls]
"""
res = self._make_request('GET', '/status')
if res is None:
@@ -96,28 +99,32 @@ class ApiHookClient:
def post_project(self, project_data: dict) -> dict[str, Any]:
"""
Updates the current project configuration.
Updates the current project configuration.
[C: simulation/sim_context.py:ContextSimulation.run]
"""
return self._make_request('POST', '/api/project', data=project_data) or {}
def get_project(self) -> dict[str, Any]:
"""
Retrieves the current project state.
Retrieves the current project state.
[C: simulation/sim_context.py:ContextSimulation.run, tests/test_api_hook_client.py:test_get_project_success, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_workflow.py:test_full_live_workflow]
"""
return self._make_request('GET', '/api/project') or {}
def get_session(self) -> dict[str, Any]:
"""
Retrieves the current discussion session history.
Retrieves the current discussion session history.
[C: simulation/ping_pong.py:main, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/sim_tools.py:ToolsSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/test_api_hook_client.py:test_get_session_success, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_live_workflow.py:test_full_live_workflow, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim]
"""
return self._make_request('GET', '/api/session') or {}
def post_session(self, session_entries: list[dict]) -> dict[str, Any]:
"""
Updates the session history.
Updates the session history.
[C: tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_live_workflow.py:test_full_live_workflow]
"""
return self._make_request('POST', '/api/session', data={"session": {"entries": session_entries}}) or {}
@@ -129,7 +136,8 @@ class ApiHookClient:
def clear_events(self) -> list[dict[str, Any]]:
"""
Retrieves and clears the event queue.
Retrieves and clears the event queue.
[C: simulation/sim_base.py:BaseSimulation.setup]
"""
return self.get_events()
@@ -137,7 +145,7 @@ class ApiHookClient:
def wait_for_event(self, event_type: str, timeout: int = 5) -> dict[str, Any] | None:
"""
[C: simulation/sim_base.py:BaseSimulation.wait_for_event, simulation/sim_execution.py:ExecutionSimulation.run, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
[C: simulation/sim_base.py:BaseSimulation.wait_for_event, simulation/sim_execution.py:ExecutionSimulation.run, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
"""
start = time.time()
while time.time() - start < timeout:
@@ -150,69 +158,81 @@ class ApiHookClient:
def post_gui(self, payload: dict) -> dict[str, Any]:
"""
Pushes an event to the GUI's AsyncEventQueue via the /api/gui endpoint.
[C: tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_api_hook_client.py:test_post_gui_success, tests/test_gui2_parity.py:test_gui2_custom_callback_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_visual_mma.py:test_visual_mma_components]
Pushes an event to the GUI's AsyncEventQueue via the /api/gui endpoint.
[C: tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_api_hook_client.py:test_post_gui_success, tests/test_gui2_parity.py:test_gui2_custom_callback_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works]
"""
return self._make_request('POST', '/api/gui', data=payload) or {}
def push_event(self, action: str, payload: dict) -> dict[str, Any]:
"""
Convenience to push a GUI task.
[C: tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_auto_switch_sim.py:trigger_tier, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_visual_mma.py:test_visual_mma_components, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
Convenience to push a GUI task.
[C: tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_auto_switch_sim.py:trigger_tier, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_visual_mma.py:test_visual_mma_components, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
"""
return self.post_gui({"action": action, **payload})
def click(self, item: str, user_data: Any = None) -> dict[str, Any]:
"""
Simulates a button click.
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_base.py:BaseSimulation.setup, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.load_prior_log, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.setup_new_project, simulation/workflow_sim.py:WorkflowSimulator.truncate_history, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_mma_v2.py:_drain_approvals, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
Simulates a button click.
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_base.py:BaseSimulation.setup, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.load_prior_log, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.setup_new_project, simulation/workflow_sim.py:WorkflowSimulator.truncate_history, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_mma.py:test_visual_mma_components, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:_drain_approvals, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
"""
return self.post_gui({"action": "click", "item": item, "user_data": user_data})
def set_value(self, item: str, value: Any) -> dict[str, Any]:
"""
Sets the value of a GUI widget.
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_ai_settings.py:AISettingsSimulation.run, simulation/sim_base.py:BaseSimulation.setup, simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.setup_new_project, simulation/workflow_sim.py:WorkflowSimulator.truncate_history, tests/smoke_status_hook.py:test_status_hook, tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
Sets the value of a GUI widget.
[C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_ai_settings.py:AISettingsSimulation.run, simulation/sim_base.py:BaseSimulation.setup, simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.setup_new_project, simulation/workflow_sim.py:WorkflowSimulator.truncate_history, tests/smoke_status_hook.py:test_status_hook, tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout, tests/test_visual_mma.py:test_visual_mma_components, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout]
"""
return self.post_gui({"action": "set_value", "item": item, "value": value})
def select_tab(self, item: str, value: str) -> dict[str, Any]:
"""
Selects a specific tab in a tab bar.
Selects a specific tab in a tab bar.
[C: simulation/live_walkthrough.py:main, tests/test_api_hook_extensions.py:test_select_tab_integration]
"""
return self.set_value(item, value)
def select_list_item(self, item: str, value: str) -> dict[str, Any]:
"""
Selects an item in a listbox or combo.
Selects an item in a listbox or combo.
[C: simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.switch_discussion, tests/test_api_hook_extensions.py:test_select_list_item_integration, tests/test_live_workflow.py:test_full_live_workflow]
"""
return self.set_value(item, value)
def drag(self, src_item: str, dst_item: str) -> dict[str, Any]:
"""
Simulates a drag and drop operation.
Simulates a drag and drop operation.
[C: tests/test_api_hook_client.py:test_drag_success]
"""
return self.push_event("drag", {"src_item": src_item, "dst_item": dst_item})
def right_click(self, item: str) -> dict[str, Any]:
"""
Simulates a right-click on an item.
Simulates a right-click on an item.
[C: tests/test_api_hook_client.py:test_right_click_success]
"""
return self.push_event("right_click", {"item": item})
def get_gui_state(self) -> dict[str, Any]:
"""
Returns the full GUI state available via the hook API.
[C: tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_conductor_api_hook_integration.py:simulate_conductor_phase_completion, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, tests/test_live_workflow.py:test_full_live_workflow, tests/test_live_workflow.py:wait_for_value, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tool_management_layout.py:test_tool_management_gettable_fields, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout]
Returns the full GUI state available via the hook API.
[C: tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_conductor_api_hook_integration.py:simulate_conductor_phase_completion, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, tests/test_live_workflow.py:test_full_live_workflow, tests/test_live_workflow.py:wait_for_value, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tool_management_layout.py:test_tool_management_gettable_fields, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout, tests/test_visual_mma.py:test_visual_mma_components]
"""
return self._make_request('GET', '/api/gui/state') or {}
def get_value(self, item: str) -> Any:
"""
Gets the value of a GUI item via its mapped field.
[C: simulation/sim_ai_settings.py:AISettingsSimulation.run, simulation/sim_base.py:BaseSimulation.get_value, simulation/sim_base.py:BaseSimulation.setup, simulation/sim_base.py:BaseSimulation.wait_for_element, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/smoke_status_hook.py:test_status_hook, tests/smoke_status_hook.py:wait_for_value, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_mma.py:test_visual_mma_components, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration]
Gets the value of a GUI item via its mapped field.
[C: simulation/sim_ai_settings.py:AISettingsSimulation.run, simulation/sim_base.py:BaseSimulation.get_value, simulation/sim_base.py:BaseSimulation.setup, simulation/sim_base.py:BaseSimulation.wait_for_element, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/smoke_status_hook.py:test_status_hook, tests/smoke_status_hook.py:wait_for_value, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration]
"""
# Try state endpoint first (new preferred way)
state = self.get_gui_state()
@@ -235,7 +255,8 @@ class ApiHookClient:
def get_text_value(self, item_tag: str) -> str | None:
"""
Wraps get_value and returns its string representation, or None.
Wraps get_value and returns its string representation, or None.
[C: tests/test_api_hook_client.py:test_get_text_value]
"""
val = self.get_value(item_tag)
@@ -243,7 +264,8 @@ class ApiHookClient:
def get_indicator_state(self, item_tag: str) -> dict[str, bool]:
"""
Returns the visibility/active state of a status indicator.
Returns the visibility/active state of a status indicator.
[C: simulation/live_walkthrough.py:main, tests/test_api_hook_extensions.py:test_get_indicator_state_integration, tests/test_live_workflow.py:test_full_live_workflow]
"""
val = self.get_value(item_tag)
@@ -251,35 +273,40 @@ class ApiHookClient:
def get_gui_diagnostics(self) -> dict[str, Any]:
"""
Retrieves performance and diagnostic metrics.
[C: tests/test_api_hook_client.py:test_get_performance_success, tests/test_hooks.py:test_live_hook_server_responses, tests/test_selectable_ui.py:test_selectable_label_stability]
Retrieves performance and diagnostic metrics.
[C: tests/test_api_hook_client.py:test_get_performance_success, tests/test_hooks.py:test_live_hook_server_responses, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing]
"""
return self._make_request('GET', '/api/gui/diagnostics') or {}
def get_performance(self) -> dict[str, Any]:
"""
Retrieves performance metrics from the dedicated endpoint.
[C: tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_gui_performance_requirements.py:test_idle_performance_requirements, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing]
Retrieves performance metrics from the dedicated endpoint.
[C: tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_gui_performance_requirements.py:test_idle_performance_requirements, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_selectable_ui.py:test_selectable_label_stability]
"""
return self._make_request('GET', '/api/performance') or {}
def get_mma_status(self) -> dict[str, Any]:
"""
Retrieves the dedicated MMA engine status.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:_poll_mma_status, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:_poll_mma_status, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:_poll]
Retrieves the dedicated MMA engine status.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:_poll_mma_status, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:_poll_mma_status, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_visual_mma.py:test_visual_mma_components, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:_poll]
"""
return self._make_request('GET', '/api/gui/mma_status') or {}
def get_mma_workers(self) -> dict[str, Any]:
"""
Retrieves status for all active MMA workers.
Retrieves status for all active MMA workers.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:_poll_mma_workers]
"""
return self._make_request('GET', '/api/mma/workers') or {}
def get_context_state(self) -> dict[str, Any]:
"""
Retrieves the current file and screenshot context state.
Retrieves the current file and screenshot context state.
[C: tests/test_gui_context_presets.py:test_gui_context_preset_save_load]
"""
return self._make_request('GET', '/api/context/state') or {}
@@ -294,7 +321,8 @@ class ApiHookClient:
def get_node_status(self, node_id: str) -> dict[str, Any]:
"""
Retrieves status for a specific node in the MMA DAG.
Retrieves status for a specific node in the MMA DAG.
[C: tests/test_api_hook_client.py:test_get_node_status]
"""
return self._make_request('GET', f'/api/mma/node/{node_id}') or {}
@@ -302,8 +330,9 @@ class ApiHookClient:
def request_confirmation(self, tool_name: str, args: dict) -> bool | None:
"""
Pushes a manual confirmation request and waits for response.
Blocks for up to 60 seconds.
Pushes a manual confirmation request and waits for response.
Blocks for up to 60 seconds.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_sync_hooks.py:test_api_ask_client_error, tests/test_sync_hooks.py:test_api_ask_client_method, tests/test_sync_hooks.py:test_api_ask_client_rejection]
"""
# Long timeout as this waits for human input (60 seconds)
@@ -314,8 +343,9 @@ class ApiHookClient:
def reset_session(self) -> None:
"""
Resets the current session via button click.
[C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:run_worker_lifecycle, tests/conftest.py:live_gui, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_minimax_provider.py:test_minimax_history_bleed_stats, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_session_logger_reset.py:test_reset_session, tests/test_token_usage.py:test_token_usage_tracking]
Resets the current session via button click.
[C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:run_worker_lifecycle, tests/conftest.py:live_gui, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_session_logger_reset.py:test_reset_session, tests/test_token_usage.py:test_token_usage_tracking]
"""
self.click("btn_reset")
@@ -328,14 +358,16 @@ class ApiHookClient:
def apply_patch(self) -> dict[str, Any]:
"""
Applies the pending patch.
Applies the pending patch.
[C: tests/test_patch_modal.py:test_apply_callback]
"""
return self._make_request('POST', '/api/patch/apply') or {}
def reject_patch(self) -> dict[str, Any]:
"""
Rejects the pending patch.
Rejects the pending patch.
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch]
"""
return self._make_request('POST', '/api/patch/reject') or {}
@@ -346,7 +378,8 @@ class ApiHookClient:
def spawn_mma_worker(self, data: dict) -> dict:
"""
Spawns a new MMA worker with the provided configuration.
Spawns a new MMA worker with the provided configuration.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation]
"""
return self._make_request('POST', '/api/mma/workers/spawn', data=data) or {}
@@ -357,7 +390,8 @@ class ApiHookClient:
def pause_mma_pipeline(self) -> dict:
"""
Pauses the MMA execution pipeline.
Pauses the MMA execution pipeline.
[C: tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow]
"""
return self._make_request('POST', '/api/mma/pipeline/pause') or {}
@@ -368,21 +402,24 @@ class ApiHookClient:
def inject_context(self, data: dict) -> dict:
"""
Injects custom file context into the application.
Injects custom file context into the application.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation]
"""
return self._make_request('POST', '/api/context/inject', data=data) or {}
def mutate_mma_dag(self, data: dict) -> dict:
"""
Mutates the MMA DAG (Directed Acyclic Graph) structure.
Mutates the MMA DAG (Directed Acyclic Graph) structure.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation]
"""
return self._make_request('POST', '/api/mma/dag/mutate', data=data) or {}
def approve_mma_ticket(self, ticket_id: str) -> dict:
"""
Manually approves a specific ticket for execution in Step Mode.
Manually approves a specific ticket for execution in Step Mode.
[C: tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow]
"""
return self._make_request('POST', '/api/mma/ticket/approve', data={"ticket_id": ticket_id}) or {}
return self._make_request('POST', '/api/mma/ticket/approve', data={"ticket_id": ticket_id}) or {}
+15 -14
View File
@@ -70,7 +70,8 @@ class HookServerInstance(ThreadingHTTPServer):
"""Custom HTTPServer that carries a reference to the main App instance."""
def __init__(self, server_address: tuple[str, int], RequestHandlerClass: type, app: Any) -> None:
"""
Initializes the server instance with an app reference.
Initializes the server instance with an app reference.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
super().__init__(server_address, RequestHandlerClass)
@@ -418,7 +419,7 @@ class HookHandler(BaseHTTPRequestHandler):
result = {"status": "done"}
def apply_patch():
"""
[C: tests/test_patch_modal.py:test_apply_callback]
[C: tests/test_patch_modal.py:test_apply_callback]
"""
try:
if hasattr(app, "_apply_pending_patch"):
@@ -447,7 +448,7 @@ class HookHandler(BaseHTTPRequestHandler):
result = {"status": "done"}
def reject_patch():
"""
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch]
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch]
"""
try:
app._show_patch_modal = False
@@ -566,7 +567,7 @@ class HookHandler(BaseHTTPRequestHandler):
elif self.path == "/api/mma/workers/kill":
def kill_worker():
"""
[C: src/app_controller.py:AppController.kill_worker, src/gui_2.py:App._cb_kill_ticket, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread]
[C: src/app_controller.py:AppController.kill_worker, src/gui_2.py:App._cb_kill_ticket, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread]
"""
try:
worker_id = data.get("worker_id")
@@ -608,7 +609,7 @@ class HookHandler(BaseHTTPRequestHandler):
elif self.path == "/api/context/inject":
def inject_context():
"""
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation]
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation]
"""
files = _get_app_attr(app, "files")
if isinstance(files, list):
@@ -672,7 +673,7 @@ class HookHandler(BaseHTTPRequestHandler):
class HookServer:
def __init__(self, app: Any, port: int = 8999) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.app = app
self.port = port
@@ -682,7 +683,7 @@ class HookServer:
def start(self) -> None:
"""
[C: src/app_controller.py:AppController._cb_accept_tracks, src/app_controller.py:AppController._cb_plan_epic, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._fetch_models, src/app_controller.py:AppController._handle_approve_ask, src/app_controller.py:AppController._handle_generate_send, src/app_controller.py:AppController._handle_md_only, src/app_controller.py:AppController._handle_reject_ask, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._prune_old_logs, src/app_controller.py:AppController._rebuild_rag_index, src/app_controller.py:AppController._run_event_loop, src/app_controller.py:AppController._start_track_logic, src/app_controller.py:AppController.cb_prune_logs, src/app_controller.py:AppController.start_services, src/gui_2.py:App._render_discussion_panel, src/mcp_client.py:ExternalMCPManager.add_server, src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: src/app_controller.py:AppController._cb_accept_tracks, src/app_controller.py:AppController._cb_plan_epic, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._fetch_models, src/app_controller.py:AppController._handle_approve_ask, src/app_controller.py:AppController._handle_generate_send, src/app_controller.py:AppController._handle_md_only, src/app_controller.py:AppController._handle_reject_ask, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._prune_old_logs, src/app_controller.py:AppController._rebuild_rag_index, src/app_controller.py:AppController._run_event_loop, src/app_controller.py:AppController._start_track_logic, src/app_controller.py:AppController.cb_prune_logs, src/app_controller.py:AppController.init_state, src/app_controller.py:AppController.start_services, src/gui_2.py:App._render_discussion_entry_read_mode, src/gui_2.py:App._update_context_file_stats, src/mcp_client.py:ExternalMCPManager.add_server, src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
if self.thread and self.thread.is_alive():
return
@@ -710,7 +711,7 @@ class HookServer:
def stop(self) -> None:
"""
[C: src/app_controller.py:AppController.shutdown, src/mcp_client.py:ExternalMCPManager.stop_all, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: src/app_controller.py:AppController.shutdown, src/mcp_client.py:ExternalMCPManager.stop_all, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
if self.websocket_server:
self.websocket_server.stop()
@@ -725,7 +726,7 @@ class WebSocketServer:
"""WebSocket gateway for real-time event streaming."""
def __init__(self, app: Any, port: int = 9000) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.app = app
self.port = port
@@ -760,7 +761,7 @@ class WebSocketServer:
self._stop_event = asyncio.Event()
async def main():
"""
[C: simulation/live_walkthrough.py:module, simulation/ping_pong.py:module, src/gui_2.py:module, tests/mock_concurrent_mma.py:module, tests/mock_gemini_cli.py:module, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_cli_tool_bridge_mapping.py:module, tests/test_discussion_takes.py:module, tests/test_external_editor_gui.py:module, tests/test_headless_service.py:TestHeadlessStartup.test_headless_flag_triggers_run, tests/test_headless_service.py:TestHeadlessStartup.test_normal_startup_calls_app_run, tests/test_mma_skeleton.py:module, tests/test_orchestrator_pm.py:module, tests/test_orchestrator_pm_history.py:module, tests/test_post_process.py:module, tests/test_presets.py:module, tests/test_project_serialization.py:module, tests/test_run_worker_lifecycle_abort.py:module, tests/test_symbol_lookup.py:module, tests/test_system_prompt_exposure.py:module, tests/test_theme_nerv_fx.py:module]
[C: simulation/live_walkthrough.py:module, simulation/ping_pong.py:module, src/gui_2.py:module, tests/mock_concurrent_mma.py:module, tests/mock_gemini_cli.py:module, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_cli_tool_bridge_mapping.py:module, tests/test_discussion_takes.py:module, tests/test_external_editor_gui.py:module, tests/test_headless_service.py:TestHeadlessStartup.test_headless_flag_triggers_run, tests/test_headless_service.py:TestHeadlessStartup.test_normal_startup_calls_app_run, tests/test_mma_skeleton.py:module, tests/test_orchestrator_pm.py:module, tests/test_orchestrator_pm_history.py:module, tests/test_presets.py:module, tests/test_project_serialization.py:module, tests/test_run_worker_lifecycle_abort.py:module, tests/test_symbol_lookup.py:module, tests/test_system_prompt_exposure.py:module, tests/test_theme_nerv_fx.py:module]
"""
async with serve(self._handler, "127.0.0.1", self.port) as server:
self.server = server
@@ -769,7 +770,7 @@ class WebSocketServer:
def start(self) -> None:
"""
[C: src/app_controller.py:AppController._cb_accept_tracks, src/app_controller.py:AppController._cb_plan_epic, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._fetch_models, src/app_controller.py:AppController._handle_approve_ask, src/app_controller.py:AppController._handle_generate_send, src/app_controller.py:AppController._handle_md_only, src/app_controller.py:AppController._handle_reject_ask, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._prune_old_logs, src/app_controller.py:AppController._rebuild_rag_index, src/app_controller.py:AppController._run_event_loop, src/app_controller.py:AppController._start_track_logic, src/app_controller.py:AppController.cb_prune_logs, src/app_controller.py:AppController.start_services, src/gui_2.py:App._render_discussion_panel, src/mcp_client.py:ExternalMCPManager.add_server, src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: src/app_controller.py:AppController._cb_accept_tracks, src/app_controller.py:AppController._cb_plan_epic, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._fetch_models, src/app_controller.py:AppController._handle_approve_ask, src/app_controller.py:AppController._handle_generate_send, src/app_controller.py:AppController._handle_md_only, src/app_controller.py:AppController._handle_reject_ask, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._prune_old_logs, src/app_controller.py:AppController._rebuild_rag_index, src/app_controller.py:AppController._run_event_loop, src/app_controller.py:AppController._start_track_logic, src/app_controller.py:AppController.cb_prune_logs, src/app_controller.py:AppController.init_state, src/app_controller.py:AppController.start_services, src/gui_2.py:App._render_discussion_entry_read_mode, src/gui_2.py:App._update_context_file_stats, src/mcp_client.py:ExternalMCPManager.add_server, src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
if self.thread and self.thread.is_alive():
return
@@ -778,7 +779,7 @@ class WebSocketServer:
def stop(self) -> None:
"""
[C: src/app_controller.py:AppController.shutdown, src/mcp_client.py:ExternalMCPManager.stop_all, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: src/app_controller.py:AppController.shutdown, src/mcp_client.py:ExternalMCPManager.stop_all, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
if self.loop and self._stop_event:
self.loop.call_soon_threadsafe(self._stop_event.set)
@@ -787,10 +788,10 @@ class WebSocketServer:
def broadcast(self, channel: str, payload: dict[str, Any]) -> None:
"""
[C: src/app_controller.py:AppController._process_pending_gui_tasks, src/events.py:AsyncEventQueue.put, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: src/app_controller.py:AppController._process_pending_gui_tasks, src/events.py:AsyncEventQueue.put, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
if not self.loop or channel not in self.clients:
return
message = json.dumps({"channel": channel, "payload": payload})
for ws in list(self.clients[channel]):
asyncio.run_coroutine_threadsafe(ws.send(message), self.loop)
asyncio.run_coroutine_threadsafe(ws.send(message), self.loop)
+217 -155
View File
File diff suppressed because it is too large Load Diff
+11 -6
View File
@@ -13,7 +13,7 @@ class Bead:
class BeadsClient:
def __init__(self, working_dir: Path):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.working_dir = Path(working_dir)
self.repo_dir = self.working_dir / ".beads_mock"
@@ -21,7 +21,8 @@ class BeadsClient:
def init_repo(self) -> None:
"""
Initialize the mock repository.
Initialize the mock repository.
[C: tests/test_aggregate_beads.py:test_build_beads_compaction, tests/test_beads_client.py:test_beads_init_and_query, tests/test_gui_dag_beads.py:test_load_active_tickets_from_beads, tests/test_mcp_client_beads.py:test_bd_mcp_tools]
"""
self.repo_dir.mkdir(parents=True, exist_ok=True)
@@ -30,14 +31,16 @@ class BeadsClient:
def is_initialized(self) -> bool:
"""
Check if the repository is initialized.
Check if the repository is initialized.
[C: src/mcp_client.py:dispatch, tests/test_beads_client.py:test_beads_init_and_query]
"""
return self.beads_file.exists()
def create_bead(self, title: str, description: str) -> str:
"""
Create a new bead and return its ID.
Create a new bead and return its ID.
[C: src/mcp_client.py:dispatch, tests/test_aggregate_beads.py:test_build_beads_compaction, tests/test_beads_client.py:test_beads_init_and_query, tests/test_gui_dag_beads.py:test_load_active_tickets_from_beads]
"""
beads = self._read_beads()
@@ -49,7 +52,8 @@ class BeadsClient:
def update_bead(self, bead_id: str, status: str) -> bool:
"""
Update the status of an existing bead.
Update the status of an existing bead.
[C: src/mcp_client.py:dispatch, tests/test_aggregate_beads.py:test_build_beads_compaction, tests/test_beads_client.py:test_beads_init_and_query]
"""
beads = self._read_beads()
@@ -62,7 +66,8 @@ class BeadsClient:
def list_beads(self) -> List[Bead]:
"""
List all beads.
List all beads.
[C: src/gui_2.py:App._render_beads_tab, src/mcp_client.py:dispatch, tests/test_beads_client.py:test_beads_init_and_query]
"""
return [Bead(**b) for b in self._read_beads()]
+4 -4
View File
@@ -8,7 +8,7 @@ from imgui_bundle import imgui, nanovg as nvg, hello_imgui
class BackgroundShader:
def __init__(self):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.enabled = False
self.start_time = time.time()
@@ -16,7 +16,7 @@ class BackgroundShader:
def render(self, width: float, height: float):
"""
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_heavy_text, src/gui_2.py:App._render_markdown_test, src/gui_2.py:App._render_response_panel, src/gui_2.py:App._render_snapshot_tab, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/markdown_helper.py:MarkdownRenderer.render, src/markdown_helper.py:render, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_discussion_entry_read_mode, src/gui_2.py:App._render_heavy_text, src/gui_2.py:App._render_markdown_test, src/gui_2.py:App._render_prior_session_view, src/gui_2.py:App._render_response_panel, src/gui_2.py:App._render_snapshot_tab, src/gui_2.py:App._render_text_viewer_window, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/markdown_helper.py:MarkdownRenderer.render, src/markdown_helper.py:render, src/theme_2.py:render_post_fx, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
"""
if not self.enabled or width <= 0 or height <= 0:
return
@@ -66,9 +66,9 @@ _bg: Optional[BackgroundShader] = None
def get_bg():
"""
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_theme_panel]
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_theme_panel]
"""
global _bg
if _bg is None:
_bg = BackgroundShader()
return _bg
return _bg
+8 -6
View File
@@ -42,9 +42,10 @@ from typing import Any
def generate_tickets(track_brief: str, module_skeletons: str) -> list[dict[str, Any]]:
"""
Tier 2 (Tech Lead) call.
Breaks down a Track Brief and module skeletons into discrete Tier 3 Tickets.
[C: src/native_orchestrator.py:NativeOrchestrator.generate_tickets, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_failure, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_success, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_success, tests/test_orchestration_logic.py:test_generate_tickets]
Tier 2 (Tech Lead) call.
Breaks down a Track Brief and module skeletons into discrete Tier 3 Tickets.
[C: tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_failure, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_success, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_success, tests/test_orchestration_logic.py:test_generate_tickets]
"""
# 1. Set Tier 2 Model (Tech Lead - Flash)
# 2. Construct Prompt
@@ -98,8 +99,9 @@ from src.models import Ticket
def topological_sort(tickets: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""
Sorts a list of tickets based on their 'depends_on' field.
Raises ValueError if a circular dependency or missing internal dependency is detected.
Sorts a list of tickets based on their 'depends_on' field.
Raises ValueError if a circular dependency or missing internal dependency is detected.
[C: tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_complex, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_cycle, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_empty, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_linear, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_missing_dependency, tests/test_conductor_tech_lead.py:test_topological_sort_vlog, tests/test_dag_engine.py:test_topological_sort, tests/test_dag_engine.py:test_topological_sort_cycle, tests/test_orchestration_logic.py:test_topological_sort, tests/test_orchestration_logic.py:test_topological_sort_circular, tests/test_perf_dag.py:test_dag_edge_cases, tests/test_perf_dag.py:test_dag_performance]
"""
# 1. Convert to Ticket objects for TrackDAG
@@ -121,4 +123,4 @@ if __name__ == "__main__":
test_brief = "Implement a new feature."
test_skeletons = "class NewFeature: pass"
tickets = generate_tickets(test_brief, test_skeletons)
print(json.dumps(tickets, indent=2))
print(json.dumps(tickets, indent=2))
+5 -4
View File
@@ -47,9 +47,10 @@ MODEL_PRICING = [
def estimate_cost(model: str, input_tokens: int, output_tokens: int) -> float:
"""
Estimate the cost of a model call based on input and output tokens.
Returns the total cost in USD.
[C: src/gui_2.py:App._render_mma_dashboard, src/gui_2.py:App._render_token_budget_panel, tests/test_cost_tracker.py:test_estimate_cost]
Estimate the cost of a model call based on input and output tokens.
Returns the total cost in USD.
[C: src/gui_2.py:App._render_mma_track_summary, src/gui_2.py:App._render_mma_usage_section, src/gui_2.py:App._render_token_budget_panel, tests/test_cost_tracker.py:test_estimate_cost]
"""
if not model:
return 0.0
@@ -60,4 +61,4 @@ def estimate_cost(model: str, input_tokens: int, output_tokens: int) -> float:
output_cost = (output_tokens / 1_000_000) * rates["output_per_mtok"]
return input_cost + output_cost
return 0.0
return 0.0
+47 -36
View File
@@ -33,16 +33,18 @@ from src.performance_monitor import get_monitor
class TrackDAG:
"""
Manages a Directed Acyclic Graph of implementation tickets.
Provides methods for dependency resolution, cycle detection, and topological sorting.
Manages a Directed Acyclic Graph of implementation tickets.
Provides methods for dependency resolution, cycle detection, and topological sorting.
"""
def __init__(self, tickets: List[Ticket]) -> None:
"""
Initializes the TrackDAG with a list of Ticket objects.
Args:
tickets: A list of Ticket instances defining the graph nodes and edges.
Initializes the TrackDAG with a list of Ticket objects.
Args:
tickets: A list of Ticket instances defining the graph nodes and edges.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.tickets = tickets
@@ -51,8 +53,9 @@ class TrackDAG:
def cascade_blocks(self) -> None:
"""
Transitively marks `todo` tickets as `blocked` if any dependency is `blocked`.
Propagates 'blocked' status from initially blocked nodes to their dependents.
Transitively marks `todo` tickets as `blocked` if any dependency is `blocked`.
Propagates 'blocked' status from initially blocked nodes to their dependents.
[C: tests/test_perf_dag.py:test_dag_performance]
"""
with get_monitor().scope("dag_cascade_blocks"):
@@ -88,9 +91,10 @@ class TrackDAG:
def get_ready_tasks(self) -> List[Ticket]:
"""
Returns a list of tickets that are in 'todo' status and whose dependencies are all 'completed'.
Returns:
A list of Ticket objects ready for execution.
Returns a list of tickets that are in 'todo' status and whose dependencies are all 'completed'.
Returns:
A list of Ticket objects ready for execution.
[C: src/models.py:Track.get_executable_tickets, tests/test_dag_engine.py:test_get_ready_tasks_branching, tests/test_dag_engine.py:test_get_ready_tasks_linear, tests/test_dag_engine.py:test_get_ready_tasks_multiple_deps, tests/test_orchestration_logic.py:test_track_executable_tickets]
"""
ready = []
@@ -102,9 +106,10 @@ class TrackDAG:
def has_cycle(self) -> bool:
"""
Performs an iterative Depth-First Search to detect cycles in the dependency graph.
Returns:
True if a cycle is detected, False otherwise.
Performs an iterative Depth-First Search to detect cycles in the dependency graph.
Returns:
True if a cycle is detected, False otherwise.
[C: src/gui_2.py:App._render_task_dag_panel, tests/test_dag_engine.py:test_has_cycle_complex_no_cycle, tests/test_dag_engine.py:test_has_cycle_direct_cycle, tests/test_dag_engine.py:test_has_cycle_indirect_cycle, tests/test_dag_engine.py:test_has_cycle_no_cycle, tests/test_perf_dag.py:test_dag_edge_cases, tests/test_perf_dag.py:test_dag_performance]
"""
with get_monitor().scope("dag_has_cycle"):
@@ -135,12 +140,13 @@ class TrackDAG:
def topological_sort(self) -> List[str]:
"""
Returns a list of ticket IDs in topological order (dependencies before dependents).
Uses Kahn's algorithm for efficient O(V+E) sorting and cycle detection.
Returns:
A list of ticket ID strings.
Raises:
ValueError: If a dependency cycle is detected.
Returns a list of ticket IDs in topological order (dependencies before dependents).
Uses Kahn's algorithm for efficient O(V+E) sorting and cycle detection.
Returns:
A list of ticket ID strings.
Raises:
ValueError: If a dependency cycle is detected.
[C: tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_complex, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_cycle, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_empty, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_linear, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_missing_dependency, tests/test_conductor_tech_lead.py:test_topological_sort_vlog, tests/test_dag_engine.py:test_topological_sort, tests/test_dag_engine.py:test_topological_sort_cycle, tests/test_orchestration_logic.py:test_topological_sort, tests/test_orchestration_logic.py:test_topological_sort_circular, tests/test_perf_dag.py:test_dag_edge_cases, tests/test_perf_dag.py:test_dag_performance]
"""
with get_monitor().scope("dag_topological_sort"):
@@ -171,17 +177,19 @@ class TrackDAG:
class ExecutionEngine:
"""
A state machine that governs the progression of tasks within a TrackDAG.
Handles automatic queueing and manual task approval.
A state machine that governs the progression of tasks within a TrackDAG.
Handles automatic queueing and manual task approval.
"""
def __init__(self, dag: TrackDAG, auto_queue: bool = False) -> None:
"""
Initializes the ExecutionEngine.
Args:
dag: The TrackDAG instance to manage.
auto_queue: If True, ready tasks will automatically move to 'in_progress'.
Initializes the ExecutionEngine.
Args:
dag: The TrackDAG instance to manage.
auto_queue: If True, ready tasks will automatically move to 'in_progress'.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.dag = dag
@@ -190,10 +198,11 @@ class ExecutionEngine:
def tick(self) -> List[Ticket]:
"""
Evaluates the DAG and returns a list of tasks that are currently 'ready' for execution.
If auto_queue is enabled, tasks without 'step_mode' will be marked as 'in_progress'.
Returns:
A list of ready Ticket objects.
Evaluates the DAG and returns a list of tasks that are currently 'ready' for execution.
If auto_queue is enabled, tasks without 'step_mode' will be marked as 'in_progress'.
Returns:
A list of ready Ticket objects.
[C: src/multi_agent_conductor.py:ConductorEngine.run, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_cascade_blocks_multi_hop, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_cascade_blocks_simple, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_execution_engine_tick_cascades_blocks, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_in_progress_not_blocked, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_manual_unblock_restores_todo, tests/test_execution_engine.py:test_execution_engine_auto_queue, tests/test_execution_engine.py:test_execution_engine_basic_flow, tests/test_execution_engine.py:test_execution_engine_step_mode]
"""
with get_monitor().scope("dag_tick"):
@@ -204,9 +213,10 @@ class ExecutionEngine:
def approve_task(self, task_id: str) -> None:
"""
Manually transitions a task from 'todo' to 'in_progress' if its dependencies are met.
Args:
task_id: The ID of the task to approve.
Manually transitions a task from 'todo' to 'in_progress' if its dependencies are met.
Args:
task_id: The ID of the task to approve.
[C: src/multi_agent_conductor.py:ConductorEngine.approve_task, tests/test_execution_engine.py:test_execution_engine_approve_task, tests/test_execution_engine.py:test_execution_engine_step_mode]
"""
ticket = self.dag.ticket_map.get(task_id)
@@ -216,10 +226,11 @@ class ExecutionEngine:
def update_task_status(self, task_id: str, status: str) -> None:
"""
Force-updates the status of a specific task.
Args:
task_id: The ID of the task.
status: The new status string (e.g., 'todo', 'in_progress', 'completed', 'blocked').
Force-updates the status of a specific task.
Args:
task_id: The ID of the task.
status: The new status string (e.g., 'todo', 'in_progress', 'completed', 'blocked').
[C: src/multi_agent_conductor.py:ConductorEngine.update_task_status, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_manual_unblock_restores_todo, tests/test_execution_engine.py:test_execution_engine_auto_queue, tests/test_execution_engine.py:test_execution_engine_basic_flow, tests/test_execution_engine.py:test_execution_engine_status_persistence, tests/test_execution_engine.py:test_execution_engine_update_nonexistent_task]
"""
ticket = self.dag.ticket_map.get(task_id)
+5 -5
View File
@@ -21,7 +21,7 @@ class DiffFile:
def parse_hunk_header(line: str) -> Optional[tuple[int, int, int, int]]:
"""
[C: tests/test_diff_viewer.py:test_parse_hunk_header]
[C: tests/test_diff_viewer.py:test_parse_hunk_header]
"""
if not line.startswith("@@"):
return None
@@ -45,7 +45,7 @@ def parse_hunk_header(line: str) -> Optional[tuple[int, int, int, int]]:
def parse_diff(diff_text: str) -> List[DiffFile]:
"""
[C: src/gui_2.py:App.request_patch_from_tier4, tests/test_diff_viewer.py:test_diff_line_classification, tests/test_diff_viewer.py:test_parse_diff_empty, tests/test_diff_viewer.py:test_parse_diff_none, tests/test_diff_viewer.py:test_parse_diff_with_context, tests/test_diff_viewer.py:test_parse_multiple_files, tests/test_diff_viewer.py:test_parse_simple_diff, tests/test_diff_viewer.py:test_render_diff_text_immediate]
[C: src/gui_2.py:App.request_patch_from_tier4, tests/test_diff_viewer.py:test_diff_line_classification, tests/test_diff_viewer.py:test_parse_diff_empty, tests/test_diff_viewer.py:test_parse_diff_none, tests/test_diff_viewer.py:test_parse_diff_with_context, tests/test_diff_viewer.py:test_parse_multiple_files, tests/test_diff_viewer.py:test_parse_simple_diff]
"""
if not diff_text or not diff_text.strip():
return []
@@ -113,7 +113,7 @@ def parse_diff(diff_text: str) -> List[DiffFile]:
def get_line_color(line: str) -> Optional[str]:
"""
[C: tests/test_diff_viewer.py:test_get_line_color]
[C: tests/test_diff_viewer.py:test_get_line_color]
"""
if line.startswith("+"):
return "green"
@@ -125,7 +125,7 @@ def get_line_color(line: str) -> Optional[str]:
def apply_patch_to_file(patch_text: str, base_dir: str = ".") -> Tuple[bool, str]:
"""
[C: src/gui_2.py:App._apply_pending_patch, tests/test_diff_viewer.py:test_apply_patch_simple, tests/test_diff_viewer.py:test_apply_patch_with_context]
[C: src/gui_2.py:App._apply_pending_patch, tests/test_diff_viewer.py:test_apply_patch_simple, tests/test_diff_viewer.py:test_apply_patch_with_context]
"""
import difflib
@@ -171,4 +171,4 @@ def apply_patch_to_file(patch_text: str, base_dir: str = ".") -> Tuple[bool, str
except Exception as e:
return False, f"Error patching {file_path}: {e}"
return True, "\n".join(results)
return True, "\n".join(results)
+44 -32
View File
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -13,13 +13,13 @@ from src.models import ExternalEditorConfig, TextEditorConfig
class ExternalEditorLauncher:
def __init__(self, config: ExternalEditorConfig):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.config = config
def get_editor(self, editor_name: Optional[str] = None) -> Optional[TextEditorConfig]:
"""
[C: tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_by_name, tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_returns_default, tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_unknown_name]
[C: tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_by_name, tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_returns_default, tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_unknown_name]
"""
if editor_name:
return self.config.editors.get(editor_name)
@@ -29,7 +29,7 @@ class ExternalEditorLauncher:
self, editor: TextEditorConfig, original_path: str, modified_path: str
) -> List[str]:
"""
[C: tests/test_external_editor.py:TestExternalEditorLauncher.test_build_diff_command, tests/test_external_editor_gui.py:test_verify_command_format, tests/test_external_editor_gui.py:test_verify_vscode_command_format]
[C: tests/test_external_editor.py:TestExternalEditorLauncher.test_build_diff_command, tests/test_external_editor_gui.py:test_verify_command_format, tests/test_external_editor_gui.py:test_verify_vscode_command_format]
"""
cmd = [editor.path] + editor.diff_args + [original_path, modified_path]
return cmd
@@ -38,7 +38,7 @@ class ExternalEditorLauncher:
self, editor_name: Optional[str], original_path: str, modified_path: str
) -> Optional[subprocess.Popen]:
"""
[C: src/gui_2.py:App._open_patch_in_external_editor, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_file_not_found, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_missing_editor, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_success]
[C: src/gui_2.py:App._open_patch_in_external_editor, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_file_not_found, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_missing_editor, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_success]
"""
editor = self.get_editor(editor_name)
if not editor:
@@ -117,7 +117,7 @@ def auto_detect_vscode() -> Optional[TextEditorConfig]:
def get_default_launcher() -> ExternalEditorLauncher:
"""
[C: src/gui_2.py:App._open_patch_in_external_editor, src/gui_2.py:App._render_external_editor_panel]
[C: src/gui_2.py:App._open_patch_in_external_editor, src/gui_2.py:App._render_external_editor_panel]
"""
from src import models
config = models.load_config()
@@ -142,7 +142,7 @@ def get_default_launcher() -> ExternalEditorLauncher:
def create_temp_modified_file(content: str) -> str:
"""
[C: src/gui_2.py:App._open_patch_in_external_editor, tests/test_external_editor.py:TestHelperFunctions.test_create_temp_modified_file]
[C: src/gui_2.py:App._open_patch_in_external_editor, tests/test_external_editor.py:TestHelperFunctions.test_create_temp_modified_file]
"""
with tempfile.NamedTemporaryFile(mode="w", suffix="_modified", delete=False, encoding="utf-8") as f:
f.write(content)
+37 -20
View File
@@ -47,13 +47,14 @@ _ast_cache: Dict[str, Tuple[float, tree_sitter.Tree]] = {}
class ASTParser:
"""
Parser for extracting AST-based views of source code.
Currently supports Python.
Parser for extracting AST-based views of source code.
Currently supports Python.
"""
#region: Core Operations
def __init__(self, language: str) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
if language not in ("python", "cpp", "c"):
raise ValueError(f"Language '{language}' not supported yet.")
@@ -69,7 +70,8 @@ class ASTParser:
def parse(self, code: str) -> tree_sitter.Tree:
"""
Parse the given code and return the tree-sitter Tree.
Parse the given code and return the tree-sitter Tree.
[C: src/mcp_client.py:_search_file, src/mcp_client.py:derive_code_path, src/mcp_client.py:py_check_syntax, src/mcp_client.py:py_get_class_summary, src/mcp_client.py:py_get_definition, src/mcp_client.py:py_get_docstring, src/mcp_client.py:py_get_imports, src/mcp_client.py:py_get_signature, src/mcp_client.py:py_get_symbol_info, src/mcp_client.py:py_get_var_declaration, src/mcp_client.py:py_set_signature, src/mcp_client.py:py_set_var_declaration, src/mcp_client.py:py_update_definition, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/rag_engine.py:RAGEngine._chunk_code, src/summarize.py:_summarise_python, tests/test_ast_parser.py:test_ast_parser_parse, tests/test_tree_sitter_setup.py:test_tree_sitter_python_setup]
"""
return self.parser.parse(bytes(code, "utf8"))
@@ -185,7 +187,8 @@ class ASTParser:
def get_skeleton(self, code: str, path: Optional[str] = None) -> str:
"""
Returns a skeleton of a Python file (preserving docstrings, stripping function bodies).
Returns a skeleton of a Python file (preserving docstrings, stripping function bodies).
[C: src/mcp_client.py:py_get_skeleton, src/mcp_client.py:ts_c_get_skeleton, src/mcp_client.py:ts_cpp_get_skeleton, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ast_parser.py:test_ast_parser_get_skeleton_c, tests/test_ast_parser.py:test_ast_parser_get_skeleton_cpp, tests/test_ast_parser.py:test_ast_parser_get_skeleton_python, tests/test_context_pruner.py:test_ast_caching, tests/test_context_pruner.py:test_performance_large_file]
"""
code_bytes = code.encode("utf8")
@@ -200,7 +203,7 @@ class ASTParser:
def walk(node: tree_sitter.Node) -> None:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if node.type == "function_definition":
body = node.child_by_field_name("body")
@@ -238,9 +241,10 @@ class ASTParser:
def get_curated_view(self, code: str, path: Optional[str] = None) -> str:
"""
Returns a curated skeleton of a Python file.
Preserves function bodies if they have @core_logic decorator or # [HOT] comment.
Otherwise strips bodies but preserves docstrings.
Returns a curated skeleton of a Python file.
Preserves function bodies if they have @core_logic decorator or # [HOT] comment.
Otherwise strips bodies but preserves docstrings.
[C: src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ast_parser.py:test_ast_parser_get_curated_view]
"""
code_bytes = code.encode("utf8")
@@ -279,7 +283,7 @@ class ASTParser:
def walk(node: tree_sitter.Node) -> None:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if node.type == "function_definition":
body = node.child_by_field_name("body")
@@ -317,8 +321,9 @@ class ASTParser:
def get_targeted_view(self, code: str, function_names: List[str], path: Optional[str] = None) -> str:
"""
Returns a targeted view of the code including only the specified functions
and their dependencies up to depth 2.
Returns a targeted view of the code including only the specified functions
and their dependencies up to depth 2.
[C: src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ast_parser.py:test_ast_parser_get_targeted_view, tests/test_context_pruner.py:test_class_targeted_extraction, tests/test_context_pruner.py:test_targeted_extraction]
"""
code_bytes = code.encode("utf8")
@@ -482,8 +487,9 @@ class ASTParser:
#region: Symbol Extraction
def get_definition(self, code: str, name: str, path: Optional[str] = None) -> str:
"""
Returns the full source code for a specific definition by name.
Supports 'ClassName::method' or 'method' for C++.
Returns the full source code for a specific definition by name.
Supports 'ClassName::method' or 'method' for C++.
[C: src/mcp_client.py:trace, src/mcp_client.py:ts_c_get_definition, src/mcp_client.py:ts_cpp_get_definition, tests/test_ast_parser.py:test_ast_parser_get_definition_c, tests/test_ast_parser.py:test_ast_parser_get_definition_cpp, tests/test_ast_parser.py:test_ast_parser_get_definition_cpp_template]
"""
code_bytes = code.encode("utf8")
@@ -492,6 +498,9 @@ class ASTParser:
parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if not target_parts:
return None
target = target_parts[0]
@@ -578,8 +587,9 @@ class ASTParser:
def get_signature(self, code: str, name: str, path: Optional[str] = None) -> str:
"""
Returns only the signature part of a function or method.
For C/C++, this is the code from the start of the definition until the block start '{'.
Returns only the signature part of a function or method.
For C/C++, this is the code from the start of the definition until the block start '{'.
[C: src/mcp_client.py:ts_c_get_signature, src/mcp_client.py:ts_cpp_get_signature, tests/test_ast_parser.py:test_ast_parser_get_signature_c, tests/test_ast_parser.py:test_ast_parser_get_signature_cpp]
"""
code_bytes = code.encode("utf8")
@@ -588,6 +598,9 @@ class ASTParser:
parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if not target_parts:
return None
target = target_parts[0]
@@ -687,7 +700,8 @@ class ASTParser:
def get_code_outline(self, code: str, path: Optional[str] = None) -> str:
"""
Returns a hierarchical outline of the code (classes, structs, functions, methods).
Returns a hierarchical outline of the code (classes, structs, functions, methods).
[C: src/mcp_client.py:ts_c_get_code_outline, src/mcp_client.py:ts_cpp_get_code_outline, tests/test_ast_parser.py:test_ast_parser_get_code_outline_c, tests/test_ast_parser.py:test_ast_parser_get_code_outline_cpp]
"""
code_bytes = code.encode("utf8")
@@ -696,7 +710,7 @@ class ASTParser:
def walk(node: tree_sitter.Node, indent: int = 0) -> None:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
ntype = node.type
label = ""
@@ -728,7 +742,8 @@ class ASTParser:
def update_definition(self, code: str, name: str, new_content: str, path: Optional[str] = None) -> str:
"""
Surgically replace the definition of a class or function by name.
Surgically replace the definition of a class or function by name.
[C: src/mcp_client.py:ts_c_update_definition, src/mcp_client.py:ts_cpp_update_definition, tests/test_ast_parser.py:test_ast_parser_update_definition_cpp]
"""
code_bytes = code.encode("utf8")
@@ -737,6 +752,9 @@ class ASTParser:
parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if not target_parts:
return None
target = target_parts[0]
@@ -830,4 +848,3 @@ def reset_client() -> None:
def get_file_id(path: Path) -> Optional[str]:
return None
#endregion: Module Level Utilities
+8 -2
View File
@@ -16,7 +16,10 @@ class FuzzyAnchor:
@classmethod
def create_slice(cls, text: str, start_line: int, end_line: int) -> dict:
"""start_line and end_line are 1-based."""
"""
start_line and end_line are 1-based.
[C: src/gui_2.py:App._populate_auto_slices, src/gui_2.py:App._render_text_viewer_window, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_create_slice_basic, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_anchor_mismatch_returns_none, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_exact_match, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_line_deleted_before_returns_none, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_line_inserted_before, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_multiple_lines_changed, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations]
"""
lines = text.splitlines()
s_idx = max(0, start_line - 1)
e_idx = min(len(lines), end_line)
@@ -33,6 +36,9 @@ class FuzzyAnchor:
@classmethod
def resolve_slice(cls, text: str, slice_data: dict) -> Optional[Tuple[int, int]]:
"""
[C: tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_anchor_mismatch_returns_none, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_exact_match, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_line_deleted_before_returns_none, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_line_inserted_before, tests/test_fuzzy_anchor.py:TestFuzzyAnchor.test_resolve_slice_multiple_lines_changed]
"""
lines = text.splitlines()
# 1. Try exact match
s_idx = slice_data["start_line"] - 1
@@ -79,4 +85,4 @@ class FuzzyAnchor:
if best_e != -1:
return (best_s + 1, best_e)
return None
return None
+12 -8
View File
@@ -45,11 +45,13 @@ from typing import Optional, Callable, Any
class GeminiCliAdapter:
"""
Adapter for the Gemini CLI that parses streaming JSON output.
Adapter for the Gemini CLI that parses streaming JSON output.
"""
def __init__(self, binary_path: str = "gemini"):
"""
Initializes the adapter with the path to the gemini CLI executable.
Initializes the adapter with the path to the gemini CLI executable.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.binary_path = binary_path
@@ -61,9 +63,10 @@ class GeminiCliAdapter:
model: str | None = None, stream_callback: Optional[Callable[[str], None]] = None) -> dict[str, Any]:
"""
Sends a message to the Gemini CLI and processes the streaming JSON output.
Uses non-blocking line-by-line reading to allow stream_callback.
[C: simulation/user_agent.py:UserSimAgent.generate_response, src/multi_agent_conductor.py:run_worker_lifecycle, src/native_orchestrator.py:NativeOrchestrator.execute_ticket, src/orchestrator_pm.py:generate_tracks, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_full_flow_integration, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_handles_tool_use_events, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_parses_jsonl_output, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_starts_subprocess_with_correct_args, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_parses_tool_calls_from_streaming_json, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_starts_subprocess_with_model, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_context_bleed_prevention, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_get_history_bleed_stats, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_token_usage.py:test_token_usage_tracking, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
Sends a message to the Gemini CLI and processes the streaming JSON output.
Uses non-blocking line-by-line reading to allow stream_callback.
[C: simulation/user_agent.py:UserSimAgent.generate_response, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_full_flow_integration, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_handles_tool_use_events, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_parses_jsonl_output, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_starts_subprocess_with_correct_args, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_parses_tool_calls_from_streaming_json, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_starts_subprocess_with_model, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_context_bleed_prevention, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_token_usage.py:test_token_usage_tracking, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
start_time = time.time()
command_parts = [self.binary_path]
@@ -192,9 +195,10 @@ class GeminiCliAdapter:
def count_tokens(self, contents: list[str]) -> int:
"""
Provides a character-based token estimation for the Gemini CLI.
Uses 4 chars/token as a conservative average.
Provides a character-based token estimation for the Gemini CLI.
Uses 4 chars/token as a conservative average.
[C: tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_count_tokens_fallback]
"""
total_chars = len("\n".join(contents))
return total_chars // 4
return total_chars // 4
+67 -22
View File
@@ -101,7 +101,7 @@ class App:
def __init__(self) -> None:
# Initialize controller and delegate state
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
# --- Core Dependencies & State ---
self.controller = app_controller.AppController()
@@ -265,8 +265,9 @@ class App:
def run(self) -> None:
"""
Initializes the ImGui runner and starts the main application loop.
[C: simulation/sim_base.py:run_sim, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/project_manager.py:get_git_log, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run]
Initializes the ImGui runner and starts the main application loop.
[C: simulation/sim_base.py:run_sim, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run]
"""
if "--headless" in sys.argv:
print("Headless mode active")
@@ -544,7 +545,8 @@ class App:
def shutdown(self) -> None:
"""
Cleanly shuts down the app's background tasks and saves state.
Cleanly shuts down the app's background tasks and saves state.
[C: tests/conftest.py:app_instance, tests/conftest.py:mock_app]
"""
try:
@@ -556,7 +558,7 @@ class App:
def save_context_preset(self, name: str) -> None:
"""
[C: tests/test_context_presets.py:test_save_context_preset]
[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()
@@ -577,7 +579,7 @@ class App:
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]
[C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset]
"""
presets = self.controller.project.get('context_presets', {})
if name in presets:
@@ -588,7 +590,7 @@ class App:
def delete_context_preset(self, name: str) -> None:
"""
[C: tests/test_context_presets.py:test_delete_context_preset, tests/test_context_presets.py:test_delete_nonexistent_preset_no_error]
[C: tests/test_context_presets.py:test_delete_context_preset, tests/test_context_presets.py:test_delete_nonexistent_preset_no_error]
"""
if 'context_presets' in self.controller.project:
self.controller.project['context_presets'].pop(name, None)
@@ -890,7 +892,7 @@ class App:
def _show_menus(self) -> None:
"""
[C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close]
[C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close]
"""
with imscope.menu("manual slop") as (active):
if active and imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
@@ -1026,7 +1028,8 @@ class App:
def _handle_history_logic(self) -> None:
"""
Logic for capturing UI state for undo/redo.
Logic for capturing UI state for undo/redo.
"""
if self._is_applying_snapshot:
return
@@ -1209,7 +1212,7 @@ class App:
def _render_shader_live_editor(self) -> None:
"""
[C: tests/test_shader_live_editor.py:test_shader_live_editor_renders]
[C: tests/test_shader_live_editor.py:test_shader_live_editor_renders]
"""
if self.show_windows.get('Shader Editor', False):
with imscope.window('Shader Editor', self.show_windows['Shader Editor']) as (exp, opened):
@@ -1524,7 +1527,7 @@ class App:
def _render_log_management(self) -> None:
"""
[C: tests/test_log_management_ui.py:test_render_log_management_logic]
[C: tests/test_log_management_ui.py:test_render_log_management_logic]
"""
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_log_management")
with imscope.window("Log Management", self.show_windows["Log Management"]) as (exp, opened):
@@ -1791,7 +1794,7 @@ class App:
def _save_paths(self) -> None:
"""
[C: tests/test_gui_paths.py:test_save_paths]
[C: tests/test_gui_paths.py:test_save_paths]
"""
self.config["paths"] = {
"logs_dir": self.ui_logs_dir,
@@ -2567,6 +2570,9 @@ class App:
#region: Context Management
def _render_files_and_media(self) -> None:
"""
[C: tests/test_gui_fast_render.py:test_render_files_and_media_fast]
"""
avail = imgui.get_content_region_avail().y
if not hasattr(self, 'files_screenshots_split'): self.files_screenshots_split = 0.65
split_y = int(avail * self.files_screenshots_split)
@@ -2787,6 +2793,9 @@ class App:
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_screenshots_panel")
def _render_context_composition_panel(self) -> None:
"""
[C: tests/test_auto_slices.py:test_add_all_triggers_auto_slices, tests/test_gui_fast_render.py:test_render_context_composition_panel_fast]
"""
if imgui.collapsing_header("Context Composition##panel"):
total_lines, total_ast = self._update_context_file_stats()
self._render_context_batch_actions(total_lines, total_ast)
@@ -2799,6 +2808,9 @@ class App:
self._render_context_presets()
def _render_ast_inspector_modal(self) -> None:
"""
[C: tests/test_ast_inspector_extended.py:test_ast_inspector_line_range_parsing]
"""
if self._show_ast_inspector:
imgui.open_popup('AST Inspector')
self._show_ast_inspector = False
@@ -2933,6 +2945,9 @@ class App:
if not opened: self.ui_inspecting_ast_file = None
def _render_add_context_files_modal(self) -> None:
"""
[C: tests/test_auto_slices.py:test_add_selected_triggers_auto_slices]
"""
if imgui.begin_popup_modal("Select Context Files", None, imgui.WindowFlags_.always_auto_resize)[0]:
imgui.text("Select files from project to add to context:")
if imgui.begin_child("ctx_picker_list", imgui.ImVec2(600, 300), True):
@@ -3018,6 +3033,9 @@ class App:
if imgui.button(f"Delete##{name}"): self.delete_context_preset(name)
def _populate_auto_slices(self, f_item: models.FileItem) -> None:
"""
[C: tests/test_auto_slices.py:test_populate_auto_slices_basic]
"""
from src import mcp_client
import re
mcp_client.configure([{"path": f_item.path}])
@@ -3415,7 +3433,7 @@ class App:
def _render_discussion_panel(self) -> None:
"""
[C: tests/test_discussion_takes_gui.py:test_render_discussion_tabs, tests/test_discussion_takes_gui.py:test_switching_discussion_via_tabs, tests/test_gui_discussion_tabs.py:test_discussion_tabs_rendered, tests/test_gui_phase4.py:test_track_discussion_toggle, tests/test_gui_symbol_navigation.py:test_render_discussion_panel_symbol_lookup]
[C: tests/test_discussion_takes_gui.py:test_render_discussion_tabs, tests/test_discussion_takes_gui.py:test_switching_discussion_via_tabs, tests/test_gui_discussion_tabs.py:test_discussion_tabs_rendered, tests/test_gui_fast_render.py:test_render_discussion_panel_fast, tests/test_gui_phase4.py:test_track_discussion_toggle, tests/test_gui_symbol_navigation.py:test_render_discussion_panel_symbol_lookup]
"""
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel")
self._render_thinking_indicator()
@@ -3654,7 +3672,8 @@ class App:
def _render_synthesis_panel(self) -> None:
"""
Renders a panel for synthesizing multiple discussion takes.
Renders a panel for synthesizing multiple discussion takes.
[C: tests/test_gui_synthesis.py:test_render_synthesis_panel]
"""
imgui.text("Select takes to synthesize:")
@@ -4358,7 +4377,7 @@ def hello():
def _reorder_ticket(self, src_idx: int, dst_idx: int) -> None:
"""
[C: tests/test_ticket_queue.py:TestReorder.test_reorder_ticket_invalid, tests/test_ticket_queue.py:TestReorder.test_reorder_ticket_valid]
[C: tests/test_ticket_queue.py:TestReorder.test_reorder_ticket_invalid, tests/test_ticket_queue.py:TestReorder.test_reorder_ticket_valid]
"""
if src_idx == dst_idx: return
new_tickets = list(self.active_tickets)
@@ -4380,7 +4399,7 @@ def hello():
def _render_ticket_queue(self) -> None:
"""
[C: tests/test_gui_kill_button.py:test_render_ticket_queue_table_columns]
[C: tests/test_gui_kill_button.py:test_render_ticket_queue_table_columns]
"""
imgui.text("Ticket Queue Management")
if not self.active_track:
@@ -4678,7 +4697,10 @@ def hello():
imgui.text_colored(imgui.ImVec4(1, 0, 0, 1), f"Error loading beads: {e}")
def _render_mma_dashboard(self) -> None:
"""Main MMA dashboard interface."""
"""
Main MMA dashboard interface.
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_ask_dialog_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_mma_approval_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_spawn_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_no_approval_badge_when_idle]
"""
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard")
self._render_mma_focus_selector()
imgui.separator()
@@ -4708,6 +4730,9 @@ def hello():
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard")
def _render_mma_focus_selector(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
imgui.text("Focus Agent:"); imgui.same_line()
focus_label = self.ui_focus_agent or "All"
if imgui.begin_combo("##focus_agent", focus_label, imgui.ComboFlags_.width_fit_preview):
@@ -4795,6 +4820,9 @@ def hello():
imgui.end_popup()
def _render_mma_track_summary(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
is_nerv = theme.is_nerv_active()
track_name = self.active_track.description if self.active_track else "None"
if getattr(self, "ui_project_execution_mode", "native") == "beads": track_name = "Beads Graph"
@@ -4823,15 +4851,24 @@ def hello():
imgui.text_colored(C_LBL, "ETA:"); imgui.same_line(); imgui.text_colored(C_VAL, f"~{int(eta_mins)}m ({remaining} tickets remaining)")
def _render_mma_epic_planner(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
imgui.text_colored(C_LBL, 'Epic Planning (Tier 1)')
_, self.ui_epic_input = imgui.input_text_multiline('##epic_input', self.ui_epic_input, imgui.ImVec2(-1, 80))
if imgui.button('Plan Epic (Tier 1)', imgui.ImVec2(-1, 0)): self._cb_plan_epic()
def _render_mma_conductor_setup(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
if imgui.button("Run Setup Scan"): self._cb_run_conductor_setup()
if self.ui_conductor_setup_summary: imgui.input_text_multiline("##setup_summary", self.ui_conductor_setup_summary, imgui.ImVec2(-1, 120), imgui.InputTextFlags_.read_only)
def _render_mma_track_browser(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
imgui.text("Track Browser")
if imgui.begin_table("mma_tracks_table", 4, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable):
imgui.table_setup_column("Title"); imgui.table_setup_column("Status"); imgui.table_setup_column("Progress"); imgui.table_setup_column("Actions"); imgui.table_headers_row()
@@ -4858,6 +4895,9 @@ def hello():
self.ui_new_track_name = ""; self.ui_new_track_desc = ""
def _render_mma_global_controls(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
changed, self.mma_step_mode = imgui.checkbox("Step Mode (HITL)", self.mma_step_mode)
imgui.same_line(); imgui.text(f"Status: {self.mma_status.upper()}")
if self.controller and hasattr(self.controller, 'engine') and self.controller.engine and hasattr(self.controller.engine, '_pause_event'):
@@ -4876,6 +4916,9 @@ def hello():
if imgui.button("Go to Approval"): pass
def _render_mma_usage_section(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
imgui.text("Tier Usage (Tokens & Cost)")
if imgui.begin_table("mma_usage", 5, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg):
imgui.table_setup_column("Tier"); imgui.table_setup_column("Model"); imgui.table_setup_column("Input"); imgui.table_setup_column("Output"); imgui.table_setup_column("Est. Cost"); imgui.table_headers_row()
@@ -4939,6 +4982,9 @@ def hello():
if imgui.button(f"Delete##{self.ui_selected_ticket_id}"): self.active_tickets = [t for t in self.active_tickets if str(t.get('id', '')) != self.ui_selected_ticket_id]; self.ui_selected_ticket_id = None; self._push_mma_state_update()
def _render_mma_agent_streams(self) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
imgui.text("Agent Streams")
if imgui.begin_tab_bar("mma_streams_tabs"):
for tier, label, sep_flag_attr in [("Tier 1", "Tier 1", "ui_separate_tier1"), ("Tier 2", "Tier 2 (Tech Lead)", "ui_separate_tier2"), ("Tier 3", None, "ui_separate_tier3"), ("Tier 4", "Tier 4 (QA)", "ui_separate_tier4")]:
@@ -4957,7 +5003,7 @@ def hello():
def _render_tier_stream_panel(self, tier_key: str, stream_key: str | None) -> None:
"""
[C: tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier1_renders_stream_content, tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier3_renders_worker_subheaders]
[C: tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier1_renders_stream_content, tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier3_renders_worker_subheaders]
"""
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_tier_stream_panel")
if self.is_viewing_prior_session:
@@ -5066,7 +5112,7 @@ def hello():
def bulk_execute(self) -> None:
"""
[C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_execute]
[C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_execute]
"""
for tid in self.ui_selected_tickets:
t = next((t for t in self.active_tickets if str(t.get('id', '')) == tid), None)
@@ -5075,7 +5121,7 @@ def hello():
def bulk_skip(self) -> None:
"""
[C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_skip]
[C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_skip]
"""
for tid in self.ui_selected_tickets:
t = next((t for t in self.active_tickets if str(t.get('id', '')) == tid), None)
@@ -5084,7 +5130,7 @@ def hello():
def bulk_block(self) -> None:
"""
[C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_block]
[C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_block]
"""
for tid in self.ui_selected_tickets:
t = next((t for t in self.active_tickets if str(t.get('id', '')) == tid), None)
@@ -5145,4 +5191,3 @@ def main() -> None:
if __name__ == "__main__":
main()
+22 -17
View File
@@ -21,7 +21,7 @@ class UISnapshot:
def to_dict(self) -> dict:
"""
[C: src/models.py:ExternalEditorConfig.to_dict, src/models.py:MCPConfiguration.to_dict, src/models.py:RAGConfig.to_dict, src/models.py:ToolPreset.to_dict, src/models.py:Track.to_dict, src/models.py:TrackState.to_dict, src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
[C: src/models.py:ContextPreset.to_dict, src/models.py:ExternalEditorConfig.to_dict, src/models.py:MCPConfiguration.to_dict, src/models.py:RAGConfig.to_dict, src/models.py:ToolPreset.to_dict, src/models.py:Track.to_dict, src/models.py:TrackState.to_dict, src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_serialization_with_annotations, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
"""
return {
"ai_input": self.ai_input,
@@ -42,7 +42,7 @@ class UISnapshot:
@classmethod
def from_dict(cls, data: dict) -> "UISnapshot":
"""
[C: src/models.py:ExternalEditorConfig.from_dict, src/models.py:MCPConfiguration.from_dict, src/models.py:RAGConfig.from_dict, src/models.py:ToolPreset.from_dict, src/models.py:Track.from_dict, src/models.py:TrackState.from_dict, src/models.py:load_mcp_config, src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
[C: src/models.py:ContextPreset.from_dict, src/models.py:ExternalEditorConfig.from_dict, src/models.py:MCPConfiguration.from_dict, src/models.py:RAGConfig.from_dict, src/models.py:ToolPreset.from_dict, src/models.py:Track.from_dict, src/models.py:TrackState.from_dict, src/models.py:load_mcp_config, src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_context_presets_models.py:test_context_preset_from_dict_legacy, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_deserialization_with_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
"""
return cls(
ai_input=data.get("ai_input", ""),
@@ -69,7 +69,7 @@ class HistoryEntry:
class HistoryManager:
def __init__(self, max_capacity: int = 100):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.max_capacity = max_capacity
self._undo_stack: typing.List[HistoryEntry] = []
@@ -78,9 +78,10 @@ class HistoryManager:
def push(self, state: typing.Any, description: str) -> None:
"""
Pushes a new state to the undo stack and clears the redo stack.
If the undo stack exceeds max_capacity, the oldest state is removed.
[C: tests/test_history.py:test_jump_to_undo, tests/test_history.py:test_max_capacity, tests/test_history.py:test_push_state, tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo]
Pushes a new state to the undo stack and clears the redo stack.
If the undo stack exceeds max_capacity, the oldest state is removed.
[C: tests/test_history.py:test_jump_to_undo, tests/test_history.py:test_max_capacity, tests/test_history.py:test_push_state, tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo, tests/test_history_manager.py:TestHistoryManager.test_get_history_returns_descriptions, tests/test_history_manager.py:TestHistoryManager.test_jump_to_undo, tests/test_history_manager.py:TestHistoryManager.test_push_and_undo, tests/test_history_manager.py:TestHistoryManager.test_push_clears_redo_stack, tests/test_history_manager.py:TestHistoryManager.test_undo_and_redo]
"""
entry = HistoryEntry(state=state, description=description)
self._undo_stack.append(entry)
@@ -91,9 +92,10 @@ class HistoryManager:
def undo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]:
"""
Undoes the last action by moving the current_state to the redo stack
and returning the top of the undo stack.
[C: tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo]
Undoes the last action by moving the current_state to the redo stack
and returning the top of the undo stack.
[C: tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo, tests/test_history_manager.py:TestHistoryManager.test_push_and_undo, tests/test_history_manager.py:TestHistoryManager.test_push_clears_redo_stack, tests/test_history_manager.py:TestHistoryManager.test_undo_and_redo, tests/test_history_manager.py:TestHistoryManager.test_undo_no_history_returns_none]
"""
if not self._undo_stack:
return None
@@ -105,9 +107,10 @@ class HistoryManager:
def redo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]:
"""
Redoes the last undone action by moving the current_state to the undo stack
and returning the top of the redo stack.
[C: tests/test_history.py:test_undo_redo]
Redoes the last undone action by moving the current_state to the undo stack
and returning the top of the redo stack.
[C: tests/test_history.py:test_undo_redo, tests/test_history_manager.py:TestHistoryManager.test_redo_no_history_returns_none, tests/test_history_manager.py:TestHistoryManager.test_undo_and_redo]
"""
if not self._redo_stack:
return None
@@ -126,8 +129,9 @@ class HistoryManager:
def get_history(self) -> typing.List[typing.Dict[str, typing.Any]]:
"""
Returns a list of descriptions and timestamps for the undo stack.
[C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state]
Returns a list of descriptions and timestamps for the undo stack.
[C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state, tests/test_history_manager.py:TestHistoryManager.test_get_history_returns_descriptions]
"""
return [
{"description": e.description, "timestamp": e.timestamp}
@@ -137,9 +141,10 @@ class HistoryManager:
def jump_to_undo(self, index: int, current_state: typing.Any, current_description: str = "Before Jump") -> typing.Optional[HistoryEntry]:
"""
Jumps to a specific state in the undo stack by moving subsequent states
and the current_state to the redo stack.
[C: tests/test_history.py:test_jump_to_undo]
Jumps to a specific state in the undo stack by moving subsequent states
and the current_state to the redo stack.
[C: tests/test_history.py:test_jump_to_undo, tests/test_history_manager.py:TestHistoryManager.test_jump_to_undo]
"""
if index < 0 or index >= len(self._undo_stack):
return None
+46 -1
View File
@@ -5,6 +5,9 @@ from imgui_bundle import imgui_node_editor
def child(id_str: str, size_x: float = 0, size_y: float = 0, flags: int = 0): return _ScopeChild(id_str, size_x, size_y, flags)
class _ScopeChild:
def __init__(self, id_str: str, size_x: float | imgui.ImVec2, size_y: float, flags: int):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._id = id_str
# Check if size_x is likely an ImVec2 without using isinstance (which breaks with mocks)
if hasattr(size_x, 'x') and hasattr(size_x, 'y'):
@@ -30,6 +33,9 @@ class _ScopeGroup:
def id(str_id: str): return _ScopeId(str_id)
class _ScopeId:
def __init__(self, str_id: str):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._id = str_id
def __enter__(self):
imgui.push_id(self._id)
@@ -40,6 +46,9 @@ class _ScopeId:
def menu(label: str): return _ScopeMenu(label)
class _ScopeMenu:
def __init__(self, label: str):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._label = label
self._active = False
def __enter__(self):
@@ -53,6 +62,9 @@ class _ScopeMenu:
def menu_bar(): return _ScopeMenuBar()
class _ScopeMenuBar:
def __init__(self):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._active = False
def __enter__(self):
self._active = imgui.begin_menu_bar()
@@ -65,6 +77,9 @@ class _ScopeMenuBar:
def node(name: str): return _ScopeNode(name)
class _ScopeNode:
def __init__(self, name: str):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._name = name
def __enter__(self):
return imgui_node_editor.begin(self._name)
@@ -75,6 +90,9 @@ class _ScopeNode:
def popup(id_str: str): return _ScopePopup(id_str)
class _ScopePopup:
def __init__(self, id_str: str):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._id = id_str
self._active = False
def __enter__(self):
@@ -88,6 +106,9 @@ class _ScopePopup:
def popup_modal(name: str, visible: bool = True, flags: int = 0): return _ScopePopupModal(name, visible, flags)
class _ScopePopupModal:
def __init__(self, name: str, visible: bool, flags: int):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._name = name
self._visible = visible
self._flags = flags
@@ -104,6 +125,9 @@ class _ScopePopupModal:
def style_color(col: int, val: Any): return _ScopeStyleColor(col, val)
class _ScopeStyleColor:
def __init__(self, col: int, val: Any):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._col = col
self._val = val
def __enter__(self):
@@ -115,6 +139,9 @@ class _ScopeStyleColor:
def style_var(var: int, val: Any): return _ScopeStyleVar(var, val)
class _ScopeStyleVar:
def __init__(self, var: int, val: Any):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._var = var
self._val = val
def __enter__(self):
@@ -126,6 +153,9 @@ class _ScopeStyleVar:
def table(name: str, columns: int, flags: int = 0): return _ScopeTable(name, columns, flags)
class _ScopeTable:
def __init__(self, name: str, columns: int, flags: int):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._name = name
self._columns = columns
self._flags = flags
@@ -141,6 +171,9 @@ class _ScopeTable:
def tab_bar(id_str: str, flags: int = 0): return _ScopeTabBar(id_str, flags)
class _ScopeTabBar:
def __init__(self, id_str: str, flags: int):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._id = id_str
self._flags = flags
self._active = False
@@ -155,6 +188,9 @@ class _ScopeTabBar:
def tab_item(label: str, flags: int = 0): return _ScopeTabItem(label, flags)
class _ScopeTabItem:
def __init__(self, label: str, flags: int):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._label = label
self._flags = flags
self._expanded = False
@@ -170,6 +206,9 @@ class _ScopeTabItem:
def text_wrap(wrap_pos: float = 0.0): return _ScopeTextWrap(wrap_pos)
class _ScopeTextWrap:
def __init__(self, wrap_pos: float):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._wrap_pos = wrap_pos
def __enter__(self):
imgui.push_text_wrap_pos(self._wrap_pos)
@@ -188,6 +227,9 @@ class _ScopeTooltip:
def tree_node_ex(label: str, flags: int = 0): return _ScopeTreeNodeEx(label, flags)
class _ScopeTreeNodeEx:
def __init__(self, label: str, flags: int):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._label = label
self._flags = flags
self._opened = False
@@ -202,6 +244,9 @@ class _ScopeTreeNodeEx:
def window(name: str, visible: bool = True, flags: int = 0): return _ScopeWindow(name, visible, flags)
class _ScopeWindow:
def __init__(self, name: str, visible: bool, flags: int):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._name = name
self._visible = visible
self._flags = flags
@@ -211,4 +256,4 @@ class _ScopeWindow:
return self._result
def __exit__(self, *args):
imgui.end()
return False
return False
+18 -15
View File
@@ -8,19 +8,21 @@ from src.log_registry import LogRegistry
class LogPruner:
"""
Handles the automated deletion of old and insignificant session logs.
Ensures that only whitelisted or significant sessions (based on size/content)
are preserved long-term.
Handles the automated deletion of old and insignificant session logs.
Ensures that only whitelisted or significant sessions (based on size/content)
are preserved long-term.
"""
def __init__(self, log_registry: LogRegistry, logs_dir: str) -> None:
"""
Initializes the LogPruner.
Args:
log_registry: An instance of LogRegistry to check session data.
logs_dir: The path to the directory containing session sub-directories.
Initializes the LogPruner.
Args:
log_registry: An instance of LogRegistry to check session data.
logs_dir: The path to the directory containing session sub-directories.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.log_registry = log_registry
@@ -29,12 +31,13 @@ class LogPruner:
def prune(self, max_age_days: int = 1, min_size_kb: int = 2) -> None:
"""
Prunes old and small session directories from the logs directory.
Deletes session directories that meet the following criteria:
1. The session start time is older than max_age_days.
2. The session name is NOT in the whitelist provided by the LogRegistry.
3. The total size of all files within the session directory is less than min_size_kb.
Prunes old and small session directories from the logs directory.
Deletes session directories that meet the following criteria:
1. The session start time is older than max_age_days.
2. The session name is NOT in the whitelist provided by the LogRegistry.
3. The total size of all files within the session directory is less than min_size_kb.
[C: tests/test_log_pruner.py:test_prune_old_insignificant_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_handles_relative_paths_starting_with_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_sessions_without_metadata_regardless_of_age, tests/test_logging_e2e.py:test_logging_e2e]
"""
now = datetime.now()
@@ -119,4 +122,4 @@ class LogPruner:
except OSError as e:
sys.stderr.write(f"[LogPruner] Error removing {resolved_path}: {e}\n")
self.log_registry.save_registry()
self.log_registry.save_registry()
+56 -48
View File
@@ -48,17 +48,19 @@ from typing import Any
class LogRegistry:
"""
Manages a persistent registry of session logs using a TOML file.
Tracks session paths, start times, whitelisting status, and metadata.
Manages a persistent registry of session logs using a TOML file.
Tracks session paths, start times, whitelisting status, and metadata.
"""
def __init__(self, registry_path: str) -> None:
"""
Initializes the LogRegistry with a path to the registry file.
Args:
registry_path (str): The file path to the TOML registry.
Initializes the LogRegistry with a path to the registry file.
Args:
registry_path (str): The file path to the TOML registry.
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.registry_path = registry_path
@@ -73,8 +75,9 @@ class LogRegistry:
def load_registry(self) -> None:
"""
Loads the registry data from the TOML file into memory.
Handles date/time conversions from TOML-native formats to strings for consistency.
Loads the registry data from the TOML file into memory.
Handles date/time conversions from TOML-native formats to strings for consistency.
"""
if os.path.exists(self.registry_path):
try:
@@ -102,8 +105,9 @@ class LogRegistry:
def save_registry(self) -> None:
"""
Serializes and saves the current registry data to the TOML file.
Converts internal datetime objects to ISO format strings for compatibility.
Serializes and saves the current registry data to the TOML file.
Converts internal datetime objects to ISO format strings for compatibility.
[C: tests/test_logging_e2e.py:test_logging_e2e]
"""
try:
@@ -137,12 +141,13 @@ class LogRegistry:
def register_session(self, session_id: str, path: str, start_time: datetime | str) -> None:
"""
Registers a new session in the registry.
Args:
session_id (str): Unique identifier for the session.
path (str): File path to the session's log directory.
start_time (datetime|str): The timestamp when the session started.
Registers a new session in the registry.
Args:
session_id (str): Unique identifier for the session.
path (str): File path to the session's log directory.
start_time (datetime|str): The timestamp when the session started.
[C: src/session_logger.py:open_session, tests/test_auto_whitelist.py:test_auto_whitelist_keywords, tests/test_auto_whitelist.py:test_auto_whitelist_large_size, tests/test_auto_whitelist.py:test_auto_whitelist_message_count, tests/test_auto_whitelist.py:test_no_auto_whitelist_insignificant, tests/test_log_pruner.py:test_prune_old_insignificant_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_empty_sessions, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_sessions_without_metadata, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_handles_relative_paths_starting_with_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_sessions_without_metadata_regardless_of_age, tests/test_log_registry.py:TestLogRegistry.test_get_old_non_whitelisted_sessions, tests/test_log_registry.py:TestLogRegistry.test_is_session_whitelisted, tests/test_log_registry.py:TestLogRegistry.test_register_session, tests/test_log_registry.py:TestLogRegistry.test_update_session_metadata, tests/test_logging_e2e.py:test_logging_e2e]
"""
if session_id in self.data:
@@ -163,15 +168,16 @@ class LogRegistry:
def update_session_metadata(self, session_id: str, message_count: int, errors: int, size_kb: int, whitelisted: bool, reason: str) -> None:
"""
Updates metadata fields for an existing session.
Args:
session_id (str): Unique identifier for the session.
message_count (int): Total number of messages in the session.
errors (int): Number of errors identified in logs.
size_kb (int): Total size of the session logs in kilobytes.
whitelisted (bool): Whether the session should be protected from pruning.
reason (str): Explanation for the current whitelisting status.
Updates metadata fields for an existing session.
Args:
session_id (str): Unique identifier for the session.
message_count (int): Total number of messages in the session.
errors (int): Number of errors identified in logs.
size_kb (int): Total size of the session logs in kilobytes.
whitelisted (bool): Whether the session should be protected from pruning.
reason (str): Explanation for the current whitelisting status.
[C: tests/test_auto_whitelist.py:test_auto_whitelist_large_size, tests/test_auto_whitelist.py:test_auto_whitelist_message_count, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_empty_sessions, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_registry.py:TestLogRegistry.test_get_old_non_whitelisted_sessions, tests/test_log_registry.py:TestLogRegistry.test_is_session_whitelisted, tests/test_log_registry.py:TestLogRegistry.test_update_session_metadata]
"""
if session_id not in self.data:
@@ -197,13 +203,14 @@ class LogRegistry:
def is_session_whitelisted(self, session_id: str) -> bool:
"""
Checks if a specific session is marked as whitelisted.
Args:
session_id (str): Unique identifier for the session.
Returns:
bool: True if whitelisted, False otherwise.
Checks if a specific session is marked as whitelisted.
Args:
session_id (str): Unique identifier for the session.
Returns:
bool: True if whitelisted, False otherwise.
[C: tests/test_auto_whitelist.py:test_auto_whitelist_keywords, tests/test_auto_whitelist.py:test_auto_whitelist_large_size, tests/test_auto_whitelist.py:test_auto_whitelist_message_count, tests/test_auto_whitelist.py:test_no_auto_whitelist_insignificant, tests/test_log_registry.py:TestLogRegistry.test_is_session_whitelisted, tests/test_logging_e2e.py:test_logging_e2e]
"""
session_data = self.data.get(session_id)
@@ -215,12 +222,13 @@ class LogRegistry:
def update_auto_whitelist_status(self, session_id: str) -> None:
"""
Analyzes session logs and updates whitelisting status based on heuristics.
Sessions are automatically whitelisted if they contain error keywords,
have a high message count, or exceed a size threshold.
Args:
session_id (str): Unique identifier for the session to analyze.
Analyzes session logs and updates whitelisting status based on heuristics.
Sessions are automatically whitelisted if they contain error keywords,
have a high message count, or exceed a size threshold.
Args:
session_id (str): Unique identifier for the session to analyze.
[C: src/session_logger.py:close_session]
"""
if session_id not in self.data:
@@ -275,15 +283,16 @@ class LogRegistry:
def get_old_non_whitelisted_sessions(self, cutoff_datetime: datetime) -> list[dict[str, Any]]:
"""
Retrieves a list of sessions that are older than a specific cutoff time
and are not marked as whitelisted.
Also includes non-whitelisted sessions that are empty (message_count=0 or size_kb=0).
Args:
cutoff_datetime (datetime): The threshold time for identifying old sessions.
Returns:
list: A list of dictionaries containing session details (id, path, start_time).
Retrieves a list of sessions that are older than a specific cutoff time
and are not marked as whitelisted.
Also includes non-whitelisted sessions that are empty (message_count=0 or size_kb=0).
Args:
cutoff_datetime (datetime): The threshold time for identifying old sessions.
Returns:
list: A list of dictionaries containing session details (id, path, start_time).
[C: tests/test_log_pruner.py:test_prune_old_insignificant_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_empty_sessions, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_sessions_without_metadata, tests/test_log_registry.py:TestLogRegistry.test_get_old_non_whitelisted_sessions]
"""
old_sessions = []
@@ -316,4 +325,3 @@ class LogRegistry:
'start_time': start_time_raw
})
return old_sessions
+9 -7
View File
@@ -10,12 +10,13 @@ from typing import Optional, Dict, Callable
class MarkdownRenderer:
"""
Hybrid Markdown renderer that uses imgui_md for text/headers
and ImGuiColorTextEdit for syntax-highlighted code blocks.
Hybrid Markdown renderer that uses imgui_md for text/headers
and ImGuiColorTextEdit for syntax-highlighted code blocks.
"""
def __init__(self):
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self.options = imgui_md.MarkdownOptions()
# Base path for fonts (Inter family)
@@ -67,8 +68,9 @@ class MarkdownRenderer:
def render(self, text: str, context_id: str = "default") -> None:
"""
Render Markdown text with code block interception.
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
Render Markdown text with code block interception.
[C: src/theme_2.py:render_post_fx, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
"""
if not text:
return
@@ -167,7 +169,7 @@ def get_renderer() -> MarkdownRenderer:
def render(text: str, context_id: str = "default") -> None:
"""
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
[C: src/theme_2.py:render_post_fx, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
"""
get_renderer().render(text, context_id)
@@ -175,4 +177,4 @@ def render_unindented(text: str) -> None:
get_renderer().render_unindented(text)
def render_code(code: str, lang: str = "", context_id: str = "default", block_idx: int = 0) -> None:
get_renderer().render_code(code, lang, context_id, block_idx)
get_renderer().render_code(code, lang, context_id, block_idx)
+79 -55
View File
@@ -102,12 +102,13 @@ perf_monitor_callback: Optional[Callable[[], dict[str, Any]]] = None
def configure(file_items: list[dict[str, Any]], extra_base_dirs: list[str] | None = None) -> None:
"""
Build the allowlist from aggregate file_items.
Called by ai_client before each send so the list reflects the current project.
file_items : list of dicts from aggregate.build_file_items()
extra_base_dirs : additional directory roots to allow traversal of
[C: tests/conftest.py:reset_ai_client, tests/test_arch_boundary_phase1.py:TestArchBoundaryPhase1.test_mcp_client_whitelist_enforcement, tests/test_mcp_client_beads.py:test_bd_mcp_tools]
Build the allowlist from aggregate file_items.
Called by ai_client before each send so the list reflects the current project.
file_items : list of dicts from aggregate.build_file_items()
extra_base_dirs : additional directory roots to allow traversal of
[C: tests/conftest.py:reset_ai_client, tests/test_arch_boundary_phase1.py:TestArchBoundaryPhase1.test_mcp_client_whitelist_enforcement, tests/test_mcp_client_beads.py:test_bd_mcp_tools, tests/test_py_struct_tools.py:test_mcp_dispatch_errors, tests/test_py_struct_tools.py:test_mcp_dispatch_integration]
"""
global _allowed_paths, _base_dirs, _primary_base_dir
_allowed_paths = set()
@@ -131,14 +132,15 @@ def configure(file_items: list[dict[str, Any]], extra_base_dirs: list[str] | Non
def _is_allowed(path: Path) -> bool:
"""
Return True if `path` is within the allowlist.
A path is allowed if:
- it is explicitly in _allowed_paths, OR
- it is contained within (or equal to) one of the _base_dirs
All paths are resolved (follows symlinks) before comparison to prevent
symlink-based path traversal.
CRITICAL: Blacklisted files (history) are NEVER allowed.
Return True if `path` is within the allowlist.
A path is allowed if:
- it is explicitly in _allowed_paths, OR
- it is contained within (or equal to) one of the _base_dirs
All paths are resolved (follows symlinks) before comparison to prevent
symlink-based path traversal.
CRITICAL: Blacklisted files (history) are NEVER allowed.
[C: tests/test_arch_boundary_phase1.py:TestArchBoundaryPhase1.test_mcp_client_whitelist_enforcement, tests/test_history_management.py:test_mcp_blacklist]
"""
from src.paths import get_config_path
@@ -180,8 +182,9 @@ def _is_allowed(path: Path) -> bool:
def _resolve_and_check(raw_path: str) -> tuple[Path | None, str]:
"""
Resolve raw_path and verify it passes the allowlist check.
Returns (resolved_path, error_string). error_string is empty on success.
Resolve raw_path and verify it passes the allowlist check.
Returns (resolved_path, error_string). error_string is empty on success.
"""
try:
p = Path(raw_path)
@@ -243,8 +246,9 @@ def list_directory(path: str) -> str:
def search_files(path: str, pattern: str) -> str:
"""
Search for files matching a glob pattern within path.
pattern examples: '*.py', '**/*.toml', 'src/**/*.rs'
Search for files matching a glob pattern within path.
pattern examples: '*.py', '**/*.toml', 'src/**/*.rs'
"""
p, err = _resolve_and_check(path)
if err or p is None:
@@ -274,9 +278,10 @@ def search_files(path: str, pattern: str) -> str:
def get_file_summary(path: str) -> str:
"""
Return the heuristic summary for a file (same as the initial context block).
For .py files: imports, classes, methods, functions, constants.
For .toml: table keys. For .md: headings. Others: line count + preview.
Return the heuristic summary for a file (same as the initial context block).
For .py files: imports, classes, methods, functions, constants.
For .toml: table keys. For .md: headings. Others: line count + preview.
"""
p, err = _resolve_and_check(path)
if err or p is None:
@@ -294,7 +299,8 @@ def get_file_summary(path: str) -> str:
def py_get_skeleton(path: str) -> str:
"""
Returns a skeleton of a Python file (preserving docstrings, stripping function bodies).
Returns a skeleton of a Python file (preserving docstrings, stripping function bodies).
"""
p, err = _resolve_and_check(path)
if err:
@@ -314,7 +320,8 @@ def py_get_skeleton(path: str) -> str:
def ts_c_get_skeleton(path: str) -> str:
"""
Returns a skeleton of a C file.
Returns a skeleton of a C file.
[C: tests/test_ts_c_tools.py:test_ts_c_get_skeleton]
"""
p, err = _resolve_and_check(path)
@@ -331,8 +338,9 @@ def ts_c_get_skeleton(path: str) -> str:
def ts_cpp_get_skeleton(path: str) -> str:
"""
Returns a skeleton of a C++ file.
[C: tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_get_skeleton]
Returns a skeleton of a C++ file.
[C: tests/test_gencpp_full_suite.py:test_gencpp_full_suite, tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_get_skeleton]
"""
p, err = _resolve_and_check(path)
if err: return err
@@ -349,7 +357,8 @@ def ts_cpp_get_skeleton(path: str) -> str:
def py_get_code_outline(path: str) -> str:
"""
Returns a hierarchical outline of a code file (classes, functions, methods with line ranges).
Returns a hierarchical outline of a code file (classes, functions, methods with line ranges).
"""
p, err = _resolve_and_check(path)
if err:
@@ -367,7 +376,8 @@ def py_get_code_outline(path: str) -> str:
def ts_c_get_code_outline(path: str) -> str:
"""
Returns a hierarchical outline of a C file.
Returns a hierarchical outline of a C file.
[C: tests/test_ts_c_tools.py:test_ts_c_get_code_outline]
"""
p, err = _resolve_and_check(path)
@@ -384,8 +394,9 @@ def ts_c_get_code_outline(path: str) -> str:
def ts_cpp_get_code_outline(path: str) -> str:
"""
Returns a hierarchical outline of a C++ file.
[C: tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_get_code_outline]
Returns a hierarchical outline of a C++ file.
[C: tests/test_gencpp_full_suite.py:test_gencpp_full_suite, tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_get_code_outline]
"""
p, err = _resolve_and_check(path)
if err: return err
@@ -415,8 +426,9 @@ def ts_c_get_definition(path: str, name: str) -> str:
def ts_cpp_get_definition(path: str, name: str) -> str:
"""
Returns the source code for a specific definition in a C++ file.
[C: tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition_gencpp]
Returns the source code for a specific definition in a C++ file.
[C: tests/test_ast_masking_core.py:test_ast_masking_gencpp_samples, tests/test_gencpp_full_suite.py:test_gencpp_full_suite, tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition_gencpp]
"""
p, err = _resolve_and_check(path)
if err: return err
@@ -478,7 +490,8 @@ def ts_c_update_definition(path: str, name: str, new_content: str) -> str:
def ts_cpp_update_definition(path: str, name: str, new_content: str) -> str:
"""
Surgically replace the definition of a class or function in a C++ file.
Surgically replace the definition of a class or function in a C++ file.
[C: tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition_gencpp]
"""
p, err = _resolve_and_check(path)
@@ -537,8 +550,9 @@ def set_file_slice(path: str, start_line: int, end_line: int, new_content: str)
def edit_file(path: str, old_string: str, new_string: str, replace_all: bool = False) -> str:
"""
Replace exact string match in a file. Preserves indentation and line endings.
Drop-in replacement for native edit tool that destroys 1-space indentation.
Replace exact string match in a file. Preserves indentation and line endings.
Drop-in replacement for native edit tool that destroys 1-space indentation.
"""
p, err = _resolve_and_check(path)
if err:
@@ -595,8 +609,9 @@ def _get_symbol_node(tree: ast.AST, name: str) -> Optional[ast.AST]:
def py_get_symbol_info(path: str, name: str) -> tuple[str, int] | str:
"""
Returns (source_code, line_number) for a specific class, function, or method definition.
If not found, returns an error string.
Returns (source_code, line_number) for a specific class, function, or method definition.
If not found, returns an error string.
"""
p, err = _resolve_and_check(path)
if err:
@@ -622,9 +637,10 @@ def py_get_symbol_info(path: str, name: str) -> tuple[str, int] | str:
def py_get_definition(path: str, name: str) -> str:
"""
Returns the source code for a specific class, function, or method definition.
path: Path to the code file.
name: Name of the definition to retrieve (e.g., 'MyClass', 'my_function', 'MyClass.my_method').
Returns the source code for a specific class, function, or method definition.
path: Path to the code file.
name: Name of the definition to retrieve (e.g., 'MyClass', 'my_function', 'MyClass.my_method').
"""
p, err = _resolve_and_check(path)
if err:
@@ -797,9 +813,10 @@ def py_set_var_declaration(path: str, name: str, new_declaration: str) -> str:
def get_git_diff(path: str, base_rev: str = "HEAD", head_rev: str = "") -> str:
"""
Returns the git diff for a file or directory.
base_rev: The base revision (default: HEAD)
head_rev: The head revision (optional)
Returns the git diff for a file or directory.
base_rev: The base revision (default: HEAD)
head_rev: The head revision (optional)
"""
p, err = _resolve_and_check(path)
if err:
@@ -1151,7 +1168,8 @@ def fetch_url(url: str) -> str:
def get_ui_performance() -> str:
"""
Returns current UI performance metrics (FPS, Frame Time, CPU, Input Lag).
Returns current UI performance metrics (FPS, Frame Time, CPU, Input Lag).
[C: tests/test_mcp_perf_tool.py:test_mcp_perf_tool_retrieval]
"""
if perf_monitor_callback is None:
@@ -1183,7 +1201,7 @@ class StdioMCPServer:
async def start(self):
"""
[C: src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
self.status = 'starting'
self.proc = await asyncio.create_subprocess_exec(
@@ -1199,7 +1217,7 @@ class StdioMCPServer:
async def stop(self):
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
if self.proc:
try:
@@ -1262,7 +1280,8 @@ class ExternalMCPManager:
async def add_server(self, config: models.MCPServerConfig):
"""
Add and start a new MCP server from a configuration object.
Add and start a new MCP server from a configuration object.
[C: tests/test_external_mcp.py:test_external_mcp_real_process, tests/test_external_mcp.py:test_get_tool_schemas_includes_external]
"""
if config.url:
@@ -1274,7 +1293,8 @@ class ExternalMCPManager:
async def stop_all(self):
"""
Stop all managed MCP servers and clear the registry.
Stop all managed MCP servers and clear the registry.
[C: tests/test_external_mcp.py:test_external_mcp_real_process, tests/test_external_mcp.py:test_get_tool_schemas_includes_external, tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call]
"""
for server in self.servers.values():
@@ -1283,7 +1303,8 @@ class ExternalMCPManager:
def get_all_tools(self) -> dict:
"""
Retrieve a dictionary of all tools available across all managed servers.
Retrieve a dictionary of all tools available across all managed servers.
[C: tests/test_external_mcp.py:test_external_mcp_real_process, tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call]
"""
all_tools = {}
@@ -1298,7 +1319,8 @@ class ExternalMCPManager:
async def async_dispatch(self, tool_name: str, tool_input: dict) -> str:
"""
Dispatch a tool call to the appropriate external MCP server asynchronously.
Dispatch a tool call to the appropriate external MCP server asynchronously.
[C: src/rag_engine.py:RAGEngine._async_search_mcp, tests/test_external_mcp.py:test_external_mcp_real_process]
"""
for server in self.servers.values():
@@ -1310,7 +1332,8 @@ _external_mcp_manager = ExternalMCPManager()
def get_external_mcp_manager() -> ExternalMCPManager:
"""
Retrieve the global ExternalMCPManager instance.
Retrieve the global ExternalMCPManager instance.
[C: tests/test_external_mcp.py:test_get_tool_schemas_includes_external, tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call]
"""
global _external_mcp_manager
@@ -1319,8 +1342,9 @@ def get_external_mcp_manager() -> ExternalMCPManager:
def dispatch(tool_name: str, tool_input: dict[str, Any]) -> str:
"""
Dispatch an MCP tool call by name. Returns the result as a string.
[C: tests/test_gemini_cli_edge_cases.py:test_gemini_cli_parameter_resilience, tests/test_mcp_client_beads.py:test_bd_mcp_tools, tests/test_mcp_ts_integration.py:test_ts_c_get_code_outline_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_signature_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_skeleton_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_update_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_code_outline_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_signature_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_skeleton_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_update_definition_dispatch]
Dispatch an MCP tool call by name. Returns the result as a string.
[C: tests/test_gemini_cli_edge_cases.py:test_gemini_cli_parameter_resilience, tests/test_mcp_client_beads.py:test_bd_mcp_tools, tests/test_mcp_ts_integration.py:test_ts_c_get_code_outline_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_signature_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_skeleton_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_update_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_code_outline_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_signature_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_skeleton_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_update_definition_dispatch, tests/test_py_struct_tools.py:test_mcp_dispatch_errors, tests/test_py_struct_tools.py:test_mcp_dispatch_integration]
"""
# Handle aliases
path = str(tool_input.get("path", tool_input.get("file_path", tool_input.get("dir_path", ""))))
@@ -1476,7 +1500,7 @@ def dispatch(tool_name: str, tool_input: dict[str, Any]) -> str:
async def async_dispatch(tool_name: str, tool_input: dict[str, Any]) -> str:
# Check native tools
"""
[C: src/rag_engine.py:RAGEngine._async_search_mcp, tests/test_external_mcp.py:test_external_mcp_real_process]
[C: src/rag_engine.py:RAGEngine._async_search_mcp, tests/test_external_mcp.py:test_external_mcp_real_process]
"""
native_names = {t['name'] for t in MCP_TOOL_SPECS}
if tool_name in native_names:
@@ -1492,7 +1516,7 @@ async def async_dispatch(tool_name: str, tool_input: dict[str, Any]) -> str:
def get_tool_schemas() -> list[dict[str, Any]]:
"""
[C: tests/test_arch_boundary_phase2.py:TestArchBoundaryPhase2.test_mcp_client_dispatch_completeness, tests/test_external_mcp.py:test_get_tool_schemas_includes_external, tests/test_mcp_client_beads.py:test_bd_mcp_tools]
[C: tests/test_arch_boundary_phase2.py:TestArchBoundaryPhase2.test_mcp_client_dispatch_completeness, tests/test_external_mcp.py:test_get_tool_schemas_includes_external, tests/test_mcp_client_beads.py:test_bd_mcp_tools]
"""
res = list(MCP_TOOL_SPECS)
manager = get_external_mcp_manager()
@@ -2283,4 +2307,4 @@ MCP_TOOL_SPECS: list[dict[str, Any]] = [
}
]
TOOL_NAMES: set[str] = {t['name'] for t in MCP_TOOL_SPECS}
TOOL_NAMES: set[str] = {t['name'] for t in MCP_TOOL_SPECS}
+150 -3
View File
File diff suppressed because one or more lines are too long
+45 -32
View File
@@ -48,7 +48,8 @@ from src.dag_engine import TrackDAG, ExecutionEngine
class WorkerPool:
"""
Manages a pool of worker threads with a concurrency limit.
Manages a pool of worker threads with a concurrency limit.
"""
def __init__(self, max_workers: int = 4):
self.max_workers = max_workers
@@ -59,8 +60,9 @@ class WorkerPool:
def spawn(self, ticket_id: str, target: Callable, args: tuple) -> Optional[threading.Thread]:
"""
Spawns a new worker thread if the pool is not full.
Returns the thread object or None if full.
Spawns a new worker thread if the pool is not full.
Returns the thread object or None if full.
[C: tests/test_parallel_execution.py:test_worker_pool_completion_cleanup, tests/test_parallel_execution.py:test_worker_pool_limit, tests/test_parallel_execution.py:test_worker_pool_tracking]
"""
with self._lock:
@@ -83,7 +85,7 @@ class WorkerPool:
def join_all(self, timeout: float = None) -> None:
"""
[C: tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_parallel_execution.py:test_worker_pool_limit, tests/test_parallel_execution.py:test_worker_pool_tracking]
[C: tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_parallel_execution.py:test_worker_pool_limit, tests/test_parallel_execution.py:test_worker_pool_tracking]
"""
with self._lock:
threads = list(self._active.values())
@@ -94,21 +96,22 @@ class WorkerPool:
def get_active_count(self) -> int:
"""
[C: tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_parallel_execution.py:test_worker_pool_completion_cleanup, tests/test_parallel_execution.py:test_worker_pool_limit]
[C: tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_parallel_execution.py:test_worker_pool_completion_cleanup, tests/test_parallel_execution.py:test_worker_pool_limit]
"""
with self._lock:
return len(self._active)
def is_full(self) -> bool:
"""
[C: tests/test_parallel_execution.py:test_worker_pool_limit]
[C: tests/test_parallel_execution.py:test_worker_pool_limit]
"""
return self.get_active_count() >= self.max_workers
class ConductorEngine:
"""
Orchestrates the execution of tickets within a track.
Orchestrates the execution of tickets within a track.
"""
def __init__(self, track: Track, event_queue: Optional[events.AsyncEventQueue] = None, auto_queue: bool = False) -> None:
@@ -148,21 +151,24 @@ class ConductorEngine:
def pause(self) -> None:
"""
Pauses the pipeline execution.
Pauses the pipeline execution.
[C: tests/test_pipeline_pause.py:test_pause_method, tests/test_pipeline_pause.py:test_resume_method]
"""
self._pause_event.set()
def resume(self) -> None:
"""
Resumes the pipeline execution.
Resumes the pipeline execution.
[C: tests/test_pipeline_pause.py:test_resume_method]
"""
self._pause_event.clear()
def approve_task(self, task_id: str) -> None:
"""
Manually transition todo to in_progress and mark engine dirty.
Manually transition todo to in_progress and mark engine dirty.
[C: tests/test_execution_engine.py:test_execution_engine_approve_task, tests/test_execution_engine.py:test_execution_engine_step_mode]
"""
self.engine.approve_task(task_id)
@@ -170,7 +176,8 @@ class ConductorEngine:
def update_task_status(self, task_id: str, status: str) -> None:
"""
Force-update ticket status and mark engine dirty.
Force-update ticket status and mark engine dirty.
[C: tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_manual_unblock_restores_todo, tests/test_execution_engine.py:test_execution_engine_auto_queue, tests/test_execution_engine.py:test_execution_engine_basic_flow, tests/test_execution_engine.py:test_execution_engine_status_persistence, tests/test_execution_engine.py:test_execution_engine_update_nonexistent_task]
"""
self.engine.update_task_status(task_id, status)
@@ -178,7 +185,8 @@ class ConductorEngine:
def kill_worker(self, ticket_id: str) -> None:
"""
Sets the abort event for a worker and attempts to join its thread.
Sets the abort event for a worker and attempts to join its thread.
[C: tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread]
"""
if ticket_id in self._abort_events:
@@ -213,8 +221,9 @@ class ConductorEngine:
def parse_json_tickets(self, json_str: str) -> None:
"""
Parses a JSON string of ticket definitions (Godot ECS Flat List format)
and populates the Track's ticket list.
Parses a JSON string of ticket definitions (Godot ECS Flat List format)
and populates the Track's ticket list.
[C: tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_orchestration_logic.py:test_conductor_engine_parse_json_tickets]
"""
try:
@@ -244,11 +253,12 @@ class ConductorEngine:
def run(self, md_content: str = "", max_ticks: Optional[int] = None) -> None:
"""
Main execution loop using the DAG engine.
Args:
md_content: The full markdown context (history + files) for AI workers.
max_ticks: Optional limit on number of iterations (for testing).
[C: simulation/sim_base.py:run_sim, src/project_manager.py:get_git_commit, src/project_manager.py:get_git_log, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run]
Main execution loop using the DAG engine.
Args:
md_content: The full markdown context (history + files) for AI workers.
max_ticks: Optional limit on number of iterations (for testing).
[C: simulation/sim_base.py:run_sim, src/project_manager.py:get_git_commit, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run]
"""
tick_count = 0
while True:
@@ -368,7 +378,8 @@ def _queue_put(event_queue: events.AsyncEventQueue, event_name: str, payload) ->
def confirm_execution(payload: str, event_queue: events.AsyncEventQueue, ticket_id: str) -> bool:
"""
Pushes an approval request to the GUI and waits for response.
Pushes an approval request to the GUI and waits for response.
"""
dialog_container = [None]
task = {
@@ -391,8 +402,9 @@ def confirm_execution(payload: str, event_queue: events.AsyncEventQueue, ticket_
def confirm_spawn(role: str, prompt: str, context_md: str, event_queue: events.AsyncEventQueue, ticket_id: str) -> Tuple[bool, str, str]:
"""
Pushes a spawn approval request to the GUI and waits for response.
Returns (approved, modified_prompt, modified_context)
Pushes a spawn approval request to the GUI and waits for response.
Returns (approved, modified_prompt, modified_context)
[C: tests/test_spawn_interception_v2.py:run_confirm]
"""
dialog_container = [None]
@@ -432,15 +444,16 @@ def confirm_spawn(role: str, prompt: str, context_md: str, event_queue: events.A
def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files: List[str] | None = None, event_queue: events.AsyncEventQueue | None = None, engine: Optional['ConductorEngine'] = None, md_content: str = "") -> None:
"""
Simulates the lifecycle of a single agent working on a ticket.
Calls the AI client and updates the ticket status based on the response.
Args:
ticket: The ticket to process.
context: The worker context.
context_files: List of files to include in the context.
event_queue: Queue for pushing state updates and receiving approvals.
engine: The conductor engine.
md_content: The markdown context (history + files) for AI workers.
Simulates the lifecycle of a single agent working on a ticket.
Calls the AI client and updates the ticket status based on the response.
Args:
ticket: The ticket to process.
context: The worker context.
context_files: List of files to include in the context.
event_queue: Queue for pushing state updates and receiving approvals.
engine: The conductor engine.
md_content: The markdown context (history + files) for AI workers.
[C: tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_calls_ai_client_send, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_context_injection, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_handles_blocked_response, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_pushes_response_via_queue, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_step_mode_confirmation, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_step_mode_rejection, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_token_usage_from_comms_log, tests/test_context_pruner.py:test_token_reduction_logging, tests/test_orchestration_logic.py:test_run_worker_lifecycle_blocked, tests/test_phase6_engine.py:test_worker_streaming_intermediate, tests/test_run_worker_lifecycle_abort.py:TestRunWorkerLifecycleAbort.test_run_worker_lifecycle_returns_early_on_abort, tests/test_spawn_interception_v2.py:test_run_worker_lifecycle_approved, tests/test_spawn_interception_v2.py:test_run_worker_lifecycle_rejected, tests/test_tiered_aggregation.py:test_run_worker_lifecycle_uses_strategy]
"""
# Enforce Context Amnesia: each ticket starts with a clean slate.
@@ -645,4 +658,4 @@ def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files:
if event_queue:
_queue_put(event_queue, "ticket_completed", {"ticket_id": ticket.id, "timestamp": time.time()})
return response
return response
+6 -4
View File
@@ -12,7 +12,8 @@ from src import paths
def get_track_history_summary() -> str:
"""
Scans conductor/archive/ and conductor/tracks/ to build a summary of past work.
Scans conductor/archive/ and conductor/tracks/ to build a summary of past work.
[C: tests/test_orchestrator_pm_history.py:TestOrchestratorPMHistory.test_get_track_history_summary, tests/test_orchestrator_pm_history.py:TestOrchestratorPMHistory.test_get_track_history_summary_missing_files]
"""
summary_parts = []
@@ -59,8 +60,9 @@ def get_track_history_summary() -> str:
def generate_tracks(user_request: str, project_config: dict[str, Any], file_items: list[dict[str, Any]], history_summary: Optional[str] = None) -> list[dict[str, Any]]:
"""
Tier 1 (Strategic PM) call.
Analyzes the project state and user request to generate a list of Tracks.
Tier 1 (Strategic PM) call.
Analyzes the project state and user request to generate a list of Tracks.
[C: tests/test_orchestration_logic.py:test_generate_tracks, tests/test_orchestrator_pm.py:TestOrchestratorPM.test_generate_tracks_malformed_json, tests/test_orchestrator_pm.py:TestOrchestratorPM.test_generate_tracks_markdown_wrapped, tests/test_orchestrator_pm.py:TestOrchestratorPM.test_generate_tracks_success, tests/test_orchestrator_pm_history.py:TestOrchestratorPMHistory.test_generate_tracks_with_history]
"""
# 1. Build Repository Map (Summary View)
@@ -128,4 +130,4 @@ if __name__ == "__main__":
print("Testing Tier 1 Track Generation...")
history = get_track_history_summary()
tracks = generate_tracks("Implement a basic unit test for the ai_client.py module.", flat, file_items, history_summary=history)
print(json.dumps(tracks, indent=2))
print(json.dumps(tracks, indent=2))
+7 -1
View File
@@ -39,6 +39,9 @@ class CodeOutliner:
pass
def outline(self, code: str) -> str:
"""
[C: tests/test_outline_tool.py:test_code_outliner_imgui_scopes, tests/test_outline_tool.py:test_code_outliner_nested_ifs, tests/test_outline_tool.py:test_code_outliner_type_hints]
"""
code = code.lstrip(chr(0xFEFF))
try:
tree = ast.parse(code)
@@ -55,6 +58,9 @@ class CodeOutliner:
count = [0]
def walk(node: ast.AST, indent: int = 0) -> None:
"""
[C: src/summarize.py:_summarise_python]
"""
count[0] += 1
if count[0] > 100000:
raise Exception("Infinite loop detected! " + str(type(node)))
@@ -120,4 +126,4 @@ def get_outline(path: Path, code: str) -> str:
outliner = CodeOutliner()
return outliner.outline(code)
else:
return f"Outlining not supported for {suffix} files yet."
return f"Outlining not supported for {suffix} files yet."
+11 -11
View File
@@ -17,7 +17,7 @@ class PatchModalManager:
def request_patch_approval(self, patch_text: str, file_paths: List[str], generated_by: str = "Tier 4 QA") -> bool:
"""
[C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
[C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
"""
from time import time
self._pending_patch = PendingPatch(
@@ -31,31 +31,31 @@ class PatchModalManager:
def get_pending_patch(self) -> Optional[PendingPatch]:
"""
[C: tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
[C: tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
"""
return self._pending_patch
def is_modal_shown(self) -> bool:
"""
[C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
[C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
"""
return self._show_modal
def set_apply_callback(self, callback: Callable[[str], bool]) -> None:
"""
[C: tests/test_patch_modal.py:test_apply_callback, tests/test_patch_modal.py:test_reset]
[C: tests/test_patch_modal.py:test_apply_callback, tests/test_patch_modal.py:test_reset]
"""
self._on_apply_callback = callback
def set_reject_callback(self, callback: Callable[[], None]) -> None:
"""
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reset]
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reset]
"""
self._on_reject_callback = callback
def apply_patch(self, patch_text: str) -> bool:
"""
[C: tests/test_patch_modal.py:test_apply_callback]
[C: tests/test_patch_modal.py:test_apply_callback]
"""
if self._on_apply_callback:
return self._on_apply_callback(patch_text)
@@ -63,7 +63,7 @@ class PatchModalManager:
def reject_patch(self) -> None:
"""
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch]
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch]
"""
self._pending_patch = None
self._show_modal = False
@@ -72,13 +72,13 @@ class PatchModalManager:
def close_modal(self) -> None:
"""
[C: tests/test_patch_modal.py:test_close_modal]
[C: tests/test_patch_modal.py:test_close_modal]
"""
self._show_modal = False
def reset(self) -> None:
"""
[C: tests/test_patch_modal.py:test_reset]
[C: tests/test_patch_modal.py:test_reset]
"""
self._pending_patch = None
self._show_modal = False
@@ -89,7 +89,7 @@ _patch_modal_manager: Optional[PatchModalManager] = None
def get_patch_modal_manager() -> PatchModalManager:
"""
[C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton]
[C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton]
"""
global _patch_modal_manager
if _patch_modal_manager is None:
@@ -98,7 +98,7 @@ def get_patch_modal_manager() -> PatchModalManager:
def reset_patch_modal_manager() -> None:
"""
[C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton]
[C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton]
"""
global _patch_modal_manager
if _patch_modal_manager:
+19 -15
View File
@@ -49,60 +49,60 @@ _RESOLVED: dict[str, Path] = {}
def get_config_path() -> Path:
"""
[C: tests/test_paths.py:test_default_paths]
[C: tests/test_paths.py:test_default_paths]
"""
root_dir = Path(__file__).resolve().parent.parent
return Path(os.environ.get("SLOP_CONFIG", root_dir / "config.toml"))
def get_global_presets_path() -> Path:
"""
[C: src/presets.py:PresetManager.__init__, src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope]
[C: src/presets.py:PresetManager.__init__, src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope]
"""
root_dir = Path(__file__).resolve().parent.parent
return Path(os.environ.get("SLOP_GLOBAL_PRESETS", root_dir / "presets.toml"))
def get_project_presets_path(project_root: Path) -> Path:
"""
[C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.project_path]
[C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.project_path]
"""
return project_root / "project_presets.toml"
def get_global_tool_presets_path() -> Path:
"""
[C: src/tool_presets.py:ToolPresetManager._get_path, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets]
[C: src/tool_presets.py:ToolPresetManager._get_path, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets]
"""
root_dir = Path(__file__).resolve().parent.parent
return Path(os.environ.get("SLOP_GLOBAL_TOOL_PRESETS", root_dir / "tool_presets.toml"))
def get_project_tool_presets_path(project_root: Path) -> Path:
"""
[C: src/tool_presets.py:ToolPresetManager._get_path, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets]
[C: src/tool_presets.py:ToolPresetManager._get_path, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets]
"""
return project_root / "project_tool_presets.toml"
def get_global_personas_path() -> Path:
"""
[C: src/personas.py:PersonaManager._get_path, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all]
[C: src/personas.py:PersonaManager._get_path, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all]
"""
root_dir = Path(__file__).resolve().parent.parent
return Path(os.environ.get("SLOP_GLOBAL_PERSONAS", root_dir / "personas.toml"))
def get_project_personas_path(project_root: Path) -> Path:
"""
[C: src/personas.py:PersonaManager._get_path, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all]
[C: src/personas.py:PersonaManager._get_path, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all]
"""
return project_root / "project_personas.toml"
def get_global_workspace_profiles_path() -> Path:
"""
[C: src/workspace_manager.py:WorkspaceManager._get_path, src/workspace_manager.py:WorkspaceManager.load_all_profiles]
[C: src/workspace_manager.py:WorkspaceManager._get_path, src/workspace_manager.py:WorkspaceManager.load_all_profiles]
"""
root_dir = Path(__file__).resolve().parent.parent
return Path(os.environ.get("SLOP_GLOBAL_WORKSPACE_PROFILES", root_dir / "workspace_profiles.toml"))
def get_project_workspace_profiles_path(project_root: Path) -> Path:
"""
[C: src/workspace_manager.py:WorkspaceManager._get_path, src/workspace_manager.py:WorkspaceManager.load_all_profiles]
[C: src/workspace_manager.py:WorkspaceManager._get_path, src/workspace_manager.py:WorkspaceManager.load_all_profiles]
"""
return project_root / ".ai" / "workspace_profiles.toml"
@@ -143,7 +143,7 @@ def _get_project_conductor_dir_from_toml(project_root: Path) -> Optional[Path]:
def get_conductor_dir(project_path: Optional[str] = None) -> Path:
"""
[C: tests/test_paths.py:test_conductor_dir_project_relative, tests/test_project_paths.py:test_get_conductor_dir_default, tests/test_project_paths.py:test_get_conductor_dir_project_specific_with_toml]
[C: tests/test_paths.py:test_conductor_dir_project_relative, tests/test_project_paths.py:test_get_conductor_dir_default, tests/test_project_paths.py:test_get_conductor_dir_project_specific_with_toml]
"""
if not project_path:
# Fallback for legacy/tests, but we should avoid this
@@ -157,7 +157,7 @@ def get_conductor_dir(project_path: Optional[str] = None) -> Path:
def get_logs_dir() -> Path:
"""
[C: src/session_logger.py:close_session, src/session_logger.py:open_session, tests/test_paths.py:test_config_overrides, tests/test_paths.py:test_default_paths, tests/test_paths.py:test_env_var_overrides, tests/test_paths.py:test_precedence]
[C: src/session_logger.py:close_session, src/session_logger.py:open_session, tests/test_paths.py:test_config_overrides, tests/test_paths.py:test_default_paths, tests/test_paths.py:test_env_var_overrides, tests/test_paths.py:test_precedence]
"""
if "logs_dir" not in _RESOLVED:
_RESOLVED["logs_dir"] = _resolve_path("SLOP_LOGS_DIR", "logs_dir", "logs/sessions")
@@ -165,7 +165,7 @@ def get_logs_dir() -> Path:
def get_scripts_dir() -> Path:
"""
[C: src/session_logger.py:log_tool_call, src/session_logger.py:open_session, tests/test_paths.py:test_config_overrides, tests/test_paths.py:test_default_paths]
[C: src/session_logger.py:log_tool_call, src/session_logger.py:open_session, tests/test_paths.py:test_config_overrides, tests/test_paths.py:test_default_paths]
"""
if "scripts_dir" not in _RESOLVED:
_RESOLVED["scripts_dir"] = _resolve_path("SLOP_SCRIPTS_DIR", "scripts_dir", "scripts/generated")
@@ -173,17 +173,20 @@ def get_scripts_dir() -> Path:
def get_tracks_dir(project_path: Optional[str] = None) -> Path:
"""
[C: src/project_manager.py:get_all_tracks, tests/test_paths.py:test_conductor_dir_project_relative]
[C: src/project_manager.py:get_all_tracks, tests/test_paths.py:test_conductor_dir_project_relative]
"""
return get_conductor_dir(project_path) / "tracks"
def get_track_state_dir(track_id: str, project_path: Optional[str] = None) -> Path:
"""
[C: src/project_manager.py:load_track_state, src/project_manager.py:save_track_state, tests/test_paths.py:test_conductor_dir_project_relative]
[C: src/project_manager.py:load_track_state, src/project_manager.py:save_track_state, tests/test_paths.py:test_conductor_dir_project_relative]
"""
return get_tracks_dir(project_path) / track_id
def get_archive_dir(project_path: Optional[str] = None) -> Path:
"""
[C: tests/test_paths.py:test_conductor_dir_project_relative]
"""
return get_conductor_dir(project_path) / "archive"
def _resolve_path_info(env_var: str, config_key: str, default: str) -> dict[str, Any]:
@@ -210,7 +213,8 @@ def get_full_path_info() -> dict[str, dict[str, Any]]:
def reset_resolved() -> None:
"""
For testing only - clear cached resolutions.
For testing only - clear cached resolutions.
[C: tests/conftest.py:reset_paths, tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_gui_phase3.py:test_conductor_setup_scan, tests/test_paths.py:reset_paths, tests/test_project_paths.py:test_get_all_tracks_project_specific, tests/test_project_paths.py:test_get_conductor_dir_default, tests/test_project_paths.py:test_get_conductor_dir_project_specific_with_toml]
"""
_RESOLVED.clear()
+17 -13
View File
@@ -77,7 +77,7 @@ class PerformanceScope:
def get_monitor() -> PerformanceMonitor:
"""
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_perf_dag.py:test_dag_performance]
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_perf_dag.py:test_dag_performance]
"""
global _instance
if _instance is None:
@@ -87,8 +87,9 @@ def get_monitor() -> PerformanceMonitor:
class PerformanceMonitor:
"""
Tracks application performance metrics like FPS, frame time, and CPU usage.
Supports thread-safe tracking for individual components with efficient moving averages.
Tracks application performance metrics like FPS, frame time, and CPU usage.
Supports thread-safe tracking for individual components with efficient moving averages.
"""
def __init__(self, history_size: int = 300) -> None:
self.enabled: bool = False
@@ -160,7 +161,7 @@ class PerformanceMonitor:
def start_frame(self) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
"""
now = time.perf_counter()
with self._lock:
@@ -174,7 +175,7 @@ class PerformanceMonitor:
def end_frame(self) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
"""
if self._start_time is None:
return
@@ -205,7 +206,7 @@ class PerformanceMonitor:
def start_component(self, name: str) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
"""
if not self.enabled: return
now = time.perf_counter()
@@ -214,7 +215,7 @@ class PerformanceMonitor:
def end_component(self, name: str) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
"""
if not self.enabled: return
now = time.perf_counter()
@@ -233,7 +234,8 @@ class PerformanceMonitor:
def get_metrics(self) -> dict[str, float]:
"""
Returns current metrics and their moving averages. Thread-safe.
Returns current metrics and their moving averages. Thread-safe.
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_perf_dag.py:test_dag_performance, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager]
"""
with self._lock:
@@ -269,8 +271,9 @@ class PerformanceMonitor:
def get_history(self, key: str) -> List[float]:
"""
Returns a snapshot of the full history buffer for a specific metric key.
[C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state]
Returns a snapshot of the full history buffer for a specific metric key.
[C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state, tests/test_history_manager.py:TestHistoryManager.test_get_history_returns_descriptions]
"""
with self._lock:
if key in self._history:
@@ -281,15 +284,16 @@ class PerformanceMonitor:
def scope(self, name: str) -> PerformanceScope:
"""
Returns a context manager for timing a component.
Returns a context manager for timing a component.
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager]
"""
return PerformanceScope(self, name)
def stop(self) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
self._stop_event.set()
if self._cpu_thread.is_alive():
self._cpu_thread.join(timeout=2.0)
self._cpu_thread.join(timeout=2.0)
+8 -7
View File
@@ -13,7 +13,7 @@ class PersonaManager:
def _get_path(self, scope: str) -> Path:
"""
[C: src/tool_presets.py:ToolPresetManager.delete_bias_profile, src/tool_presets.py:ToolPresetManager.delete_preset, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
[C: src/tool_presets.py:ToolPresetManager.delete_bias_profile, src/tool_presets.py:ToolPresetManager.delete_preset, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
"""
if scope == "global":
return paths.get_global_personas_path()
@@ -26,7 +26,8 @@ class PersonaManager:
def load_all(self) -> Dict[str, Persona]:
"""
Merges global and project personas into a single dictionary.
Merges global and project personas into a single dictionary.
[C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
"""
personas = {}
@@ -46,7 +47,7 @@ class PersonaManager:
def save_persona(self, persona: Persona, scope: str = "project") -> None:
"""
[C: tests/test_persona_manager.py:test_save_persona]
[C: tests/test_persona_manager.py:test_save_persona]
"""
path = self._get_path(scope)
data = self._load_file(path)
@@ -73,7 +74,7 @@ class PersonaManager:
def delete_persona(self, name: str, scope: str = "project") -> None:
"""
[C: tests/test_persona_manager.py:test_delete_persona]
[C: tests/test_persona_manager.py:test_delete_persona]
"""
path = self._get_path(scope)
data = self._load_file(path)
@@ -83,7 +84,7 @@ class PersonaManager:
def _load_file(self, path: Path) -> Dict[str, Any]:
"""
[C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.load_all, src/presets.py:PresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.load_all_profiles, src/workspace_manager.py:WorkspaceManager.save_profile]
[C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.load_all, src/presets.py:PresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.load_all_profiles, src/workspace_manager.py:WorkspaceManager.save_profile]
"""
if not path.exists():
return {}
@@ -95,8 +96,8 @@ class PersonaManager:
def _save_file(self, path: Path, data: Dict[str, Any]) -> None:
"""
[C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
[C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
"""
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "wb") as f:
tomli_w.dump(data, f)
tomli_w.dump(data, f)
+7 -5
View File
@@ -19,7 +19,8 @@ class PresetManager:
def load_all(self) -> Dict[str, Preset]:
"""
Merges global and project presets into a single dictionary.
Merges global and project presets into a single dictionary.
[C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
"""
presets: Dict[str, Preset] = {}
@@ -45,7 +46,8 @@ class PresetManager:
def save_preset(self, preset: Preset, scope: str = "project") -> None:
"""
Saves a preset to either the global or project-specific TOML file.
Saves a preset to either the global or project-specific TOML file.
[C: tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_preset_manager.py:test_save_preset_project_no_root, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
"""
path = self.global_path if scope == "global" else self.project_path
@@ -63,7 +65,7 @@ class PresetManager:
def delete_preset(self, name: str, scope: str) -> None:
"""
[C: tests/test_preset_manager.py:test_delete_preset, tests/test_presets.py:TestPresetManager.test_delete_preset]
[C: tests/test_preset_manager.py:test_delete_preset, tests/test_presets.py:TestPresetManager.test_delete_preset]
"""
if scope == "project" and self.project_root:
path = get_project_presets_path(self.project_root)
@@ -92,7 +94,7 @@ class PresetManager:
def _load_file(self, path: Path) -> Dict[str, Any]:
"""
[C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.load_all_profiles, src/workspace_manager.py:WorkspaceManager.save_profile]
[C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.load_all_profiles, src/workspace_manager.py:WorkspaceManager.save_profile]
"""
if not path.exists():
return {"presets": {}}
@@ -110,7 +112,7 @@ class PresetManager:
def _save_file(self, path: Path, data: Dict[str, Any]) -> None:
"""
[C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
[C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
"""
if path.parent.exists() and path.parent.is_file():
raise ValueError(f"Cannot save to {path}: Parent directory {path.parent} is a file.")
+48 -30
View File
@@ -30,7 +30,8 @@ def parse_ts(s: str) -> Optional[datetime.datetime]:
def entry_to_str(entry: dict[str, Any]) -> str:
"""
Serialise a disc entry dict -> stored string.
Serialise a disc entry dict -> stored string.
[C: tests/test_thinking_persistence.py:test_entry_to_str_with_thinking]
"""
ts = entry.get("ts", "")
@@ -50,7 +51,8 @@ def entry_to_str(entry: dict[str, Any]) -> str:
def str_to_entry(raw: str, roles: list[str]) -> dict[str, Any]:
"""
Parse a stored string back to a disc entry dict.
Parse a stored string back to a disc entry dict.
[C: tests/test_thinking_persistence.py:test_str_to_entry_with_thinking]
"""
ts = ""
@@ -91,13 +93,13 @@ def get_git_commit(git_dir: str) -> str:
def default_discussion() -> dict[str, Any]:
"""
[C: tests/test_discussion_takes.py:TestDiscussionTakes.test_promote_take_renames_discussion]
[C: tests/test_discussion_takes.py:TestDiscussionTakes.test_promote_take_renames_discussion]
"""
return {"git_commit": "", "last_updated": now_ts(), "history": []}
def default_project(name: str = "unnamed") -> dict[str, Any]:
"""
[C: tests/test_deepseek_infra.py:test_default_project_includes_reasoning_role, tests/test_discussion_takes.py:TestDiscussionTakes.setUp, tests/test_history_management.py:test_history_persistence_across_turns, tests/test_history_management.py:test_save_separation, tests/test_project_manager_modes.py:test_default_project_execution_mode, tests/test_project_manager_modes.py:test_load_save_execution_mode, tests/test_project_serialization.py:TestProjectSerialization.test_default_roles_include_context, tests/test_project_serialization.py:TestProjectSerialization.test_fileitem_roundtrip]
[C: tests/test_deepseek_infra.py:test_default_project_includes_reasoning_role, tests/test_discussion_takes.py:TestDiscussionTakes.setUp, tests/test_history_management.py:test_history_persistence_across_turns, tests/test_history_management.py:test_save_separation, tests/test_project_manager_modes.py:test_default_project_execution_mode, tests/test_project_manager_modes.py:test_load_save_execution_mode, tests/test_project_serialization.py:TestProjectSerialization.test_default_roles_include_context, tests/test_project_serialization.py:TestProjectSerialization.test_fileitem_roundtrip]
"""
return {
"project": {"name": name, "git_dir": "", "system_prompt": "", "execution_mode": "native"},
@@ -152,7 +154,8 @@ def default_project(name: str = "unnamed") -> dict[str, Any]:
def get_history_path(project_path: Union[str, Path]) -> Path:
"""
Return the Path to the sibling history TOML file for a given project.
Return the Path to the sibling history TOML file for a given project.
[C: tests/test_history_management.py:test_save_separation]
"""
p = Path(project_path)
@@ -161,8 +164,9 @@ def get_history_path(project_path: Union[str, Path]) -> Path:
def load_project(path: Union[str, Path]) -> dict[str, Any]:
"""
Load a project TOML file.
Automatically migrates legacy 'discussion' keys to a sibling history file.
Load a project TOML file.
Automatically migrates legacy 'discussion' keys to a sibling history file.
[C: tests/test_history_management.py:test_history_persistence_across_turns, tests/test_history_management.py:test_migration_on_load, tests/test_project_manager_modes.py:test_load_save_execution_mode, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_project_serialization.py:TestProjectSerialization.test_fileitem_roundtrip]
"""
with open(path, "rb") as f:
@@ -185,7 +189,8 @@ def load_project(path: Union[str, Path]) -> dict[str, Any]:
def load_history(project_path: Union[str, Path]) -> dict[str, Any]:
"""
Load the segregated discussion history from its dedicated TOML file.
Load the segregated discussion history from its dedicated TOML file.
[C: tests/test_thinking_persistence.py:test_save_and_load_history_with_thinking_segments]
"""
hist_path = get_history_path(project_path)
@@ -196,7 +201,8 @@ def load_history(project_path: Union[str, Path]) -> dict[str, Any]:
def clean_nones(data: Any) -> Any:
"""
Recursively remove None values from a dictionary/list.
Recursively remove None values from a dictionary/list.
[C: tests/test_thinking_persistence.py:test_clean_nones_removes_thinking]
"""
if isinstance(data, dict):
@@ -208,8 +214,9 @@ def clean_nones(data: Any) -> Any:
def save_project(proj: dict[str, Any], path: Union[str, Path], disc_data: Optional[dict[str, Any]] = None) -> None:
"""
Save the project TOML.
If 'discussion' is present in proj, it is moved to the sibling history file.
Save the project TOML.
If 'discussion' is present in proj, it is moved to the sibling history file.
[C: tests/test_history_management.py:test_history_persistence_across_turns, tests/test_history_management.py:test_save_separation, tests/test_project_manager_modes.py:test_load_save_execution_mode, tests/test_project_serialization.py:TestProjectSerialization.test_fileitem_roundtrip, tests/test_thinking_persistence.py:test_save_and_load_history_with_thinking_segments]
"""
proj = clean_nones(proj)
@@ -270,7 +277,8 @@ def flat_config(proj: dict[str, Any], disc_name: Optional[str] = None, track_id:
def save_context_preset(project_dict: dict, preset_name: str, files: list[str], screenshots: list[str]) -> None:
"""
Save a named context preset (files + screenshots) into the project dict.
Save a named context preset (files + screenshots) into the project dict.
[C: tests/test_context_presets.py:test_save_context_preset]
"""
if "context_presets" not in project_dict:
@@ -282,7 +290,8 @@ def save_context_preset(project_dict: dict, preset_name: str, files: list[str],
def load_context_preset(project_dict: dict, preset_name: str) -> dict:
"""
Return the files and screenshots for a named preset.
Return the files and screenshots for a named preset.
[C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset]
"""
if "context_presets" not in project_dict or preset_name not in project_dict["context_presets"]:
@@ -291,7 +300,8 @@ def load_context_preset(project_dict: dict, preset_name: str) -> dict:
def delete_context_preset(project_dict: dict, preset_name: str) -> None:
"""
Remove a named preset if it exists.
Remove a named preset if it exists.
[C: tests/test_context_presets.py:test_delete_context_preset, tests/test_context_presets.py:test_delete_nonexistent_preset_no_error]
"""
if "context_presets" in project_dict:
@@ -301,7 +311,8 @@ def delete_context_preset(project_dict: dict, preset_name: str) -> None:
def save_track_state(track_id: str, state: 'TrackState', base_dir: Union[str, Path] = ".") -> None:
"""
Saves a TrackState object to conductor/tracks/<track_id>/state.toml.
Saves a TrackState object to conductor/tracks/<track_id>/state.toml.
[C: tests/test_project_manager_tracks.py:test_get_all_tracks_with_state, tests/test_track_state_persistence.py:test_track_state_persistence]
"""
track_dir = paths.get_track_state_dir(track_id, project_path=str(base_dir))
@@ -314,7 +325,8 @@ def save_track_state(track_id: str, state: 'TrackState', base_dir: Union[str, Pa
def load_track_state(track_id: str, base_dir: Union[str, Path] = ".") -> Optional['TrackState']:
"""
Loads a TrackState object from conductor/tracks/<track_id>/state.toml.
Loads a TrackState object from conductor/tracks/<track_id>/state.toml.
[C: tests/test_track_state_persistence.py:test_track_state_persistence]
"""
from src.models import TrackState
@@ -328,8 +340,9 @@ def load_track_state(track_id: str, base_dir: Union[str, Path] = ".") -> Optiona
def load_track_history(track_id: str, base_dir: Union[str, Path] = ".") -> list[str]:
"""
Loads the discussion history for a specific track from its state.toml.
Returns a list of entry strings formatted with @timestamp.
Loads the discussion history for a specific track from its state.toml.
Returns a list of entry strings formatted with @timestamp.
"""
state = load_track_state(track_id, base_dir)
if not state:
@@ -346,8 +359,9 @@ def load_track_history(track_id: str, base_dir: Union[str, Path] = ".") -> list[
def save_track_history(track_id: str, history: list[str], base_dir: Union[str, Path] = ".") -> None:
"""
Saves the discussion history for a specific track to its state.toml.
'history' is expected to be a list of formatted strings.
Saves the discussion history for a specific track to its state.toml.
'history' is expected to be a list of formatted strings.
"""
state = load_track_state(track_id, base_dir)
if not state:
@@ -360,11 +374,12 @@ def save_track_history(track_id: str, history: list[str], base_dir: Union[str, P
def get_all_tracks(base_dir: Union[str, Path] = ".") -> list[dict[str, Any]]:
"""
Scans the conductor/tracks/ directory and returns a list of dictionaries
containing track metadata: 'id', 'title', 'status', 'complete', 'total',
and 'progress' (0.0 to 1.0).
Handles missing or malformed metadata.json or state.toml by falling back
to available info or defaults.
Scans the conductor/tracks/ directory and returns a list of dictionaries
containing track metadata: 'id', 'title', 'status', 'complete', 'total',
and 'progress' (0.0 to 1.0).
Handles missing or malformed metadata.json or state.toml by falling back
to available info or defaults.
[C: tests/test_project_manager_tracks.py:test_get_all_tracks_empty, tests/test_project_manager_tracks.py:test_get_all_tracks_malformed, tests/test_project_manager_tracks.py:test_get_all_tracks_with_metadata_json, tests/test_project_manager_tracks.py:test_get_all_tracks_with_state, tests/test_project_paths.py:test_get_all_tracks_project_specific]
"""
tracks_dir = paths.get_tracks_dir(project_path=str(base_dir))
@@ -428,8 +443,9 @@ def get_all_tracks(base_dir: Union[str, Path] = ".") -> list[dict[str, Any]]:
def calculate_track_progress(tickets: list) -> dict:
"""
Calculates track progress based on ticket statuses.
percentage (float), completed (int), total (int), in_progress (int), blocked (int), todo (int)
Calculates track progress based on ticket statuses.
percentage (float), completed (int), total (int), in_progress (int), blocked (int), todo (int)
[C: tests/test_progress_viz.py:test_calculate_track_progress_all_completed, tests/test_progress_viz.py:test_calculate_track_progress_all_todo, tests/test_progress_viz.py:test_calculate_track_progress_empty, tests/test_progress_viz.py:test_calculate_track_progress_mixed]
"""
total = len(tickets)
@@ -463,8 +479,9 @@ def calculate_track_progress(tickets: list) -> dict:
def branch_discussion(project_dict: dict, source_id: str, new_id: str, message_index: int) -> None:
"""
Creates a new discussion in project_dict['discussion']['discussions'] by copying
the history from source_id up to (and including) message_index, and sets active to new_id.
Creates a new discussion in project_dict['discussion']['discussions'] by copying
the history from source_id up to (and including) message_index, and sets active to new_id.
[C: tests/test_discussion_takes.py:TestDiscussionTakes.test_branch_discussion_creates_new_take]
"""
if "discussion" not in project_dict or "discussions" not in project_dict["discussion"]:
@@ -483,7 +500,8 @@ def branch_discussion(project_dict: dict, source_id: str, new_id: str, message_i
def promote_take(project_dict: dict, take_id: str, new_id: str) -> None:
"""
Renames a take_id to new_id in the discussions dict.
Renames a take_id to new_id in the discussions dict.
[C: tests/test_discussion_takes.py:TestDiscussionTakes.test_promote_take_renames_discussion]
"""
if "discussion" not in project_dict or "discussions" not in project_dict["discussion"]:
+3 -3
View File
@@ -108,7 +108,7 @@ class RAGEngine:
def add_documents(self, ids: List[str], texts: List[str], metadatas: Optional[List[Dict[str, Any]]] = None):
"""
[C: tests/test_rag_engine.py:test_rag_engine_chroma]
[C: tests/test_rag_engine.py:test_rag_engine_chroma]
"""
if not self.config.enabled or self.collection == "mock":
return
@@ -215,7 +215,7 @@ class RAGEngine:
def search(self, query: str, top_k: int = 5) -> List[Dict[str, Any]]:
"""
[C: tests/mock_concurrent_mma.py:main, tests/test_rag_engine.py:test_rag_engine_chroma]
[C: tests/mock_concurrent_mma.py:main, tests/test_rag_engine.py:test_rag_engine_chroma]
"""
if not self.config.enabled:
return []
@@ -243,7 +243,7 @@ class RAGEngine:
def delete_documents(self, ids: List[str]):
"""
[C: tests/test_rag_engine.py:test_rag_engine_chroma]
[C: tests/test_rag_engine.py:test_rag_engine_chroma]
"""
if not self.config.enabled or self.collection == "mock":
return
+15 -10
View File
@@ -44,8 +44,9 @@ def _now_ts() -> str:
def open_session(label: Optional[str] = None) -> None:
"""
Called once at GUI startup. Creates the log directories if needed and
opens the log files for this session within a sub-directory.
Called once at GUI startup. Creates the log directories if needed and
opens the log files for this session within a sub-directory.
[C: tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_logging_e2e.py:test_logging_e2e, tests/test_session_logger_optimization.py:test_log_tool_call_saves_in_session_scripts, tests/test_session_logger_optimization.py:test_log_tool_output_saves_in_session_outputs, tests/test_session_logger_optimization.py:test_session_directory_and_subdirectories_creation, tests/test_session_logger_reset.py:test_reset_session, tests/test_session_logging.py:test_open_session_creates_subdir_and_registry]
"""
global _ts, _session_id, _session_dir, _comms_fh, _tool_fh, _api_fh, _cli_fh, _seq, _output_seq
@@ -89,7 +90,8 @@ def open_session(label: Optional[str] = None) -> None:
def close_session() -> None:
"""
Flush and close all log files. Called on clean exit.
Flush and close all log files. Called on clean exit.
[C: tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_logging_e2e.py:e2e_setup, tests/test_logging_e2e.py:test_logging_e2e, tests/test_session_logger_optimization.py:temp_session_setup, tests/test_session_logger_reset.py:temp_logs, tests/test_session_logging.py:temp_logs]
"""
global _comms_fh, _tool_fh, _api_fh, _cli_fh, _session_id
@@ -136,8 +138,9 @@ def log_api_hook(method: str, path: str, payload: str) -> None:
def log_comms(entry: dict[str, Any]) -> None:
"""
Append one comms entry to the comms log file as a JSON-L line.
Thread-safe (GIL + line-buffered file).
Append one comms entry to the comms log file as a JSON-L line.
Thread-safe (GIL + line-buffered file).
[C: tests/test_logging_e2e.py:test_logging_e2e]
"""
if _comms_fh is None:
@@ -150,8 +153,9 @@ def log_comms(entry: dict[str, Any]) -> None:
def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optional[str]:
"""
Append a tool-call record to the toolcalls log and write the PS1 script to
the session's scripts directory. Returns the path of the written script file.
Append a tool-call record to the toolcalls log and write the PS1 script to
the session's scripts directory. Returns the path of the written script file.
[C: tests/test_session_logger_optimization.py:test_log_tool_call_saves_in_session_scripts]
"""
global _seq
@@ -194,8 +198,9 @@ def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optio
def log_tool_output(content: str) -> Optional[str]:
"""
Save tool output content to a unique file in the session's outputs directory.
Returns the path of the written file.
Save tool output content to a unique file in the session's outputs directory.
Returns the path of the written file.
[C: tests/test_session_logger_optimization.py:test_log_tool_output_returns_none_if_no_session, tests/test_session_logger_optimization.py:test_log_tool_output_saves_in_session_outputs]
"""
global _output_seq
@@ -232,4 +237,4 @@ def log_cli_call(command: str, stdin_content: Optional[str], stdout_content: Opt
_cli_fh.write(json.dumps(log_data, ensure_ascii=False, default=str) + "\n")
_cli_fh.flush()
except Exception:
pass
pass
+4 -3
View File
@@ -3,8 +3,9 @@ from imgui_bundle import imgui
def draw_soft_shadow(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p_max: imgui.ImVec2, color: imgui.ImVec4, shadow_size: float = 10.0, rounding: float = 0.0) -> None:
"""
Simulates a soft shadow effect by drawing multiple concentric rounded rectangles
with decreasing alpha values. This is a faux-shader effect using primitive batching.
Simulates a soft shadow effect by drawing multiple concentric rounded rectangles
with decreasing alpha values. This is a faux-shader effect using primitive batching.
"""
r, g, b, a = color.x, color.y, color.z, color.w
steps = int(shadow_size)
@@ -35,4 +36,4 @@ def draw_soft_shadow(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p_max: im
rounding + expand if rounding > 0 else 0.0,
flags=imgui.ImDrawFlags_.round_corners_all if rounding > 0 else imgui.ImDrawFlags_.none,
thickness=1.0
)
)
+8 -7
View File
@@ -54,12 +54,13 @@ def _build_subprocess_env() -> dict[str, str]:
def run_powershell(script: str, base_dir: str, qa_callback: Optional[Callable[[str], str]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
"""
Run a PowerShell script with working directory set to base_dir.
Returns a string combining stdout, stderr, and exit code.
Environment is configured via mcp_env.toml (project root).
If qa_callback is provided and the command fails or has stderr,
the callback is called with the stderr content and its result is appended.
If patch_callback is provided, it receives (error, file_context) and returns patch text.
Run a PowerShell script with working directory set to base_dir.
Returns a string combining stdout, stderr, and exit code.
Environment is configured via mcp_env.toml (project root).
If qa_callback is provided and the command fails or has stderr,
the callback is called with the stderr content and its result is appended.
If patch_callback is provided, it receives (error, file_context) and returns patch text.
[C: tests/test_tier4_interceptor.py:test_run_powershell_no_qa_callback_on_success, tests/test_tier4_interceptor.py:test_run_powershell_optional_qa_callback, tests/test_tier4_interceptor.py:test_run_powershell_qa_callback_on_failure, tests/test_tier4_interceptor.py:test_run_powershell_qa_callback_on_stderr_only]
"""
safe_dir: str = str(base_dir).replace("'", "''")
@@ -98,4 +99,4 @@ def run_powershell(script: str, base_dir: str, qa_callback: Optional[Callable[[s
except Exception as e:
if 'process' in locals() and process:
subprocess.run(["taskkill", "/F", "/T", "/PID", str(process.pid)], capture_output=True)
return f"ERROR: {e}"
return f"ERROR: {e}"
+10 -7
View File
@@ -154,8 +154,9 @@ _SUMMARISERS: dict[str, Callable[[Path, str], str]] = {
def summarise_file(path: Path, content: str) -> str:
"""
Return a compact markdown summary string for a single file.
`content` is the already-read file text (or an error string).
Return a compact markdown summary string for a single file.
`content` is the already-read file text (or an error string).
[C: tests/test_subagent_summarization.py:test_summarise_file_integration]
"""
content_hash = get_file_hash(content)
@@ -193,8 +194,9 @@ def summarise_file(path: Path, content: str) -> str:
def summarise_items(file_items: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""
Given a list of file_item dicts (as returned by aggregate.build_file_items),
return a parallel list of dicts with an added `summary` key.
Given a list of file_item dicts (as returned by aggregate.build_file_items),
return a parallel list of dicts with an added `summary` key.
"""
result = []
for item in file_items:
@@ -212,8 +214,9 @@ def summarise_items(file_items: list[dict[str, Any]]) -> list[dict[str, Any]]:
def build_summary_markdown(file_items: list[dict[str, Any]]) -> str:
"""
Build a compact markdown string of file summaries, suitable for the
initial <context> block instead of full file contents.
Build a compact markdown string of file summaries, suitable for the
initial <context> block instead of full file contents.
"""
summarised = summarise_items(file_items)
parts = []
@@ -221,4 +224,4 @@ def build_summary_markdown(file_items: list[dict[str, Any]]) -> str:
path = item.get("path") or item.get("entry", "unknown")
summary = item.get("summary", "")
parts.append(f"### `{path}`\n\n{summary}")
return "\n\n---\n\n".join(parts)
return "\n\n---\n\n".join(parts)
+14 -8
View File
@@ -5,7 +5,8 @@ from typing import Optional, Dict
def get_file_hash(content: str) -> str:
"""
Returns SHA256 hash of the content.
Returns SHA256 hash of the content.
[C: tests/test_summary_cache.py:test_get_file_hash, tests/test_summary_cache.py:test_summary_cache]
"""
return hashlib.sha256(content.encode("utf-8")).hexdigest()
@@ -13,8 +14,9 @@ def get_file_hash(content: str) -> str:
class SummaryCache:
"""
A hash-based cache for file summaries to avoid redundant processing.
Invalidates when content hash changes.
A hash-based cache for file summaries to avoid redundant processing.
Invalidates when content hash changes.
"""
def __init__(self, cache_file: Optional[str] = None, max_entries: int = 1000):
if cache_file:
@@ -28,8 +30,9 @@ class SummaryCache:
def load(self) -> None:
"""
Loads cache from disk.
[C: src/tool_presets.py:ToolPresetManager._read_raw, src/workspace_manager.py:WorkspaceManager._load_file, tests/test_gui_phase3.py:test_create_track, tests/test_history_management.py:test_save_separation, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_session_logging.py:test_open_session_creates_subdir_and_registry, tests/test_visual_sim_gui_ux.py:test_gui_track_creation]
Loads cache from disk.
[C: src/tool_presets.py:ToolPresetManager._read_raw, src/workspace_manager.py:WorkspaceManager._load_file, tests/test_gui_phase3.py:test_create_track, tests/test_history_management.py:test_save_separation, tests/test_session_logging.py:test_open_session_creates_subdir_and_registry]
"""
if self.cache_file.exists():
try:
@@ -49,7 +52,8 @@ class SummaryCache:
def get_summary(self, file_path: str, content_hash: str) -> Optional[str]:
"""
Returns cached summary if hash matches, otherwise None.
Returns cached summary if hash matches, otherwise None.
[C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru]
"""
entry = self.cache.get(file_path)
@@ -62,7 +66,8 @@ class SummaryCache:
def set_summary(self, file_path: str, content_hash: str, summary: str) -> None:
"""
Stores summary in cache and saves to disk.
Stores summary in cache and saves to disk.
[C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru]
"""
if file_path in self.cache:
@@ -80,7 +85,8 @@ class SummaryCache:
def clear(self) -> None:
"""
Clears the cache both in-memory and on disk.
Clears the cache both in-memory and on disk.
[C: tests/conftest.py:reset_ai_client]
"""
self.cache.clear()
+1 -1
View File
@@ -1,6 +1,6 @@
def format_takes_diff(takes: dict[str, list[dict]]) -> str:
"""
[C: tests/test_synthesis_formatter.py:test_format_takes_diff_common_prefix, tests/test_synthesis_formatter.py:test_format_takes_diff_empty, tests/test_synthesis_formatter.py:test_format_takes_diff_no_common_prefix, tests/test_synthesis_formatter.py:test_format_takes_diff_single_take]
[C: tests/test_synthesis_formatter.py:test_format_takes_diff_common_prefix, tests/test_synthesis_formatter.py:test_format_takes_diff_empty, tests/test_synthesis_formatter.py:test_format_takes_diff_no_common_prefix, tests/test_synthesis_formatter.py:test_format_takes_diff_single_take]
"""
if not takes:
return ""
+6 -3
View File
@@ -23,7 +23,8 @@ from src.theme_nerv_fx import CRTFilter, AlertPulsing, StatusFlicker
def _c(r: int, g: int, b: int, a: int = 255) -> tuple[float, float, float, float]:
"""
Convert 0-255 RGBA to 0.0-1.0 floats.
Convert 0-255 RGBA to 0.0-1.0 floats.
[C: src/theme_nerv.py:module]
"""
return (r / 255.0, g / 255.0, b / 255.0, a / 255.0)
@@ -283,7 +284,9 @@ def set_child_transparency(val: float) -> None:
def apply(palette_name: str) -> None:
"""
Apply a named palette by setting all ImGui style colors and applying global professional styling.
Apply a named palette by setting all ImGui style colors and applying global professional styling.
[C: tests/test_theme.py:test_theme_apply_sets_rounding_and_padding]
"""
global _current_palette
_current_palette = palette_name
@@ -420,4 +423,4 @@ def render_post_fx(width: float, height: float, ai_status: str, crt_enabled: boo
_alert_pulsing.update(ai_status)
_alert_pulsing.render(width, height)
_crt_filter.enabled = crt_enabled
_crt_filter.render(width, height)
_crt_filter.render(width, height)
+3 -2
View File
@@ -63,7 +63,8 @@ NERV_PALETTE = {
def apply_nerv() -> None:
"""
Apply NERV theme with hard edges and specific palette.
Apply NERV theme with hard edges and specific palette.
[C: tests/test_theme_nerv.py:test_apply_nerv_sets_rounding_and_colors]
"""
style = imgui.get_style()
@@ -84,4 +85,4 @@ def apply_nerv() -> None:
style.frame_border_size = 1.0
style.popup_border_size = 1.0
style.child_border_size = 1.0
style.tab_border_size = 1.0
style.tab_border_size = 1.0
+5 -5
View File
@@ -9,7 +9,7 @@ class CRTFilter:
def render(self, width: float, height: float):
"""
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
"""
if not self.enabled:
return
@@ -68,7 +68,7 @@ class StatusFlicker:
def get_alpha(self) -> float:
# Modulate between 0.7 and 1.0 using sin wave
"""
[C: tests/test_theme_nerv_fx.py:TestThemeNervFx.test_status_flicker_get_alpha]
[C: tests/test_theme_nerv_fx.py:TestThemeNervFx.test_status_flicker_get_alpha]
"""
return 0.85 + 0.15 * math.sin(time.time() * 20.0)
@@ -78,13 +78,13 @@ class AlertPulsing:
def update(self, status: str):
"""
[C: tests/test_spawn_interception_v2.py:MockDialog.wait, tests/test_theme_nerv_alert.py:test_alert_pulsing_update, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_update]
[C: tests/test_spawn_interception_v2.py:MockDialog.wait, tests/test_theme_nerv_alert.py:test_alert_pulsing_update, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_update]
"""
self.active = status.lower().startswith("error")
def render(self, width: float, height: float):
"""
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
[C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render]
"""
if not self.active:
return
@@ -94,4 +94,4 @@ class AlertPulsing:
# multiply by (0.2 - 0.05) = 0.15 and add 0.05
alpha = 0.05 + 0.15 * ((math.sin(time.time() * 4.0) + 1.0) / 2.0)
color = imgui.get_color_u32((1.0, 0.0, 0.0, alpha))
draw_list.add_rect((0.0, 0.0), (width, height), color, 0.0, 0, 10.0)
draw_list.add_rect((0.0, 0.0), (width, height), color, 0.0, 0, 10.0)
+4 -3
View File
@@ -5,9 +5,10 @@ from src.models import ThinkingSegment
def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]:
"""
Parses thinking segments from text and returns (segments, response_content).
Support extraction of thinking traces from <thinking>...</thinking>, <thought>...</thought>,
and blocks prefixed with Thinking:.
Parses thinking segments from text and returns (segments, response_content).
Support extraction of thinking traces from <thinking>...</thinking>, <thought>...</thought>,
and blocks prefixed with Thinking:.
[C: tests/test_thinking_trace.py:test_parse_empty_response, tests/test_thinking_trace.py:test_parse_multiple_markers, tests/test_thinking_trace.py:test_parse_no_thinking, tests/test_thinking_trace.py:test_parse_text_thinking_prefix, tests/test_thinking_trace.py:test_parse_thinking_with_empty_response, tests/test_thinking_trace.py:test_parse_xml_thinking_tag, tests/test_thinking_trace.py:test_parse_xml_thought_tag]
"""
segments = []
+3 -3
View File
@@ -4,7 +4,7 @@ from src.models import Tool, ToolPreset, BiasProfile
class ToolBiasEngine:
def apply_semantic_nudges(self, tool_definitions: List[Dict[str, Any]], preset: ToolPreset) -> List[Dict[str, Any]]:
"""
[C: tests/test_tool_bias.py:test_apply_semantic_nudges, tests/test_tool_bias.py:test_parameter_bias_nudging]
[C: tests/test_tool_bias.py:test_apply_semantic_nudges, tests/test_tool_bias.py:test_parameter_bias_nudging]
"""
weight_map = {
5: "[HIGH PRIORITY] ",
@@ -40,7 +40,7 @@ class ToolBiasEngine:
def generate_tooling_strategy(self, preset: ToolPreset, global_bias: BiasProfile) -> str:
"""
[C: tests/test_tool_bias.py:test_generate_tooling_strategy]
[C: tests/test_tool_bias.py:test_generate_tooling_strategy]
"""
lines = ["### Tooling Strategy"]
@@ -68,4 +68,4 @@ class ToolBiasEngine:
for cat, mult in global_bias.category_multipliers.items():
lines.append(f"- {cat}: {mult}x")
return "\n\n".join(lines)
return "\n\n".join(lines)
+10 -9
View File
@@ -11,7 +11,7 @@ class ToolPresetManager:
def _get_path(self, scope: str) -> Path:
"""
[C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
[C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile]
"""
if scope == "global":
return paths.get_global_tool_presets_path()
@@ -38,7 +38,7 @@ class ToolPresetManager:
def load_all_presets(self) -> Dict[str, ToolPreset]:
"""
[C: tests/test_tool_preset_manager.py:test_load_all_presets_merged]
[C: tests/test_tool_preset_manager.py:test_load_all_presets_merged]
"""
global_path = paths.get_global_tool_presets_path()
global_data = self._read_raw(global_path).get("presets", {})
@@ -59,14 +59,15 @@ class ToolPresetManager:
def load_all(self) -> Dict[str, ToolPreset]:
"""
Backward compatibility for load_all().
Backward compatibility for load_all().
[C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
"""
return self.load_all_presets()
def save_preset(self, preset: ToolPreset, scope: str = "project") -> None:
"""
[C: tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_preset_manager.py:test_save_preset_project_no_root, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
[C: tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_preset_manager.py:test_save_preset_project_no_root, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project]
"""
path = self._get_path(scope)
data = self._read_raw(path)
@@ -77,7 +78,7 @@ class ToolPresetManager:
def delete_preset(self, name: str, scope: str = "project") -> None:
"""
[C: tests/test_preset_manager.py:test_delete_preset, tests/test_presets.py:TestPresetManager.test_delete_preset]
[C: tests/test_preset_manager.py:test_delete_preset, tests/test_presets.py:TestPresetManager.test_delete_preset]
"""
path = self._get_path(scope)
data = self._read_raw(path)
@@ -87,7 +88,7 @@ class ToolPresetManager:
def load_all_bias_profiles(self) -> Dict[str, BiasProfile]:
"""
[C: tests/test_tool_preset_manager.py:test_bias_profiles_merged, tests/test_tool_preset_manager.py:test_delete_bias_profile, tests/test_tool_preset_manager.py:test_save_bias_profile]
[C: tests/test_tool_preset_manager.py:test_bias_profiles_merged, tests/test_tool_preset_manager.py:test_delete_bias_profile, tests/test_tool_preset_manager.py:test_save_bias_profile]
"""
global_path = paths.get_global_tool_presets_path()
global_data = self._read_raw(global_path).get("bias_profiles", {})
@@ -114,7 +115,7 @@ class ToolPresetManager:
def save_bias_profile(self, profile: BiasProfile, scope: str = "project") -> None:
"""
[C: tests/test_tool_preset_manager.py:test_save_bias_profile]
[C: tests/test_tool_preset_manager.py:test_save_bias_profile]
"""
path = self._get_path(scope)
data = self._read_raw(path)
@@ -125,10 +126,10 @@ class ToolPresetManager:
def delete_bias_profile(self, name: str, scope: str = "project") -> None:
"""
[C: tests/test_tool_preset_manager.py:test_delete_bias_profile]
[C: tests/test_tool_preset_manager.py:test_delete_bias_profile]
"""
path = self._get_path(scope)
data = self._read_raw(path)
if "bias_profiles" in data and name in data["bias_profiles"]:
del data["bias_profiles"][name]
self._write_raw(path, data)
self._write_raw(path, data)
+4 -3
View File
@@ -26,7 +26,8 @@ class WorkspaceManager:
def load_all_profiles(self) -> Dict[str, WorkspaceProfile]:
"""
Merges global and project profiles into a single dictionary.
Merges global and project profiles into a single dictionary.
[C: tests/test_workspace_manager.py:test_delete_profile, tests/test_workspace_manager.py:test_load_all_profiles_merged, tests/test_workspace_manager.py:test_save_profile_global_and_project]
"""
profiles = {}
@@ -46,7 +47,7 @@ class WorkspaceManager:
def save_profile(self, profile: WorkspaceProfile, scope: str = "project") -> None:
"""
[C: tests/test_workspace_manager.py:test_delete_profile, tests/test_workspace_manager.py:test_save_profile_global_and_project]
[C: tests/test_workspace_manager.py:test_delete_profile, tests/test_workspace_manager.py:test_save_profile_global_and_project]
"""
path = self._get_path(scope)
data = self._load_file(path)
@@ -58,7 +59,7 @@ class WorkspaceManager:
def delete_profile(self, name: str, scope: str = "project") -> None:
"""
[C: tests/test_workspace_manager.py:test_delete_profile]
[C: tests/test_workspace_manager.py:test_delete_profile]
"""
path = self._get_path(scope)
data = self._load_file(path)