diff --git a/.gitignore b/.gitignore index 4a87365..f01975f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ dpg_layout.ini .env .coverage tests/temp_workspace +.mypy_cache diff --git a/aggregate.py b/aggregate.py index 619330c..62f9805 100644 --- a/aggregate.py +++ b/aggregate.py @@ -256,7 +256,7 @@ def build_tier3_context(file_items: list[dict[str, Any]], screenshot_base_dir: P parser = ASTParser("python") skeleton = parser.get_skeleton(content) sections.append(f"### `{entry or path_str}` (AST Skeleton)\n\n```python\n{skeleton}\n```") - except Exception as e: + except Exception: # Fallback to summary if AST parsing fails sections.append(f"### `{entry or path_str}`\n\n" + summarize.summarise_file(path, content)) else: diff --git a/ai_client.py b/ai_client.py index ff47105..a84db52 100644 --- a/ai_client.py +++ b/ai_client.py @@ -21,7 +21,6 @@ import hashlib import difflib import threading import requests -from pathlib import Path from typing import Optional, Callable, Any import os import project_manager @@ -1416,7 +1415,7 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_item def _ensure_deepseek_client() -> None: global _deepseek_client if _deepseek_client is None: - creds = _load_credentials() + _load_credentials() # Placeholder for Dedicated DeepSeek SDK instantiation # import deepseek # _deepseek_client = deepseek.DeepSeek(api_key=creds["deepseek"]["api_key"]) @@ -1672,7 +1671,6 @@ def run_tier4_analysis(stderr: str) -> str: return f"[QA ANALYSIS FAILED] {e}" # ------------------------------------------------------------------ unified send -import json def send( @@ -1737,7 +1735,7 @@ def get_history_bleed_stats(md_content: str | None = None) -> dict[str, Any]: # For Anthropic, we have a robust estimator with _anthropic_history_lock: history_snapshot = list(_anthropic_history) - hist_only = _estimate_prompt_tokens([], history_snapshot) - 2500 # subtract fixed tools + _estimate_prompt_tokens([], history_snapshot) - 2500 # subtract fixed tools sys_tok = max(1, int(len(md_content) / _CHARS_PER_TOKEN)) if md_content else 0 current_tokens = _estimate_prompt_tokens([], history_snapshot) if md_content: @@ -1784,7 +1782,7 @@ def get_history_bleed_stats(md_content: str | None = None) -> dict[str, Any]: "current": current_tokens, "percentage": percentage, }, sys_tok=0, tool_tok=0) - except Exception as e: + except Exception: pass elif md_content: try: @@ -1801,7 +1799,7 @@ def get_history_bleed_stats(md_content: str | None = None) -> dict[str, Any]: "current": current_tokens, "percentage": percentage, }) - except Exception as e: + except Exception: pass return _add_bleed_derived({ "provider": "gemini", diff --git a/cleanup_ai_client.py b/cleanup_ai_client.py deleted file mode 100644 index 2d86fb3..0000000 --- a/cleanup_ai_client.py +++ /dev/null @@ -1,583 +0,0 @@ - -import os - -path = 'ai_client.py' -with open(path, 'r', encoding='utf-8') as f: - lines = f.readlines() - -# Very basic cleanup: remove lines after the first 'def get_history_bleed_stats' -# or other markers of duplication if they exist. -# Actually, I'll just rewrite the relevant functions and clean up the end of the file. - -new_lines = [] -skip = False -for line in lines: - if 'def _send_gemini(' in line and 'stream_callback' in line: - # This is my partially applied change, I'll keep it but fix it. - pass - if 'def send(' in line and 'import json' in lines[lines.index(line)-1]: - # This looks like the duplicated send at the end - skip = True - if not skip: - new_lines.append(line) - if skip and 'return {' in line and 'percentage' in line: - # End of duplicated get_history_bleed_stats - # skip = False # actually just keep skipping till the end - pass - -# It's better to just surgically fix the file content in memory. -content = "".join(new_lines) - -# I'll use a more robust approach: I'll define the final versions of the functions I want to change. - -_SEND_GEMINI_NEW = '''def _send_gemini(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], bool]] = None, - qa_callback: Optional[Callable[[str], str]] = None, - enable_tools: bool = True, - stream_callback: Optional[Callable[[str], None]] = None) -> str: - global _gemini_chat, _gemini_cache, _gemini_cache_md_hash, _gemini_cache_created_at - try: - _ensure_gemini_client(); mcp_client.configure(file_items or [], [base_dir]) - # Only stable content (files + screenshots) goes in the cached system instruction. - # Discussion history is sent as conversation messages so the cache isn't invalidated every turn. - sys_instr = f"{_get_combined_system_prompt()} - - -{md_content} -" - td = _gemini_tool_declaration() if enable_tools else None - tools_decl = [td] if td else None - # DYNAMIC CONTEXT: Check if files/context changed mid-session - current_md_hash = hashlib.md5(md_content.encode()).hexdigest() - old_history = None - if _gemini_chat and _gemini_cache_md_hash != current_md_hash: - old_history = list(_get_gemini_history_list(_gemini_chat)) if _get_gemini_history_list(_gemini_chat) else [] - if _gemini_cache: - try: _gemini_client.caches.delete(name=_gemini_cache.name) - except Exception as e: _append_comms("OUT", "request", {"message": f"[CACHE DELETE WARN] {e}"}) - _gemini_chat = None - _gemini_cache = None - _gemini_cache_created_at = None - _append_comms("OUT", "request", {"message": "[CONTEXT CHANGED] Rebuilding cache and chat session..."}) - if _gemini_chat and _gemini_cache and _gemini_cache_created_at: - elapsed = time.time() - _gemini_cache_created_at - if elapsed > _GEMINI_CACHE_TTL * 0.9: - old_history = list(_get_gemini_history_list(_gemini_chat)) if _get_gemini_history_list(_get_gemini_history_list(_gemini_chat)) else [] - try: _gemini_client.caches.delete(name=_gemini_cache.name) - except Exception as e: _append_comms("OUT", "request", {"message": f"[CACHE DELETE WARN] {e}"}) - _gemini_chat = None - _gemini_cache = None - _gemini_cache_created_at = None - _append_comms("OUT", "request", {"message": f"[CACHE TTL] Rebuilding cache (expired after {int(elapsed)}s)..."}) - if not _gemini_chat: - chat_config = types.GenerateContentConfig( - system_instruction=sys_instr, - tools=tools_decl, - temperature=_temperature, - max_output_tokens=_max_tokens, - safety_settings=[types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_ONLY_HIGH")] - ) - should_cache = False - try: - count_resp = _gemini_client.models.count_tokens(model=_model, contents=[sys_instr]) - if count_resp.total_tokens >= 2048: - should_cache = True - else: - _append_comms("OUT", "request", {"message": f"[CACHING SKIPPED] Context too small ({count_resp.total_tokens} tokens < 2048)"}) - except Exception as e: - _append_comms("OUT", "request", {"message": f"[COUNT FAILED] {e}"}) - if should_cache: - try: - _gemini_cache = _gemini_client.caches.create( - model=_model, - config=types.CreateCachedContentConfig( - system_instruction=sys_instr, - tools=tools_decl, - ttl=f"{_GEMINI_CACHE_TTL}s", - ) - ) - _gemini_cache_created_at = time.time() - chat_config = types.GenerateContentConfig( - cached_content=_gemini_cache.name, - temperature=_temperature, - max_output_tokens=_max_tokens, - safety_settings=[types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_ONLY_HIGH")] - ) - _append_comms("OUT", "request", {"message": f"[CACHE CREATED] {_gemini_cache.name}"}) - except Exception as e: - _gemini_cache = None - _gemini_cache_created_at = None - _append_comms("OUT", "request", {"message": f"[CACHE FAILED] {type(e).__name__}: {e} \u2014 falling back to inline system_instruction"}) - kwargs = {"model": _model, "config": chat_config} - if old_history: - kwargs["history"] = old_history - _gemini_chat = _gemini_client.chats.create(**kwargs) - _gemini_cache_md_hash = current_md_hash - if discussion_history and not old_history: - _gemini_chat.send_message(f"[DISCUSSION HISTORY] - -{discussion_history}") - _append_comms("OUT", "request", {"message": f"[HISTORY INJECTED] {len(discussion_history)} chars"}) - _append_comms("OUT", "request", {"message": f"[ctx {len(md_content)} + msg {len(user_message)}]"}) - payload: str | list[types.Part] = user_message - all_text: list[str] = [] - _cumulative_tool_bytes = 0 - if _gemini_chat and _get_gemini_history_list(_gemini_chat): - for msg in _get_gemini_history_list(_gemini_chat): - if msg.role == "user" and hasattr(msg, "parts"): - for p in msg.parts: - if hasattr(p, "function_response") and p.function_response and hasattr(p.function_response, "response"): - r = p.function_response.response - if isinstance(r, dict) and "output" in r: - val = r["output"] - if isinstance(val, str): - if "[SYSTEM: FILES UPDATED]" in val: - val = val.split("[SYSTEM: FILES UPDATED]")[0].strip() - if _history_trunc_limit > 0 and len(val) > _history_trunc_limit: - val = val[:_history_trunc_limit] + " - -... [TRUNCATED BY SYSTEM TO SAVE TOKENS.]" - r["output"] = val - for r_idx in range(MAX_TOOL_ROUNDS + 2): - events.emit("request_start", payload={"provider": "gemini", "model": _model, "round": r_idx}) - if stream_callback: - resp = _gemini_chat.send_message_stream(payload) - txt_chunks = [] - for chunk in resp: - c_txt = chunk.text - if c_txt: - txt_chunks.append(c_txt) - stream_callback(c_txt) - txt = "".join(txt_chunks) - calls = [p.function_call for c in resp.candidates if getattr(c, "content", None) for p in c.content.parts if hasattr(p, "function_call") and p.function_call] - usage = {"input_tokens": getattr(resp.usage_metadata, "prompt_token_count", 0), "output_tokens": getattr(resp.usage_metadata, "candidates_token_count", 0)} - cached_tokens = getattr(resp.usage_metadata, "cached_content_token_count", None) - if cached_tokens: usage["cache_read_input_tokens"] = cached_tokens - else: - resp = _gemini_chat.send_message(payload) - txt = " -".join(p.text for c in resp.candidates if getattr(c, "content", None) for p in c.content.parts if hasattr(p, "text") and p.text) - calls = [p.function_call for c in resp.candidates if getattr(c, "content", None) for p in c.content.parts if hasattr(p, "function_call") and p.function_call] - usage = {"input_tokens": getattr(resp.usage_metadata, "prompt_token_count", 0), "output_tokens": getattr(resp.usage_metadata, "candidates_token_count", 0)} - cached_tokens = getattr(resp.usage_metadata, "cached_content_token_count", None) - if cached_tokens: usage["cache_read_input_tokens"] = cached_tokens - if txt: all_text.append(txt) - events.emit("response_received", payload={"provider": "gemini", "model": _model, "usage": usage, "round": r_idx}) - reason = resp.candidates[0].finish_reason.name if resp.candidates and hasattr(resp.candidates[0], "finish_reason") else "STOP" - _append_comms("IN", "response", {"round": r_idx, "stop_reason": reason, "text": txt, "tool_calls": [{"name": c.name, "args": dict(c.args)} for c in calls], "usage": usage}) - total_in = usage.get("input_tokens", 0) - if total_in > _GEMINI_MAX_INPUT_TOKENS * 0.4 and _gemini_chat and _get_gemini_history_list(_gemini_chat): - hist = _get_gemini_history_list(_gemini_chat) - dropped = 0 - while len(hist) > 4 and total_in > _GEMINI_MAX_INPUT_TOKENS * 0.3: - saved = 0 - for _ in range(2): - if not hist: break - for p in hist[0].parts: - if hasattr(p, "text") and p.text: saved += int(len(p.text) / _CHARS_PER_TOKEN) - elif hasattr(p, "function_response") and p.function_response: - r = getattr(p.function_response, "response", {}) - if isinstance(r, dict): saved += int(len(str(r.get("output", ""))) / _CHARS_PER_TOKEN) - hist.pop(0) - dropped += 1 - total_in -= max(saved, 200) - if dropped > 0: _append_comms("OUT", "request", {"message": f"[GEMINI HISTORY TRIMMED: dropped {dropped} old entries]"}) - if not calls or r_idx > MAX_TOOL_ROUNDS: break - f_resps: list[types.Part] = [] - log: list[dict[str, Any]] = [] - for i, fc in enumerate(calls): - name, args = fc.name, dict(fc.args) - if pre_tool_callback: - payload_str = json.dumps({"tool": name, "args": args}) - if not pre_tool_callback(payload_str): - out = "USER REJECTED: tool execution cancelled" - f_resps.append(types.Part.from_function_response(name=name, response={"output": out})) - log.append({"tool_use_id": name, "content": out}) - continue - events.emit("tool_execution", payload={"status": "started", "tool": name, "args": args, "round": r_idx}) - if name in mcp_client.TOOL_NAMES: - _append_comms("OUT", "tool_call", {"name": name, "args": args}) - out = mcp_client.dispatch(name, args) - elif name == TOOL_NAME: - scr = args.get("script", "") - _append_comms("OUT", "tool_call", {"name": TOOL_NAME, "script": scr}) - out = _run_script(scr, base_dir, qa_callback) - else: out = f"ERROR: unknown tool '{name}'" - if i == len(calls) - 1: - if file_items: - file_items, changed = _reread_file_items(file_items) - ctx = _build_file_diff_text(changed) - if ctx: out += f" - -[SYSTEM: FILES UPDATED] - -{ctx}" - if r_idx == MAX_TOOL_ROUNDS: out += " - -[SYSTEM: MAX ROUNDS. PROVIDE FINAL ANSWER.]" - out = _truncate_tool_output(out) - _cumulative_tool_bytes += len(out) - f_resps.append(types.Part.from_function_response(name=name, response={"output": out})) - log.append({"tool_use_id": name, "content": out}) - events.emit("tool_execution", payload={"status": "completed", "tool": name, "result": out, "round": r_idx}) - if _cumulative_tool_bytes > _MAX_TOOL_OUTPUT_BYTES: - f_resps.append(types.Part.from_text(f"SYSTEM WARNING: Cumulative tool output exceeded {_MAX_TOOL_OUTPUT_BYTES // 1000}KB budget.")) - _append_comms("OUT", "request", {"message": f"[TOOL OUTPUT BUDGET EXCEEDED: {_cumulative_tool_bytes} bytes]"}) - _append_comms("OUT", "tool_result_send", {"results": log}) - payload = f_resps - return " - -".join(all_text) if all_text else "(No text returned)" - except Exception as e: raise _classify_gemini_error(e) from e -''' - -_SEND_ANTHROPIC_NEW = '''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], bool]] = None, qa_callback: Optional[Callable[[str], str]] = None, stream_callback: Optional[Callable[[str], None]] = None) -> str: - try: - _ensure_anthropic_client() - mcp_client.configure(file_items or [], [base_dir]) - stable_prompt = _get_combined_system_prompt() - stable_blocks = [{"type": "text", "text": stable_prompt, "cache_control": {"type": "ephemeral"}}] - context_text = f" - - -{md_content} -" - context_blocks = _build_chunked_context_blocks(context_text) - system_blocks = stable_blocks + context_blocks - if discussion_history and not _anthropic_history: - user_content: list[dict[str, Any]] = [{"type": "text", "text": f"[DISCUSSION HISTORY] - -{discussion_history} - ---- - -{user_message}"}] - else: - user_content = [{"type": "text", "text": user_message}] - for msg in _anthropic_history: - if msg.get("role") == "user" and isinstance(msg.get("content"), list): - modified = False - for block in msg["content"]: - if isinstance(block, dict) and block.get("type") == "tool_result": - t_content = block.get("content", "") - if _history_trunc_limit > 0 and isinstance(t_content, str) and len(t_content) > _history_trunc_limit: - block["content"] = t_content[:_history_trunc_limit] + " - -... [TRUNCATED BY SYSTEM]" - modified = True - if modified: _invalidate_token_estimate(msg) - _strip_cache_controls(_anthropic_history) - _repair_anthropic_history(_anthropic_history) - _anthropic_history.append({"role": "user", "content": user_content}) - _add_history_cache_breakpoint(_anthropic_history) - all_text_parts: list[str] = [] - _cumulative_tool_bytes = 0 - def _strip_private_keys(history: list[dict[str, Any]]) -> list[dict[str, Any]]: - return [{k: v for k, v in m.items() if not k.startswith("_")} for m in history] - for round_idx in range(MAX_TOOL_ROUNDS + 2): - dropped = _trim_anthropic_history(system_blocks, _anthropic_history) - if dropped > 0: - est_tokens = _estimate_prompt_tokens(system_blocks, _anthropic_history) - _append_comms("OUT", "request", {"message": f"[HISTORY TRIMMED: dropped {dropped} old messages]"}) - events.emit("request_start", payload={"provider": "anthropic", "model": _model, "round": round_idx}) - if stream_callback: - with _anthropic_client.messages.stream( - model=_model, - max_tokens=_max_tokens, - temperature=_temperature, - system=system_blocks, - tools=_get_anthropic_tools(), - messages=_strip_private_keys(_anthropic_history), - ) as stream: - for event in stream: - if event.type == "content_block_delta" and event.delta.type == "text_delta": - stream_callback(event.delta.text) - response = stream.get_final_message() - else: - response = _anthropic_client.messages.create( - model=_model, - max_tokens=_max_tokens, - temperature=_temperature, - system=system_blocks, - tools=_get_anthropic_tools(), - messages=_strip_private_keys(_anthropic_history), - ) - serialised_content = [_content_block_to_dict(b) for b in response.content] - _anthropic_history.append({"role": "assistant", "content": serialised_content}) - text_blocks = [b.text for b in response.content if hasattr(b, "text") and b.text] - if text_blocks: all_text_parts.append(" -".join(text_blocks)) - tool_use_blocks = [{"id": b.id, "name": b.name, "input": b.input} for b in response.content if getattr(b, "type", None) == "tool_use"] - usage_dict: dict[str, Any] = {} - if response.usage: - usage_dict["input_tokens"] = response.usage.input_tokens - usage_dict["output_tokens"] = response.usage.output_tokens - for k in ["cache_creation_input_tokens", "cache_read_input_tokens"]: - val = getattr(response.usage, k, None) - if val is not None: usage_dict[k] = val - events.emit("response_received", payload={"provider": "anthropic", "model": _model, "usage": usage_dict, "round": round_idx}) - _append_comms("IN", "response", {"round": round_idx, "stop_reason": response.stop_reason, "text": " -".join(text_blocks), "tool_calls": tool_use_blocks, "usage": usage_dict}) - if response.stop_reason != "tool_use" or not tool_use_blocks: break - if round_idx > MAX_TOOL_ROUNDS: break - tool_results: list[dict[str, Any]] = [] - for block in response.content: - if getattr(block, "type", None) != "tool_use": continue - b_name, b_id, b_input = block.name, block.id, block.input - if pre_tool_callback: - if not pre_tool_callback(json.dumps({"tool": b_name, "args": b_input})): - tool_results.append({"type": "tool_result", "tool_use_id": b_id, "content": "USER REJECTED: tool execution cancelled"}) - continue - events.emit("tool_execution", payload={"status": "started", "tool": b_name, "args": b_input, "round": round_idx}) - if b_name in mcp_client.TOOL_NAMES: - _append_comms("OUT", "tool_call", {"name": b_name, "id": b_id, "args": b_input}) - output = mcp_client.dispatch(b_name, b_input) - elif b_name == TOOL_NAME: - scr = b_input.get("script", "") - _append_comms("OUT", "tool_call", {"name": TOOL_NAME, "id": b_id, "script": scr}) - output = _run_script(scr, base_dir, qa_callback) - else: output = f"ERROR: unknown tool '{b_name}'" - truncated = _truncate_tool_output(output) - _cumulative_tool_bytes += len(truncated) - tool_results.append({"type": "tool_result", "tool_use_id": b_id, "content": truncated}) - _append_comms("IN", "tool_result", {"name": b_name, "id": b_id, "output": output}) - events.emit("tool_execution", payload={"status": "completed", "tool": b_name, "result": output, "round": round_idx}) - if _cumulative_tool_bytes > _MAX_TOOL_OUTPUT_BYTES: - tool_results.append({"type": "text", "text": "SYSTEM WARNING: Cumulative tool output exceeded budget."}) - if file_items: - file_items, changed = _reread_file_items(file_items) - refreshed_ctx = _build_file_diff_text(changed) - if refreshed_ctx: tool_results.append({"type": "text", "text": f"[FILES UPDATED] - -{refreshed_ctx}"}) - if round_idx == MAX_TOOL_ROUNDS: tool_results.append({"type": "text", "text": "SYSTEM WARNING: MAX TOOL ROUNDS REACHED."}) - _anthropic_history.append({"role": "user", "content": tool_results}) - _append_comms("OUT", "tool_result_send", {"results": [{"tool_use_id": r["tool_use_id"], "content": r["content"]} for r in tool_results if r.get("type") == "tool_result"]}) - return " - -".join(all_text_parts) if all_text_parts else "(No text returned)" - except Exception as exc: raise _classify_anthropic_error(exc) from exc -''' - -_SEND_DEEPSEEK_NEW = '''def _send_deepseek(md_content: str, user_message: str, base_dir: str, - file_items: list[dict[str, Any]] | None = None, - discussion_history: str = "", - stream: bool = False, - pre_tool_callback: Optional[Callable[[str], bool]] = None, - qa_callback: Optional[Callable[[str], str]] = None, - stream_callback: Optional[Callable[[str], None]] = None) -> str: - try: - mcp_client.configure(file_items or [], [base_dir]) - creds = _load_credentials() - api_key = creds.get("deepseek", {}).get("api_key") - if not api_key: raise ValueError("DeepSeek API key not found") - api_url = "https://api.deepseek.com/chat/completions" - headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} - current_api_messages: list[dict[str, Any]] = [] - with _deepseek_history_lock: - for msg in _deepseek_history: current_api_messages.append(msg) - initial_user_message_content = user_message - if discussion_history: initial_user_message_content = f"[DISCUSSION HISTORY] - -{discussion_history} - ---- - -{user_message}" - current_api_messages.append({"role": "user", "content": initial_user_message_content}) - request_payload: dict[str, Any] = {"model": _model, "messages": current_api_messages, "temperature": _temperature, "max_tokens": _max_tokens, "stream": stream} - sys_msg = {"role": "system", "content": f"{_get_combined_system_prompt()} - - -{md_content} -"} - request_payload["messages"].insert(0, sys_msg) - all_text_parts: list[str] = [] - _cumulative_tool_bytes = 0 - round_idx = 0 - while round_idx <= MAX_TOOL_ROUNDS + 1: - events.emit("request_start", payload={"provider": "deepseek", "model": _model, "round": round_idx, "streaming": stream}) - try: - response = requests.post(api_url, headers=headers, json=request_payload, timeout=60, stream=stream) - response.raise_for_status() - except requests.exceptions.RequestException as e: raise _classify_deepseek_error(e) from e - if stream: - aggregated_content, aggregated_tool_calls, aggregated_reasoning = "", [], "" - current_usage, final_finish_reason = {}, "stop" - for line in response.iter_lines(): - if not line: continue - decoded = line.decode('utf-8') - if decoded.startswith('data: '): - chunk_str = decoded[len('data: '):] - if chunk_str.strip() == '[DONE]': continue - try: - chunk = json.loads(chunk_str) - delta = chunk.get("choices", [{}])[0].get("delta", {}) - if delta.get("content"): - aggregated_content += delta["content"] - if stream_callback: stream_callback(delta["content"]) - if delta.get("reasoning_content"): aggregated_reasoning += delta["reasoning_content"] - if delta.get("tool_calls"): - for tc_delta in delta["tool_calls"]: - idx = tc_delta.get("index", 0) - while len(aggregated_tool_calls) <= idx: aggregated_tool_calls.append({"id": "", "type": "function", "function": {"name": "", "arguments": ""}}) - target = aggregated_tool_calls[idx] - if tc_delta.get("id"): target["id"] = tc_delta["id"] - if tc_delta.get("function", {}).get("name"): target["function"]["name"] += tc_delta["function"]["name"] - if tc_delta.get("function", {}).get("arguments"): target["function"]["arguments"] += tc_delta["function"]["arguments"] - if chunk.get("choices", [{}])[0].get("finish_reason"): final_finish_reason = chunk["choices"][0]["finish_reason"] - if chunk.get("usage"): current_usage = chunk["usage"] - except json.JSONDecodeError: continue - assistant_text, tool_calls_raw, reasoning_content, finish_reason, usage = aggregated_content, aggregated_tool_calls, aggregated_reasoning, final_finish_reason, current_usage - else: - response_data = response.json() - choices = response_data.get("choices", []) - if not choices: break - choice = choices[0] - message = choice.get("message", {}) - assistant_text, tool_calls_raw, reasoning_content, finish_reason, usage = message.get("content", ""), message.get("tool_calls", []), message.get("reasoning_content", ""), choice.get("finish_reason", "stop"), response_data.get("usage", {}) - full_assistant_text = (f" -{reasoning_content} - -" if reasoning_content else "") + assistant_text - with _deepseek_history_lock: - msg_to_store = {"role": "assistant", "content": assistant_text} - if reasoning_content: msg_to_store["reasoning_content"] = reasoning_content - if tool_calls_raw: msg_to_store["tool_calls"] = tool_calls_raw - _deepseek_history.append(msg_to_store) - if full_assistant_text: all_text_parts.append(full_assistant_text) - _append_comms("IN", "response", {"round": round_idx, "stop_reason": finish_reason, "text": full_assistant_text, "tool_calls": tool_calls_raw, "usage": usage, "streaming": stream}) - if finish_reason != "tool_calls" and not tool_calls_raw: break - if round_idx > MAX_TOOL_ROUNDS: break - tool_results_for_history: list[dict[str, Any]] = [] - for i, tc_raw in enumerate(tool_calls_raw): - tool_info = tc_raw.get("function", {}) - tool_name, tool_args_str, tool_id = tool_info.get("name"), tool_info.get("arguments", "{}"), tc_raw.get("id") - try: tool_args = json.loads(tool_args_str) - except: tool_args = {} - if pre_tool_callback: - if not pre_tool_callback(json.dumps({"tool": tool_name, "args": tool_args})): - tool_output = "USER REJECTED: tool execution cancelled" - tool_results_for_history.append({"role": "tool", "tool_call_id": tool_id, "content": tool_output}) - continue - events.emit("tool_execution", payload={"status": "started", "tool": tool_name, "args": tool_args, "round": round_idx}) - if tool_name in mcp_client.TOOL_NAMES: - _append_comms("OUT", "tool_call", {"name": tool_name, "id": tool_id, "args": tool_args}) - tool_output = mcp_client.dispatch(tool_name, tool_args) - elif tool_name == TOOL_NAME: - script = tool_args.get("script", "") - _append_comms("OUT", "tool_call", {"name": TOOL_NAME, "id": tool_id, "script": script}) - tool_output = _run_script(script, base_dir, qa_callback) - else: tool_output = f"ERROR: unknown tool '{tool_name}'" - if i == len(tool_calls_raw) - 1: - if file_items: - file_items, changed = _reread_file_items(file_items) - ctx = _build_file_diff_text(changed) - if ctx: tool_output += f" - -[SYSTEM: FILES UPDATED] - -{ctx}" - if round_idx == MAX_TOOL_ROUNDS: tool_output += " - -[SYSTEM: MAX ROUNDS. PROVIDE FINAL ANSWER.]" - tool_output = _truncate_tool_output(tool_output) - _cumulative_tool_bytes += len(tool_output) - tool_results_for_history.append({"role": "tool", "tool_call_id": tool_id, "content": tool_output}) - _append_comms("IN", "tool_result", {"name": tool_name, "id": tool_id, "output": tool_output}) - events.emit("tool_execution", payload={"status": "completed", "tool": tool_name, "result": tool_output, "round": round_idx}) - if _cumulative_tool_bytes > _MAX_TOOL_OUTPUT_BYTES: - tool_results_for_history.append({"role": "user", "content": "SYSTEM WARNING: Cumulative tool output exceeded budget."}) - with _deepseek_history_lock: - for tr in tool_results_for_history: _deepseek_history.append(tr) - next_messages: list[dict[str, Any]] = [] - with _deepseek_history_lock: - for msg in _deepseek_history: next_messages.append(msg) - next_messages.insert(0, sys_msg) - request_payload["messages"] = next_messages - round_idx += 1 - return " - -".join(all_text_parts) if all_text_parts else "(No text returned)" - except Exception as e: raise _classify_deepseek_error(e) from e -''' - -_SEND_NEW = '''def send( - md_content: str, - user_message: str, - base_dir: str = ".", - file_items: list[dict[str, Any]] | None = None, - discussion_history: str = "", - stream: bool = False, - pre_tool_callback: Optional[Callable[[str], bool]] = None, - qa_callback: Optional[Callable[[str], str]] = None, - enable_tools: bool = True, - stream_callback: Optional[Callable[[str], None]] = None, -) -> str: - """ - Sends a prompt with the full markdown context to the current AI provider. - Returns the final text response. - """ - with _send_lock: - if _provider == "gemini": - return _send_gemini( - md_content, user_message, base_dir, file_items, discussion_history, - pre_tool_callback, qa_callback, enable_tools, stream_callback - ) - elif _provider == "gemini_cli": - return _send_gemini_cli( - md_content, user_message, base_dir, file_items, discussion_history, - pre_tool_callback, qa_callback - ) - elif _provider == "anthropic": - return _send_anthropic( - md_content, user_message, base_dir, file_items, discussion_history, - pre_tool_callback, qa_callback, stream_callback=stream_callback - ) - elif _provider == "deepseek": - return _send_deepseek( - md_content, user_message, base_dir, file_items, discussion_history, - stream, pre_tool_callback, qa_callback, stream_callback - ) - else: - raise ValueError(f"Unknown provider: {_provider}") -''' - -# Use regex or simple string replacement to replace the old functions with new ones. -import re - -def replace_func(content, func_name, new_body): - # This is tricky because functions can be complex. - # I'll just use a marker based approach for this specific file. - start_marker = f'def {func_name}(' - # Find the next 'def ' or end of file - start_idx = content.find(start_marker) - if start_idx == -1: return content - - # Find the end of the function (rough estimation based on next def at column 0) - next_def = re.search(r' - -def ', content[start_idx+1:]) - if next_def: - end_idx = start_idx + 1 + next_def.start() - else: - end_idx = len(content) - - return content[:start_idx] + new_body + content[end_idx:] - -# Final content construction -content = replace_func(content, '_send_gemini', _SEND_GEMINI_NEW) -content = replace_func(content, '_send_anthropic', _SEND_ANTHROPIC_NEW) -content = replace_func(content, '_send_deepseek', _SEND_DEEPSEEK_NEW) -content = replace_func(content, 'send', _SEND_NEW) - -# Remove the duplicated parts at the end if any -marker = 'import json -from typing import Any, Callable, Optional, List' -if marker in content: - content = content[:content.find(marker)] - -with open(path, 'w', encoding='utf-8') as f: - f.write(content) diff --git a/conductor/tests/diag_subagent.py b/conductor/tests/diag_subagent.py index 9c709e8..e4b290a 100644 --- a/conductor/tests/diag_subagent.py +++ b/conductor/tests/diag_subagent.py @@ -1,6 +1,5 @@ import subprocess import sys -import os def run_diag(role: str, prompt: str) -> str: print(f"--- Running Diag for {role} ---") diff --git a/conductor/tests/test_infrastructure.py b/conductor/tests/test_infrastructure.py index e558fad..b21a66b 100644 --- a/conductor/tests/test_infrastructure.py +++ b/conductor/tests/test_infrastructure.py @@ -1,6 +1,4 @@ import subprocess -import pytest -import os def run_ps_script(role: str, prompt: str) -> subprocess.CompletedProcess: """Helper to run the run_subagent.ps1 script.""" diff --git a/config.toml b/config.toml index 8cc7214..746ada0 100644 --- a/config.toml +++ b/config.toml @@ -1,6 +1,6 @@ [ai] -provider = "gemini" -model = "gemini-2.5-flash-lite" +provider = "gemini_cli" +model = "gemini-2.0-flash" temperature = 0.0 max_tokens = 8192 history_trunc_limit = 8000 @@ -15,7 +15,7 @@ paths = [ "C:\\projects\\manual_slop\\tests\\artifacts\\temp_livetoolssim.toml", "C:\\projects\\manual_slop\\tests\\artifacts\\temp_liveexecutionsim.toml", ] -active = "C:\\projects\\manual_slop\\tests\\artifacts\\temp_livecontextsim.toml" +active = "C:\\projects\\manual_slop\\tests\\artifacts\\temp_project.toml" [gui.show_windows] "Context Hub" = true diff --git a/dag_engine.py b/dag_engine.py index de21ae7..1b5e80e 100644 --- a/dag_engine.py +++ b/dag_engine.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List from models import Ticket class TrackDAG: diff --git a/debug_ast.py b/debug_ast.py deleted file mode 100644 index f998d20..0000000 --- a/debug_ast.py +++ /dev/null @@ -1,18 +0,0 @@ -import tree_sitter -import tree_sitter_python - -code = """def hot_func(): - # [HOT] - print(1)""" - -PY_LANGUAGE = tree_sitter.Language(tree_sitter_python.language()) -parser = tree_sitter.Parser(PY_LANGUAGE) -tree = parser.parse(bytes(code, "utf8")) - -def walk(node, indent=0): - content = code[node.start_byte:node.end_byte].strip() - print(f"{' ' * indent}{node.type} ({node.start_byte}-{node.end_byte}): {content[:20]}") - for child in node.children: - walk(child, indent + 1) - -walk(tree.root_node) diff --git a/debug_ast_2.py b/debug_ast_2.py deleted file mode 100644 index c6a9591..0000000 --- a/debug_ast_2.py +++ /dev/null @@ -1,98 +0,0 @@ -import tree_sitter -import tree_sitter_python - -class ASTParser: - def __init__(self, language: str) -> None: - self.language = tree_sitter.Language(tree_sitter_python.language()) - self.parser = tree_sitter.Parser(self.language) - - def parse(self, code: str) -> tree_sitter.Tree: - return self.parser.parse(bytes(code, "utf8")) - - def get_curated_view(self, code: str) -> str: - tree = self.parse(code) - edits = [] - - def is_docstring(node): - if node.type == "expression_statement" and node.child_count > 0: - if node.children[0].type == "string": - return True - return False - - def has_core_logic_decorator(node): - parent = node.parent - if parent and parent.type == "decorated_definition": - for child in parent.children: - if child.type == "decorator": - if "@core_logic" in code[child.start_byte:child.end_byte]: - return True - return False - - def has_hot_comment(func_node): - print(f"Checking {code[func_node.start_byte:func_node.start_byte+20].strip()}...") - stack = [func_node] - while stack: - curr = stack.pop() - if curr.type == "comment": - comment_text = code[curr.start_byte:curr.end_byte] - print(f" Found comment: {comment_text}") - if "[HOT]" in comment_text: - print(" [HOT] FOUND!") - return True - for child in curr.children: - stack.append(child) - return False - - def walk(node): - if node.type == "function_definition": - body = node.child_by_field_name("body") - if body and body.type == "block": - preserve = has_core_logic_decorator(node) or has_hot_comment(node) - print(f"Function {code[node.start_byte:node.start_byte+20].strip()}, preserve={preserve}") - if not preserve: - indent = " " * body.start_point.column - first_stmt = None - for child in body.children: - if child.type != "comment": - first_stmt = child - break - if first_stmt and is_docstring(first_stmt): - start_byte = first_stmt.end_byte - end_byte = body.end_byte - if end_byte > start_byte: - edits.append((start_byte, end_byte, "\\n" + indent + "...")) - else: - start_byte = body.start_byte - end_byte = body.end_byte - edits.append((start_byte, end_byte, "...")) - for child in node.children: - walk(child) - walk(tree.root_node) - edits.sort(key=lambda x: x[0], reverse=True) - code_bytes = bytearray(code, "utf8") - for start, end, replacement in edits: - code_bytes[start:end] = bytes(replacement, "utf8") - return code_bytes.decode("utf8") - -parser = ASTParser("python") -code = ''' -@core_logic -def core_func(): - """Core logic doc.""" - print("this should be preserved") - return True - -def hot_func(): - # [HOT] - print("this should also be preserved") - return 42 - -def normal_func(): - """Normal doc.""" - print("this should be stripped") - return None -''' - -result = parser.get_curated_view(code) -print("\\n--- RESULT ---\\n") -print(result) diff --git a/gemini.py b/gemini.py index b0eae6e..8caa5b9 100644 --- a/gemini.py +++ b/gemini.py @@ -1,10 +1,8 @@ # gemini.py from __future__ import annotations import tomllib -from pathlib import Path from typing import Any from google import genai -from google.genai import types _client: genai.Client | None = None _chat: Any = None diff --git a/gemini_cli_adapter.py b/gemini_cli_adapter.py index 7abab69..785a842 100644 --- a/gemini_cli_adapter.py +++ b/gemini_cli_adapter.py @@ -38,7 +38,6 @@ class GeminiCliAdapter: accumulated_text = "" tool_calls = [] stdout_content = [] - stderr_content = [] env = os.environ.copy() env["GEMINI_CLI_HOOK_CONTEXT"] = "manual_slop" diff --git a/gui_2.py b/gui_2.py index 35fce3c..607d0dd 100644 --- a/gui_2.py +++ b/gui_2.py @@ -12,7 +12,7 @@ import uuid import requests from pathlib import Path from tkinter import filedialog, Tk -from typing import Optional, Callable, Any, Dict, List, Tuple, Union +from typing import Optional, Callable, Any import aggregate import ai_client import cost_tracker @@ -35,7 +35,7 @@ import multi_agent_conductor from models import Track, Ticket from file_cache import ASTParser -from fastapi import FastAPI, Depends, HTTPException, Security +from fastapi import FastAPI, Depends, HTTPException from fastapi.security.api_key import APIKeyHeader from pydantic import BaseModel from imgui_bundle import imgui, hello_imgui, immapp @@ -431,7 +431,6 @@ class App: 'btn_mma_start_track': self._cb_start_track, 'btn_mma_create_track': lambda: self._cb_create_track(self.ui_new_track_name, self.ui_new_track_desc, self.ui_new_track_type), 'btn_approve_tool': self._handle_approve_tool, - 'btn_approve_script': self._handle_approve_script, 'btn_approve_mma_step': self._handle_approve_mma_step, 'btn_approve_spawn': self._handle_approve_spawn, } @@ -1070,7 +1069,7 @@ class App: return self._scroll_disc_to_bottom = True for item in items: - role = item.get("role", "unknown") + item.get("role", "unknown") if item.get("role") and item["role"] not in self.disc_roles: self.disc_roles.append(item["role"]) disc_sec = self.project.get("discussion", {}) @@ -3389,7 +3388,7 @@ class App: imgui.begin_tooltip() imgui.text_colored(C_KEY, f"ID: {tid}") imgui.text_colored(C_LBL, f"Target: {target}") - imgui.text_colored(C_LBL, f"Description:") + imgui.text_colored(C_LBL, "Description:") imgui.same_line() imgui.text_wrapped(ticket.get('description', 'N/A')) deps = ticket.get('depends_on', []) @@ -3481,8 +3480,8 @@ class App: imgui.push_id(f"comms_{idx}") if blink_alpha > 0: # Draw a background highlight for the entry - draw_list = imgui.get_window_draw_list() - p_min = imgui.get_cursor_screen_pos() + imgui.get_window_draw_list() + imgui.get_cursor_screen_pos() # Estimate height or just use a fixed height for the background # It's better to wrap the entry in a group or just use separators # For now, let's just use the style color push if we are sure we pop it diff --git a/gui_legacy.py b/gui_legacy.py index d52b88d..fba4674 100644 --- a/gui_legacy.py +++ b/gui_legacy.py @@ -1020,7 +1020,6 @@ class App: dpg.add_button( label="x", width=24, callback=self._make_remove_project_cb(i) ) - name_color = (140, 255, 160) if is_active else (200, 200, 200) marker = " *" if is_active else "" dpg.add_button( label=f"{Path(pp).stem}{marker}", diff --git a/inspect_ast.py b/inspect_ast.py deleted file mode 100644 index 6b8dc49..0000000 --- a/inspect_ast.py +++ /dev/null @@ -1,29 +0,0 @@ - -import tree_sitter -import tree_sitter_python - -language = tree_sitter.Language(tree_sitter_python.language()) -parser = tree_sitter.Parser(language) - -code = """ -@core_logic -def decorated_func(): - """Docstring.""" - print("core logic here") - -def hot_func(): - # [HOT] - print("hot logic here") - -def normal_func(): - print("normal logic here") -""" - -tree = parser.parse(bytes(code, "utf8")) - -def print_node(node, indent=0): - print(" " * indent + f"{node.type} [{node.start_byte}-{node.end_byte}] " + (f"'{code[node.start_byte:node.end_byte]}'" if node.type in ["decorator", "comment", "identifier"] else "")) - for child in node.children: - print_node(child, indent + 1) - -print_node(tree.root_node) diff --git a/log_pruner.py b/log_pruner.py index 0dee89a..65edf16 100644 --- a/log_pruner.py +++ b/log_pruner.py @@ -39,7 +39,7 @@ class LogPruner: old_sessions_to_check = self.log_registry.get_old_non_whitelisted_sessions(cutoff_time) # Prune sessions if their size is less than 2048 bytes for session_info in old_sessions_to_check: - session_id = session_info['session_id'] + session_info['session_id'] session_path = session_info['path'] if not session_path or not os.path.isdir(session_path): continue diff --git a/mcp_client.py b/mcp_client.py index 3dc5a45..9c9460b 100644 --- a/mcp_client.py +++ b/mcp_client.py @@ -414,7 +414,7 @@ def py_set_signature(path: str, name: str, new_signature: str) -> str: try: import ast code = p.read_text(encoding="utf-8").lstrip(chr(0xFEFF)) - lines = code.splitlines(keepends=True) + code.splitlines(keepends=True) tree = ast.parse(code) node = _get_symbol_node(tree, name) if not node or not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): diff --git a/multi_agent_conductor.py b/multi_agent_conductor.py index 155d4f3..ba21456 100644 --- a/multi_agent_conductor.py +++ b/multi_agent_conductor.py @@ -1,7 +1,6 @@ import ai_client import json import asyncio -import threading import time import traceback from typing import List, Optional, Tuple @@ -242,7 +241,7 @@ def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files: parser = ASTParser(language="python") for i, file_path in enumerate(context_files): try: - abs_path = Path(file_path) + Path(file_path) # (This is a bit simplified, but helps) with open(file_path, 'r', encoding='utf-8') as f: content = f.read() diff --git a/project_history.toml b/project_history.toml index d209f70..3513205 100644 --- a/project_history.toml +++ b/project_history.toml @@ -8,5 +8,5 @@ active = "main" [discussions.main] git_commit = "" -last_updated = "2026-03-01T22:58:49" +last_updated = "2026-03-02T13:16:02" history = [] diff --git a/project_manager.py b/project_manager.py index d6b844b..5b21bb0 100644 --- a/project_manager.py +++ b/project_manager.py @@ -242,7 +242,6 @@ def load_track_history(track_id: str, base_dir: Union[str, Path] = ".") -> list[ Loads the discussion history for a specific track from its state.toml. Returns a list of entry strings formatted with @timestamp. """ - from models import TrackState state = load_track_state(track_id, base_dir) if not state: return [] @@ -260,7 +259,6 @@ def save_track_history(track_id: str, history: list[str], base_dir: Union[str, P Saves the discussion history for a specific track to its state.toml. 'history' is expected to be a list of formatted strings. """ - from models import TrackState state = load_track_state(track_id, base_dir) if not state: return @@ -277,7 +275,6 @@ def get_all_tracks(base_dir: Union[str, Path] = ".") -> list[dict[str, Any]]: Handles missing or malformed metadata.json or state.toml by falling back to available info or defaults. """ - from models import TrackState tracks_dir = Path(base_dir) / "conductor" / "tracks" if not tracks_dir.exists(): return [] diff --git a/scripts/apply_type_hints.py b/scripts/apply_type_hints.py index 7b9e8c9..5f5f301 100644 --- a/scripts/apply_type_hints.py +++ b/scripts/apply_type_hints.py @@ -9,7 +9,7 @@ import ast import re import sys import os -from typing import Any, Callable +from typing import Any BASE: str = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) stats: dict[str, Any] = {"auto_none": 0, "manual_sig": 0, "vars": 0, "errors": []} @@ -332,7 +332,7 @@ if __name__ == "__main__": print(f" {f}: {r}") if "Error" in r: all_ok = False - print(f"\n=== Summary ===") + print("\n=== Summary ===") print(f" Auto -> None: {stats['auto_none']}") print(f" Manual sigs: {stats['manual_sig']}") print(f" Variables: {stats['vars']}") diff --git a/scripts/check_hints.py b/scripts/check_hints.py index 09c5508..4711e90 100644 --- a/scripts/check_hints.py +++ b/scripts/check_hints.py @@ -1,5 +1,3 @@ -import re -import sys files = ['ai_client.py', 'aggregate.py', 'mcp_client.py', 'shell_runner.py'] for file_path in files: diff --git a/scripts/check_hints_v2.py b/scripts/check_hints_v2.py index 86a37e0..d53c49c 100644 --- a/scripts/check_hints_v2.py +++ b/scripts/check_hints_v2.py @@ -1,5 +1,4 @@ import re -import sys files = ['ai_client.py', 'aggregate.py', 'mcp_client.py', 'shell_runner.py'] for file_path in files: diff --git a/scripts/cli_tool_bridge.py b/scripts/cli_tool_bridge.py index 52c5a38..dff7a1c 100644 --- a/scripts/cli_tool_bridge.py +++ b/scripts/cli_tool_bridge.py @@ -97,7 +97,7 @@ def main(): })) return if hook_context == "mma_headless": - logging.debug(f"GEMINI_CLI_HOOK_CONTEXT is 'mma_headless'. Allowing execution for sub-agent.") + logging.debug("GEMINI_CLI_HOOK_CONTEXT is 'mma_headless'. Allowing execution for sub-agent.") print(json.dumps({ "decision": "allow", "reason": "Sub-agent headless mode (MMA)." diff --git a/scripts/inject_tools.py b/scripts/inject_tools.py index dddd28d..e8dba6b 100644 --- a/scripts/inject_tools.py +++ b/scripts/inject_tools.py @@ -1,4 +1,3 @@ -import os import re with open('mcp_client.py', 'r', encoding='utf-8') as f: diff --git a/scripts/mma_exec.py b/scripts/mma_exec.py index 2f549d4..1391eba 100644 --- a/scripts/mma_exec.py +++ b/scripts/mma_exec.py @@ -211,19 +211,19 @@ def execute_agent(role: str, prompt: str, docs: list[str], debug: bool = False, env = os.environ.copy() env["GEMINI_CLI_HOOK_CONTEXT"] = "mma_headless" if debug: - print(f"--- MMA DEBUG ---") + print("--- MMA DEBUG ---") print(f"Executing Command: {cmd}") - print(f"Relevant Environment Variables:") + print("Relevant Environment Variables:") for key, value in env.items(): if key.startswith("GEMINI_CLI_"): print(f" {key}={value}") process = subprocess.run(cmd, input=command_text, capture_output=True, text=True, encoding='utf-8', env=env) if debug: - print(f"Subprocess Result:") + print("Subprocess Result:") print(f" Return Code: {process.returncode}") print(f" Stdout: {process.stdout[:1000]}...") print(f" Stderr: {process.stderr}") - print(f"--- END DEBUG ---") + print("--- END DEBUG ---") result = process.stdout if not process.stdout and process.stderr: result = f"Error: {process.stderr}" diff --git a/scripts/scan_all_hints.py b/scripts/scan_all_hints.py index d2a54af..9077b25 100644 --- a/scripts/scan_all_hints.py +++ b/scripts/scan_all_hints.py @@ -1,5 +1,6 @@ """Scan all .py files for missing type hints. Writes scan_report.txt.""" -import ast, os +import ast +import os SKIP: set[str] = {'.git', '__pycache__', '.venv', 'venv', 'node_modules', '.claude', '.gemini'} BASE: str = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) diff --git a/shell_runner.py b/shell_runner.py index 2acd743..8a8d2cc 100644 --- a/shell_runner.py +++ b/shell_runner.py @@ -1,5 +1,7 @@ # shell_runner.py -import os, subprocess, shutil +import os +import subprocess +import shutil from pathlib import Path from typing import Callable, Optional diff --git a/simulation/live_walkthrough.py b/simulation/live_walkthrough.py index bbc0ef5..0a8f38f 100644 --- a/simulation/live_walkthrough.py +++ b/simulation/live_walkthrough.py @@ -1,7 +1,5 @@ -import sys import os import time -import random from api_hook_client import ApiHookClient from simulation.workflow_sim import WorkflowSimulator diff --git a/simulation/sim_ai_settings.py b/simulation/sim_ai_settings.py index e6ceb63..7a5de48 100644 --- a/simulation/sim_ai_settings.py +++ b/simulation/sim_ai_settings.py @@ -1,5 +1,3 @@ -import sys -import os import time from simulation.sim_base import BaseSimulation, run_sim diff --git a/simulation/sim_base.py b/simulation/sim_base.py index 50ba21b..bd8d701 100644 --- a/simulation/sim_base.py +++ b/simulation/sim_base.py @@ -1,7 +1,6 @@ import sys import os import time -import pytest from typing import Any, Optional from api_hook_client import ApiHookClient from simulation.workflow_sim import WorkflowSimulator @@ -19,7 +18,7 @@ class BaseSimulation: self.project_path = None def setup(self, project_name: str = "SimProject") -> None: - print(f"\n[BaseSim] Connecting to GUI...") + print("\n[BaseSim] Connecting to GUI...") if not self.client.wait_for_server(timeout=5): raise RuntimeError("Could not connect to GUI. Ensure it is running with --enable-test-hooks") self.client.set_value("auto_add_history", True) diff --git a/simulation/sim_context.py b/simulation/sim_context.py index abc7a0b..90de522 100644 --- a/simulation/sim_context.py +++ b/simulation/sim_context.py @@ -1,4 +1,3 @@ -import sys import os import time from simulation.sim_base import BaseSimulation, run_sim @@ -31,7 +30,7 @@ class ContextSimulation(BaseSimulation): self.client.click("btn_md_only") time.sleep(5) # Verify status - proj_updated = self.client.get_project() + self.client.get_project() status = self.client.get_value("ai_status") print(f"[Sim] Status: {status}") assert "md written" in status, f"Expected 'md written' in status, got {status}" diff --git a/simulation/sim_execution.py b/simulation/sim_execution.py index 187410f..5583789 100644 --- a/simulation/sim_execution.py +++ b/simulation/sim_execution.py @@ -1,4 +1,3 @@ -import sys import os import time from simulation.sim_base import BaseSimulation, run_sim diff --git a/simulation/sim_tools.py b/simulation/sim_tools.py index 8fe8ebd..4720b5e 100644 --- a/simulation/sim_tools.py +++ b/simulation/sim_tools.py @@ -1,5 +1,3 @@ -import sys -import os import time from simulation.sim_base import BaseSimulation, run_sim diff --git a/simulation/workflow_sim.py b/simulation/workflow_sim.py index 532f3a9..a8ab1c7 100644 --- a/simulation/workflow_sim.py +++ b/simulation/workflow_sim.py @@ -1,5 +1,4 @@ import time -import os from api_hook_client import ApiHookClient from simulation.user_agent import UserSimAgent @@ -84,7 +83,7 @@ class WorkflowSimulator: content = last_entry.get('content') print(f"\n[AI]: {content[:100]}...") if "error" in content.lower() or "blocked" in content.lower(): - print(f"[WARN] AI response appears to contain an error message.") + print("[WARN] AI response appears to contain an error message.") return last_entry print("\nTimeout waiting for AI") active_disc = self.client.get_value("active_discussion") diff --git a/summarize.py b/summarize.py index 90320f3..28d9510 100644 --- a/summarize.py +++ b/summarize.py @@ -171,7 +171,7 @@ def summarise_items(file_items: list[dict]) -> list[dict]: content = item.get("content", "") error = item.get("error", False) if error or path is None: - summary = f"_Error reading file_" + summary = "_Error reading file_" else: p = Path(path) if not isinstance(path, Path) else path summary = summarise_file(p, content) diff --git a/test_mma_persistence.py b/test_mma_persistence.py index a3154f6..e2cd411 100644 --- a/test_mma_persistence.py +++ b/test_mma_persistence.py @@ -1,9 +1,7 @@ -import os import unittest from pathlib import Path import project_manager -from models import Track, Ticket class TestMMAPersistence(unittest.TestCase): def test_default_project_has_mma(self) -> None: diff --git a/tests/mock_gemini_cli.py b/tests/mock_gemini_cli.py index bfc05a3..2403b59 100644 --- a/tests/mock_gemini_cli.py +++ b/tests/mock_gemini_cli.py @@ -1,6 +1,5 @@ import sys import json -import subprocess import os def main() -> None: diff --git a/tests/test_agent_capabilities.py b/tests/test_agent_capabilities.py index df52a5c..711dadc 100644 --- a/tests/test_agent_capabilities.py +++ b/tests/test_agent_capabilities.py @@ -1,11 +1,9 @@ -import pytest import sys import os # Ensure project root is in path sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -import ai_client def test_agent_capabilities_listing() -> None: pass diff --git a/tests/test_agent_tools_wiring.py b/tests/test_agent_tools_wiring.py index 61537fc..7e259f6 100644 --- a/tests/test_agent_tools_wiring.py +++ b/tests/test_agent_tools_wiring.py @@ -1,7 +1,5 @@ -import pytest import sys import os -from unittest.mock import MagicMock, patch # Ensure project root is in path sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) diff --git a/tests/test_ai_client_cli.py b/tests/test_ai_client_cli.py index a3f0f54..09dde64 100644 --- a/tests/test_ai_client_cli.py +++ b/tests/test_ai_client_cli.py @@ -1,5 +1,4 @@ -import pytest -from unittest.mock import MagicMock, patch +from unittest.mock import patch import ai_client def test_ai_client_send_gemini_cli() -> None: diff --git a/tests/test_ai_client_list_models.py b/tests/test_ai_client_list_models.py index 70adb45..720b032 100644 --- a/tests/test_ai_client_list_models.py +++ b/tests/test_ai_client_list_models.py @@ -1,5 +1,3 @@ -import pytest -from unittest.mock import patch, MagicMock import ai_client def test_list_models_gemini_cli() -> None: diff --git a/tests/test_ai_style_formatter.py b/tests/test_ai_style_formatter.py index c52b543..12f35ba 100644 --- a/tests/test_ai_style_formatter.py +++ b/tests/test_ai_style_formatter.py @@ -1,4 +1,3 @@ -import pytest import textwrap from scripts.ai_style_formatter import format_code diff --git a/tests/test_api_events.py b/tests/test_api_events.py index dd0bd2f..7a6cae0 100644 --- a/tests/test_api_events.py +++ b/tests/test_api_events.py @@ -36,20 +36,7 @@ def test_event_emission() -> None: callback.assert_called_once_with(payload={"data": 123}) def test_send_emits_events() -> None: - with patch("ai_client._send_gemini") as mock_send_gemini, \ - patch("ai_client._send_anthropic") as mock_send_anthropic: - mock_send_gemini.return_value = "gemini response" - start_callback = MagicMock() - response_callback = MagicMock() - ai_client.events.on("request_start", start_callback) - ai_client.events.on("response_received", response_callback) - ai_client.set_provider("gemini", "gemini-2.5-flash-lite") - ai_client.send("context", "message") - # We mocked _send_gemini so it doesn't emit events inside. - # But wait, ai_client.send itself emits request_start and response_received? - # Actually, ai_client.send delegates to _send_gemini. - # Let's mock _gemini_client instead to let _send_gemini run and emit events. - pass + pytest.fail("TODO: This test is mocked incorrectly and asserts nothing. Use _proper version below.") def test_send_emits_events_proper() -> None: with patch("ai_client._ensure_gemini_client"), \ @@ -72,7 +59,6 @@ def test_send_emits_events_proper() -> None: assert kwargs['payload']['provider'] == 'gemini' def test_send_emits_tool_events() -> None: - import mcp_client with patch("ai_client._ensure_gemini_client"), \ patch("ai_client._gemini_client") as mock_client, \ patch("mcp_client.dispatch") as mock_dispatch: diff --git a/tests/test_api_hook_client.py b/tests/test_api_hook_client.py index 0324e9e..4624094 100644 --- a/tests/test_api_hook_client.py +++ b/tests/test_api_hook_client.py @@ -1,9 +1,5 @@ import pytest -import requests -from unittest.mock import MagicMock, patch -import threading -import time -import json +from unittest.mock import patch import sys import os diff --git a/tests/test_api_hook_extensions.py b/tests/test_api_hook_extensions.py index 5b794b1..ae6e740 100644 --- a/tests/test_api_hook_extensions.py +++ b/tests/test_api_hook_extensions.py @@ -1,4 +1,3 @@ -import pytest import sys import os from typing import Any diff --git a/tests/test_ast_parser_curated.py b/tests/test_ast_parser_curated.py index 2ee0ea6..6edd73d 100644 --- a/tests/test_ast_parser_curated.py +++ b/tests/test_ast_parser_curated.py @@ -1,4 +1,3 @@ -import pytest from file_cache import ASTParser def test_ast_parser_get_curated_view() -> None: diff --git a/tests/test_async_events.py b/tests/test_async_events.py index 3450074..c254215 100644 --- a/tests/test_async_events.py +++ b/tests/test_async_events.py @@ -1,5 +1,4 @@ import asyncio -import pytest from events import AsyncEventQueue def test_async_event_queue_put_get() -> None: diff --git a/tests/test_auto_whitelist.py b/tests/test_auto_whitelist.py index 35efcab..52db39e 100644 --- a/tests/test_auto_whitelist.py +++ b/tests/test_auto_whitelist.py @@ -1,4 +1,3 @@ -import os import pytest from typing import Any from datetime import datetime diff --git a/tests/test_conductor_api_hook_integration.py b/tests/test_conductor_api_hook_integration.py index 19ba95e..2eff33c 100644 --- a/tests/test_conductor_api_hook_integration.py +++ b/tests/test_conductor_api_hook_integration.py @@ -1,10 +1,5 @@ -import pytest -from unittest.mock import MagicMock, patch +from unittest.mock import patch import os -import threading -import time -import json -import requests import sys from typing import Any diff --git a/tests/test_conductor_engine.py b/tests/test_conductor_engine.py index 74b250a..66e3a64 100644 --- a/tests/test_conductor_engine.py +++ b/tests/test_conductor_engine.py @@ -2,7 +2,6 @@ from unittest.mock import MagicMock, patch from models import Ticket, Track, WorkerContext import ai_client -import multi_agent_conductor # These tests define the expected interface for multi_agent_conductor.py # which will be implemented in the next phase of TDD. diff --git a/tests/test_conductor_tech_lead.py b/tests/test_conductor_tech_lead.py index ad174f9..cbab38b 100644 --- a/tests/test_conductor_tech_lead.py +++ b/tests/test_conductor_tech_lead.py @@ -1,5 +1,5 @@ import unittest -from unittest.mock import MagicMock, patch +from unittest.mock import patch import conductor_tech_lead import pytest diff --git a/tests/test_deepseek_infra.py b/tests/test_deepseek_infra.py index 089f4f2..3b67fdc 100644 --- a/tests/test_deepseek_infra.py +++ b/tests/test_deepseek_infra.py @@ -1,9 +1,6 @@ from typing import Any import pytest import os -import tomllib -import tomli_w -from pathlib import Path import sys # Ensure project root is in path diff --git a/tests/test_deepseek_provider.py b/tests/test_deepseek_provider.py index 8aedef2..d8b1412 100644 --- a/tests/test_deepseek_provider.py +++ b/tests/test_deepseek_provider.py @@ -1,4 +1,3 @@ -import pytest from unittest.mock import patch, MagicMock import ai_client diff --git a/tests/test_execution_engine.py b/tests/test_execution_engine.py index 33a6e02..6690288 100644 --- a/tests/test_execution_engine.py +++ b/tests/test_execution_engine.py @@ -1,4 +1,3 @@ -import pytest from models import Ticket from dag_engine import TrackDAG, ExecutionEngine diff --git a/tests/test_gemini_cli_adapter.py b/tests/test_gemini_cli_adapter.py index d0540fd..8ae53ad 100644 --- a/tests/test_gemini_cli_adapter.py +++ b/tests/test_gemini_cli_adapter.py @@ -3,7 +3,6 @@ from typing import Any from unittest.mock import patch, MagicMock import json import subprocess -import io import sys import os diff --git a/tests/test_gemini_cli_adapter_parity.py b/tests/test_gemini_cli_adapter_parity.py index cfd85c4..f083cee 100644 --- a/tests/test_gemini_cli_adapter_parity.py +++ b/tests/test_gemini_cli_adapter_parity.py @@ -1,8 +1,6 @@ import unittest -from unittest.mock import patch, MagicMock, ANY +from unittest.mock import patch, MagicMock import json -import subprocess -import io import sys import os diff --git a/tests/test_gemini_cli_edge_cases.py b/tests/test_gemini_cli_edge_cases.py index 6ec26cc..af52e76 100644 --- a/tests/test_gemini_cli_edge_cases.py +++ b/tests/test_gemini_cli_edge_cases.py @@ -1,9 +1,7 @@ -import pytest -import time +import time import os import sys import requests -import json from typing import Any from api_hook_client import ApiHookClient diff --git a/tests/test_gemini_cli_integration.py b/tests/test_gemini_cli_integration.py index ada5cd9..9d3c480 100644 --- a/tests/test_gemini_cli_integration.py +++ b/tests/test_gemini_cli_integration.py @@ -1,5 +1,4 @@ from typing import Any -import pytest import time import os import sys @@ -23,7 +22,7 @@ def test_gemini_cli_full_integration(live_gui: Any) -> None: # For CI/testing we prefer mock mock_script = os.path.abspath("tests/mock_gemini_cli.py") cli_cmd = f'"{sys.executable}" "{mock_script}"' - print(f"[TEST] Setting current_provider to gemini_cli") + print("[TEST] Setting current_provider to gemini_cli") client.set_value("current_provider", "gemini_cli") print(f"[TEST] Setting gcli_path to {cli_cmd}") client.set_value("gcli_path", cli_cmd) @@ -64,7 +63,7 @@ def test_gemini_cli_full_integration(live_gui: Any) -> None: content = entry.get("content", "") success_markers = ["processed the tool results", "Here are the files", "Here are the lines", "Script hello.ps1 created successfully"] if any(marker in content for marker in success_markers): - print(f"[TEST] Success! Found final message in history.") + print("[TEST] Success! Found final message in history.") found_final = True break if found_final: diff --git a/tests/test_gemini_cli_parity_regression.py b/tests/test_gemini_cli_parity_regression.py index f6b6e03..896a65b 100644 --- a/tests/test_gemini_cli_parity_regression.py +++ b/tests/test_gemini_cli_parity_regression.py @@ -1,6 +1,5 @@ from typing import Any -import pytest -from unittest.mock import patch, MagicMock +from unittest.mock import patch import ai_client @patch('ai_client.GeminiCliAdapter') diff --git a/tests/test_gemini_metrics.py b/tests/test_gemini_metrics.py index f5ecd5a..3c8367a 100644 --- a/tests/test_gemini_metrics.py +++ b/tests/test_gemini_metrics.py @@ -1,4 +1,3 @@ -import pytest import os import sys from unittest.mock import MagicMock, patch diff --git a/tests/test_gui2_events.py b/tests/test_gui2_events.py index c613e95..94cd625 100644 --- a/tests/test_gui2_events.py +++ b/tests/test_gui2_events.py @@ -1,5 +1,5 @@ import pytest -from unittest.mock import MagicMock, patch +from unittest.mock import patch from typing import Generator from gui_2 import App import ai_client diff --git a/tests/test_gui2_parity.py b/tests/test_gui2_parity.py index 84c22e2..00ccc7f 100644 --- a/tests/test_gui2_parity.py +++ b/tests/test_gui2_parity.py @@ -1,7 +1,6 @@ import pytest from typing import Any import time -import json import os import uuid from pathlib import Path diff --git a/tests/test_gui2_performance.py b/tests/test_gui2_performance.py index 6bde01a..fd32092 100644 --- a/tests/test_gui2_performance.py +++ b/tests/test_gui2_performance.py @@ -68,7 +68,7 @@ def test_performance_parity() -> None: # Actually I'll use 0.15 for assertion and log the actual. fps_diff_pct = abs(gui_m["avg_fps"] - gui2_m["avg_fps"]) / gui_m["avg_fps"] if gui_m["avg_fps"] > 0 else 0 cpu_diff_pct = abs(gui_m["avg_cpu"] - gui2_m["avg_cpu"]) / gui_m["avg_cpu"] if gui_m["avg_cpu"] > 0 else 0 - print(f"\n--- Performance Parity Results ---") + print("\n--- Performance Parity Results ---") print(f"FPS Diff: {fps_diff_pct*100:.2f}%") print(f"CPU Diff: {cpu_diff_pct*100:.2f}%") # We follow the 5% requirement for FPS diff --git a/tests/test_gui_async_events.py b/tests/test_gui_async_events.py index d964003..369787d 100644 --- a/tests/test_gui_async_events.py +++ b/tests/test_gui_async_events.py @@ -1,6 +1,5 @@ import pytest -from unittest.mock import MagicMock, patch, AsyncMock -import asyncio +from unittest.mock import MagicMock, patch from gui_2 import App from events import UserRequestEvent diff --git a/tests/test_gui_diagnostics.py b/tests/test_gui_diagnostics.py index bdb19f4..5ed46cf 100644 --- a/tests/test_gui_diagnostics.py +++ b/tests/test_gui_diagnostics.py @@ -46,7 +46,7 @@ def test_diagnostics_panel_updates(app_instance: Any) -> None: app_instance.perf_monitor.get_metrics = MagicMock(return_value=mock_metrics) with patch('dearpygui.dearpygui.is_item_shown', return_value=True), \ patch('dearpygui.dearpygui.set_value') as mock_set_value, \ - patch('dearpygui.dearpygui.configure_item') as mock_configure_item, \ + patch('dearpygui.dearpygui.configure_item'), \ patch('dearpygui.dearpygui.does_item_exist', return_value=True): # We also need to mock ai_client stats with patch('ai_client.get_history_bleed_stats', return_value={}): diff --git a/tests/test_gui_events.py b/tests/test_gui_events.py index 861ad1c..384b8fe 100644 --- a/tests/test_gui_events.py +++ b/tests/test_gui_events.py @@ -1,9 +1,8 @@ import pytest -from unittest.mock import MagicMock, patch +from unittest.mock import patch from typing import Generator import dearpygui.dearpygui as dpg -import gui_legacy from gui_legacy import App import ai_client diff --git a/tests/test_gui_performance_requirements.py b/tests/test_gui_performance_requirements.py index aaaba53..a431d99 100644 --- a/tests/test_gui_performance_requirements.py +++ b/tests/test_gui_performance_requirements.py @@ -1,4 +1,3 @@ -import pytest import time import sys import os @@ -36,7 +35,7 @@ def test_idle_performance_requirements(live_gui) -> None: assert frame_time < 33.3, f"Frame time {frame_time}ms exceeds 30fps threshold" if valid_ft_count == 0 or total_ft == 0: - print(f"[Warning] Frame time is 0.0. This is expected in headless CI/CD environments.") + print("[Warning] Frame time is 0.0. This is expected in headless CI/CD environments.") print(f"[Test] Valid frame time samples: {valid_ft_count}/5") # In some CI environments without a real display, frame time might remain 0 # but we've verified the hook is returning the dictionary. diff --git a/tests/test_gui_phase3.py b/tests/test_gui_phase3.py index 8c5972b..17bd71d 100644 --- a/tests/test_gui_phase3.py +++ b/tests/test_gui_phase3.py @@ -1,5 +1,4 @@ import os -import shutil import json from pathlib import Path from unittest.mock import MagicMock, patch diff --git a/tests/test_gui_phase4.py b/tests/test_gui_phase4.py index 0d0744e..58b9deb 100644 --- a/tests/test_gui_phase4.py +++ b/tests/test_gui_phase4.py @@ -2,8 +2,7 @@ import pytest from unittest.mock import MagicMock, patch from gui_2 import App -from models import Track, Ticket -import project_manager +from models import Track @pytest.fixture def mock_app() -> App: @@ -73,7 +72,7 @@ def test_add_ticket_logic(mock_app: App): assert t["assigned_to"] == "tier3-worker" # Verify form was closed - assert mock_app._show_add_ticket_form == False + assert not mock_app._show_add_ticket_form # Verify push was called mock_push.assert_called_once() @@ -140,7 +139,7 @@ def test_track_discussion_toggle(mock_app: App): mock_app._render_discussion_panel() - assert mock_app._track_discussion_active == True + assert mock_app._track_discussion_active mock_flush.assert_called() mock_load.assert_called_with("track-1", ".") assert len(mock_app.disc_entries) == 1 @@ -158,7 +157,7 @@ def test_track_discussion_toggle(mock_app: App): mock_app._render_discussion_panel() - assert mock_app._track_discussion_active == False + assert not mock_app._track_discussion_active mock_switch.assert_called_with(mock_app.active_discussion) def test_push_mma_state_update(mock_app: App): diff --git a/tests/test_gui_streaming.py b/tests/test_gui_streaming.py index cc78f2b..5c1ff5a 100644 --- a/tests/test_gui_streaming.py +++ b/tests/test_gui_streaming.py @@ -1,8 +1,6 @@ import pytest -import asyncio -from unittest.mock import patch, MagicMock +from unittest.mock import patch from gui_2 import App -import events @pytest.fixture def app_instance(): diff --git a/tests/test_gui_stress_performance.py b/tests/test_gui_stress_performance.py index 9fcdc38..c550b49 100644 --- a/tests/test_gui_stress_performance.py +++ b/tests/test_gui_stress_performance.py @@ -1,4 +1,3 @@ -import pytest import time import sys import os diff --git a/tests/test_gui_updates.py b/tests/test_gui_updates.py index cf52ed2..d95381a 100644 --- a/tests/test_gui_updates.py +++ b/tests/test_gui_updates.py @@ -1,5 +1,5 @@ import pytest -from unittest.mock import patch, MagicMock +from unittest.mock import patch import importlib.util import sys import os @@ -61,7 +61,7 @@ def test_telemetry_panel_updates_correctly(app_instance: Any) -> None: patch('dearpygui.dearpygui.set_value') as mock_set_value, \ patch('dearpygui.dearpygui.configure_item') as mock_configure_item, \ patch('dearpygui.dearpygui.is_item_shown', return_value=False), \ - patch('dearpygui.dearpygui.does_item_exist', return_value=True) as mock_does_item_exist: + patch('dearpygui.dearpygui.does_item_exist', return_value=True): # 4. Call the method under test app_instance._refresh_api_metrics() # 5. Assert the results @@ -88,11 +88,11 @@ def test_cache_data_display_updates_correctly(app_instance: Any) -> None: expected_text = "Gemini Caches: 5 (12.1 KB)" # 3. Patch dependencies app_instance._last_bleed_update_time = 0 # Force update - with patch('ai_client.get_gemini_cache_stats', return_value=mock_cache_stats) as mock_get_cache_stats, \ + with patch('ai_client.get_gemini_cache_stats', return_value=mock_cache_stats), \ patch('dearpygui.dearpygui.set_value') as mock_set_value, \ patch('dearpygui.dearpygui.configure_item') as mock_configure_item, \ patch('dearpygui.dearpygui.is_item_shown', return_value=False), \ - patch('dearpygui.dearpygui.does_item_exist', return_value=True) as mock_does_item_exist: + patch('dearpygui.dearpygui.does_item_exist', return_value=True): # We also need to mock get_history_bleed_stats as it's called in the same function with patch('ai_client.get_history_bleed_stats', return_value={}): # 4. Call the method under test with payload diff --git a/tests/test_headless_verification.py b/tests/test_headless_verification.py index ac616a4..68a3e96 100644 --- a/tests/test_headless_verification.py +++ b/tests/test_headless_verification.py @@ -1,11 +1,9 @@ from typing import Any import pytest -from unittest.mock import MagicMock, patch, call -from models import Ticket, Track, WorkerContext +from unittest.mock import MagicMock, patch +from models import Ticket, Track import multi_agent_conductor from multi_agent_conductor import ConductorEngine -import ai_client -import json @pytest.mark.asyncio async def test_headless_verification_full_run(vlogger) -> None: @@ -121,7 +119,7 @@ async def test_headless_verification_error_and_qa_interceptor(vlogger) -> None: vlogger.log_state("T1 Initial Status", "todo", t1.status) # Patch engine used in test - with patch("multi_agent_conductor.run_worker_lifecycle", wraps=multi_agent_conductor.run_worker_lifecycle) as mock_worker_wrap: + with patch("multi_agent_conductor.run_worker_lifecycle", wraps=multi_agent_conductor.run_worker_lifecycle): await engine.run() vlogger.log_state("T1 Final Status", "todo", t1.status) diff --git a/tests/test_history_management.py b/tests/test_history_management.py index f9e4925..92f22b4 100644 --- a/tests/test_history_management.py +++ b/tests/test_history_management.py @@ -1,10 +1,8 @@ -import pytest import sys import os import tomli_w import tomllib from pathlib import Path -from unittest.mock import MagicMock # Ensure project root is in path for imports sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) @@ -23,7 +21,7 @@ def test_aggregate_includes_segregated_history(tmp_path: Path) -> None: when it's segregated into a separate file. """ proj_path = tmp_path / "manual_slop.toml" - hist_path = tmp_path / "manual_slop_history.toml" + tmp_path / "manual_slop_history.toml" # Setup segregated project configuration proj_data = project_manager.default_project("test-aggregate") proj_data["discussion"]["discussions"]["main"]["history"] = ["@2026-02-24T14:00:00\nUser:\nShow me history"] diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 03c5298..2d22696 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -1,8 +1,5 @@ import os import sys -import pytest -import requests -import json from unittest.mock import patch # Ensure project root is in path diff --git a/tests/test_live_gui_integration.py b/tests/test_live_gui_integration.py index 01438ae..5f31776 100644 --- a/tests/test_live_gui_integration.py +++ b/tests/test_live_gui_integration.py @@ -1,11 +1,10 @@ from typing import Generator import pytest -from unittest.mock import MagicMock, patch, AsyncMock, ANY +from unittest.mock import patch, ANY import asyncio import time from gui_2 import App from events import UserRequestEvent -import ai_client @pytest.fixture def mock_app() -> Generator[App, None, None]: @@ -94,7 +93,7 @@ def test_user_request_error_handling(mock_app: App) -> None: """ app = mock_app with ( - patch('ai_client.send', side_effect=Exception("API Failure")) as mock_send, + patch('ai_client.send', side_effect=Exception("API Failure")), patch('ai_client.set_custom_system_prompt'), patch('ai_client.set_model_params'), patch('ai_client.set_agent_tools') diff --git a/tests/test_live_workflow.py b/tests/test_live_workflow.py index b01caa5..e73e419 100644 --- a/tests/test_live_workflow.py +++ b/tests/test_live_workflow.py @@ -46,12 +46,10 @@ def test_full_live_workflow(live_gui) -> None: client.set_value("ai_input", "Hello! This is an automated test. Just say 'Acknowledged'.") client.click("btn_gen_send") time.sleep(2) # Verify thinking indicator appears (might be brief) - thinking_seen = False print("\nPolling for thinking indicator...") for i in range(40): state = client.get_indicator_state("thinking_indicator") if state.get('shown'): - thinking_seen = True print(f"Thinking indicator seen at poll {i}") break time.sleep(0.5) diff --git a/tests/test_log_management_ui.py b/tests/test_log_management_ui.py index 53a33c1..1f4a224 100644 --- a/tests/test_log_management_ui.py +++ b/tests/test_log_management_ui.py @@ -1,10 +1,8 @@ import pytest from unittest.mock import MagicMock, patch -import os from pathlib import Path # We can safely import gui_2 if we don't instantiate App without mocking its threads -import gui_2 from gui_2 import App @pytest.fixture @@ -69,8 +67,8 @@ def test_render_log_management_logic(app_instance: App) -> None: patch("gui_2.imgui.begin") as mock_begin, \ patch("gui_2.imgui.begin_table") as mock_begin_table, \ patch("gui_2.imgui.text") as mock_text, \ - patch("gui_2.imgui.end_table") as mock_end_table, \ - patch("gui_2.imgui.end") as mock_end, \ + patch("gui_2.imgui.end_table"), \ + patch("gui_2.imgui.end"), \ patch("gui_2.imgui.push_style_color"), \ patch("gui_2.imgui.pop_style_color"), \ patch("gui_2.imgui.table_setup_column"), \ diff --git a/tests/test_log_pruner.py b/tests/test_log_pruner.py index b36fda2..eef9ad6 100644 --- a/tests/test_log_pruner.py +++ b/tests/test_log_pruner.py @@ -1,6 +1,4 @@ from typing import Tuple -import os -import shutil import pytest from pathlib import Path from datetime import datetime, timedelta diff --git a/tests/test_logging_e2e.py b/tests/test_logging_e2e.py index 8a2121d..a77ac25 100644 --- a/tests/test_logging_e2e.py +++ b/tests/test_logging_e2e.py @@ -1,12 +1,8 @@ -import os -import shutil import pytest from typing import Any from pathlib import Path from datetime import datetime, timedelta -from unittest.mock import patch import session_logger -import tomllib from log_registry import LogRegistry from log_pruner import LogPruner diff --git a/tests/test_mcp_perf_tool.py b/tests/test_mcp_perf_tool.py index 250842f..ac4abe7 100644 --- a/tests/test_mcp_perf_tool.py +++ b/tests/test_mcp_perf_tool.py @@ -1,7 +1,6 @@ -import pytest import sys import os -from unittest.mock import MagicMock, patch +from unittest.mock import patch # Ensure project root is in path sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) diff --git a/tests/test_mma_approval_indicators.py b/tests/test_mma_approval_indicators.py index 5bce691..758b45d 100644 --- a/tests/test_mma_approval_indicators.py +++ b/tests/test_mma_approval_indicators.py @@ -1,7 +1,5 @@ from __future__ import annotations -import math -import pytest -from unittest.mock import patch, MagicMock, call +from unittest.mock import patch, MagicMock from gui_2 import App diff --git a/tests/test_mma_dashboard_streams.py b/tests/test_mma_dashboard_streams.py index 7e11da8..4da98b5 100644 --- a/tests/test_mma_dashboard_streams.py +++ b/tests/test_mma_dashboard_streams.py @@ -1,5 +1,4 @@ from __future__ import annotations -import pytest from unittest.mock import patch, MagicMock from gui_2 import App diff --git a/tests/test_mma_models.py b/tests/test_mma_models.py index a07b7e6..a2bcfc9 100644 --- a/tests/test_mma_models.py +++ b/tests/test_mma_models.py @@ -1,4 +1,3 @@ -import pytest from models import Ticket, Track, WorkerContext def test_ticket_instantiation() -> None: diff --git a/tests/test_mma_orchestration_gui.py b/tests/test_mma_orchestration_gui.py index e70b5c1..db43b59 100644 --- a/tests/test_mma_orchestration_gui.py +++ b/tests/test_mma_orchestration_gui.py @@ -1,7 +1,6 @@ import pytest import json -from unittest.mock import patch, MagicMock -import threading +from unittest.mock import patch import time from gui_2 import App @@ -56,8 +55,7 @@ def test_cb_plan_epic_launches_thread(app_instance: App) -> None: with ( patch('orchestrator_pm.get_track_history_summary', return_value="History summary") as mock_get_history, patch('orchestrator_pm.generate_tracks', return_value=mock_tracks) as mock_gen_tracks, - patch('aggregate.build_file_items', return_value=[]) as mock_build_files - ): + patch('aggregate.build_file_items', return_value=[])): # We need to mock project_manager.flat_config and project_manager.load_project with ( patch('project_manager.load_project', return_value={}), diff --git a/tests/test_mma_prompts.py b/tests/test_mma_prompts.py index 45c3c6c..8a9738f 100644 --- a/tests/test_mma_prompts.py +++ b/tests/test_mma_prompts.py @@ -1,4 +1,3 @@ -import pytest from mma_prompts import PROMPTS def test_tier1_epic_init_constraints() -> None: diff --git a/tests/test_mma_ticket_actions.py b/tests/test_mma_ticket_actions.py index 2886acf..5691b90 100644 --- a/tests/test_mma_ticket_actions.py +++ b/tests/test_mma_ticket_actions.py @@ -1,7 +1,6 @@ from typing import Generator import pytest from unittest.mock import patch, MagicMock -import asyncio from gui_2 import App @pytest.fixture diff --git a/tests/test_mock_gemini_cli.py b/tests/test_mock_gemini_cli.py index 2e5d58b..5cd676b 100644 --- a/tests/test_mock_gemini_cli.py +++ b/tests/test_mock_gemini_cli.py @@ -1,6 +1,5 @@ import subprocess import json -import pytest def get_message_content(stdout): diff --git a/tests/test_orchestration_logic.py b/tests/test_orchestration_logic.py index 173b849..de352de 100644 --- a/tests/test_orchestration_logic.py +++ b/tests/test_orchestration_logic.py @@ -1,5 +1,5 @@ import pytest -from unittest.mock import MagicMock, patch +from unittest.mock import patch import json from typing import Any import orchestrator_pm diff --git a/tests/test_orchestrator_pm.py b/tests/test_orchestrator_pm.py index 9e4960d..be84221 100644 --- a/tests/test_orchestrator_pm.py +++ b/tests/test_orchestrator_pm.py @@ -1,6 +1,6 @@ import unittest from typing import Any -from unittest.mock import patch, MagicMock +from unittest.mock import patch import json import orchestrator_pm import mma_prompts @@ -32,7 +32,7 @@ class TestOrchestratorPM(unittest.TestCase): # Verify summarize call mock_summarize.assert_called_once_with(file_items) # Verify ai_client.send call - expected_system_prompt = mma_prompts.PROMPTS['tier1_epic_init'] + mma_prompts.PROMPTS['tier1_epic_init'] mock_send.assert_called_once() args, kwargs = mock_send.call_args self.assertEqual(kwargs['md_content'], "") diff --git a/tests/test_orchestrator_pm_history.py b/tests/test_orchestrator_pm_history.py index ddf39f5..1a12299 100644 --- a/tests/test_orchestrator_pm_history.py +++ b/tests/test_orchestrator_pm_history.py @@ -1,6 +1,5 @@ import unittest from unittest.mock import patch, MagicMock -import os import shutil import json from pathlib import Path diff --git a/tests/test_performance_monitor.py b/tests/test_performance_monitor.py index 34a633d..e37790c 100644 --- a/tests/test_performance_monitor.py +++ b/tests/test_performance_monitor.py @@ -1,4 +1,3 @@ -import pytest import sys import os import time diff --git a/tests/test_phase6_engine.py b/tests/test_phase6_engine.py index 3a3f09b..19379c9 100644 --- a/tests/test_phase6_engine.py +++ b/tests/test_phase6_engine.py @@ -1,8 +1,5 @@ import pytest from unittest.mock import MagicMock, patch, AsyncMock -import asyncio -import json -import multi_agent_conductor from multi_agent_conductor import ConductorEngine, run_worker_lifecycle from models import Ticket, Track, WorkerContext diff --git a/tests/test_process_pending_gui_tasks.py b/tests/test_process_pending_gui_tasks.py index ed6c159..14bacc4 100644 --- a/tests/test_process_pending_gui_tasks.py +++ b/tests/test_process_pending_gui_tasks.py @@ -1,6 +1,6 @@ from typing import Generator import pytest -from unittest.mock import MagicMock, patch +from unittest.mock import patch import ai_client from gui_2 import App diff --git a/tests/test_project_manager_tracks.py b/tests/test_project_manager_tracks.py index 9cbcefc..e52d282 100644 --- a/tests/test_project_manager_tracks.py +++ b/tests/test_project_manager_tracks.py @@ -1,7 +1,6 @@ import pytest from typing import Any import json -from pathlib import Path from project_manager import get_all_tracks, save_track_state from models import TrackState, Metadata, Ticket from datetime import datetime diff --git a/tests/test_session_logging.py b/tests/test_session_logging.py index bd6bfcb..b603a95 100644 --- a/tests/test_session_logging.py +++ b/tests/test_session_logging.py @@ -1,10 +1,6 @@ import pytest -import shutil -import os import tomllib from pathlib import Path -from datetime import datetime -from unittest.mock import patch from typing import Generator import session_logger diff --git a/tests/test_sim_ai_settings.py b/tests/test_sim_ai_settings.py index a899b99..fc461ef 100644 --- a/tests/test_sim_ai_settings.py +++ b/tests/test_sim_ai_settings.py @@ -1,4 +1,3 @@ -import pytest from unittest.mock import MagicMock, patch import os import sys diff --git a/tests/test_sim_base.py b/tests/test_sim_base.py index 5768a16..e3a2af2 100644 --- a/tests/test_sim_base.py +++ b/tests/test_sim_base.py @@ -1,4 +1,3 @@ -import pytest from unittest.mock import MagicMock, patch import os import sys diff --git a/tests/test_sim_context.py b/tests/test_sim_context.py index 3be7ec4..798b5e9 100644 --- a/tests/test_sim_context.py +++ b/tests/test_sim_context.py @@ -1,4 +1,3 @@ -import pytest from unittest.mock import MagicMock, patch import os import sys diff --git a/tests/test_sim_execution.py b/tests/test_sim_execution.py index 875a033..c0c6e50 100644 --- a/tests/test_sim_execution.py +++ b/tests/test_sim_execution.py @@ -1,4 +1,3 @@ -import pytest from unittest.mock import MagicMock, patch import os import sys diff --git a/tests/test_sim_tools.py b/tests/test_sim_tools.py index 85c7d23..8d4842f 100644 --- a/tests/test_sim_tools.py +++ b/tests/test_sim_tools.py @@ -1,4 +1,3 @@ -import pytest from unittest.mock import MagicMock, patch import os import sys diff --git a/tests/test_sync_hooks.py b/tests/test_sync_hooks.py index 0aab231..0a8f39d 100644 --- a/tests/test_sync_hooks.py +++ b/tests/test_sync_hooks.py @@ -1,7 +1,6 @@ import threading import time import requests -import pytest from api_hook_client import ApiHookClient def test_api_ask_client_method(live_gui) -> None: diff --git a/tests/test_tier4_interceptor.py b/tests/test_tier4_interceptor.py index 09003d8..5faa6b6 100644 --- a/tests/test_tier4_interceptor.py +++ b/tests/test_tier4_interceptor.py @@ -1,6 +1,4 @@ -import pytest from unittest.mock import MagicMock, patch -import subprocess from shell_runner import run_powershell def test_run_powershell_qa_callback_on_failure(vlogger) -> None: diff --git a/tests/test_tiered_context.py b/tests/test_tiered_context.py index e49a5c7..36693e4 100644 --- a/tests/test_tiered_context.py +++ b/tests/test_tiered_context.py @@ -1,4 +1,3 @@ -import pytest from typing import Any from pathlib import Path from aggregate import build_tier1_context, build_tier2_context, build_tier3_context @@ -24,8 +23,6 @@ def test_build_tier2_context_exists() -> None: def test_build_tier3_context_ast_skeleton(monkeypatch: Any) -> None: from unittest.mock import MagicMock - import aggregate - import file_cache # Mock ASTParser mock_parser_instance = MagicMock() mock_parser_instance.get_skeleton.return_value = "def other():\n ..." diff --git a/tests/test_token_usage.py b/tests/test_token_usage.py index 9f2eb26..e2307f1 100644 --- a/tests/test_token_usage.py +++ b/tests/test_token_usage.py @@ -1,4 +1,3 @@ -import pytest import sys import os @@ -10,6 +9,5 @@ import ai_client def test_token_usage_tracking() -> None: ai_client.reset_session() # Mock an API response with token usage - usage = {"prompt_tokens": 100, "candidates_tokens": 50, "total_tokens": 150} # This would test the internal accumulator in ai_client pass diff --git a/tests/test_track_state_persistence.py b/tests/test_track_state_persistence.py index 4779ec6..648c29e 100644 --- a/tests/test_track_state_persistence.py +++ b/tests/test_track_state_persistence.py @@ -1,7 +1,4 @@ -import pytest -from pathlib import Path from datetime import datetime -import os # Import the real models from models import TrackState, Metadata, Ticket diff --git a/tests/test_track_state_schema.py b/tests/test_track_state_schema.py index 7346995..bc66561 100644 --- a/tests/test_track_state_schema.py +++ b/tests/test_track_state_schema.py @@ -1,4 +1,3 @@ -import pytest from datetime import datetime, timezone, timedelta # Import necessary classes from models.py diff --git a/tests/test_user_agent.py b/tests/test_user_agent.py index 4f25598..7cec5df 100644 --- a/tests/test_user_agent.py +++ b/tests/test_user_agent.py @@ -1,4 +1,3 @@ -import pytest import sys import os diff --git a/tests/test_vlogger_availability.py b/tests/test_vlogger_availability.py index 0fa0633..543099d 100644 --- a/tests/test_vlogger_availability.py +++ b/tests/test_vlogger_availability.py @@ -1,4 +1,3 @@ -import pytest def test_vlogger_available(vlogger): vlogger.log_state("Test", "Before", "After") diff --git a/tests/test_workflow_sim.py b/tests/test_workflow_sim.py index 38e410c..ffbc4db 100644 --- a/tests/test_workflow_sim.py +++ b/tests/test_workflow_sim.py @@ -1,7 +1,6 @@ -import pytest import sys import os -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock # Ensure project root is in path sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) diff --git a/tests/visual_mma_verification.py b/tests/visual_mma_verification.py index 8f21f12..1c34724 100644 --- a/tests/visual_mma_verification.py +++ b/tests/visual_mma_verification.py @@ -2,7 +2,6 @@ import subprocess import time import sys import os -import glob # --- Configuration --- GUI_SCRIPT = 'gui_2.py' diff --git a/tests/visual_orchestration_verification.py b/tests/visual_orchestration_verification.py index 5b01228..debcb77 100644 --- a/tests/visual_orchestration_verification.py +++ b/tests/visual_orchestration_verification.py @@ -2,7 +2,6 @@ import pytest import time import sys import os -from pathlib import Path # Ensure project root is in path sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) diff --git a/theme_2.py b/theme_2.py index aa69a53..e1c596b 100644 --- a/theme_2.py +++ b/theme_2.py @@ -8,8 +8,7 @@ Font loading uses hello_imgui.load_font(). Scale uses imgui.get_style().font_scale_main. """ -from imgui_bundle import imgui, hello_imgui -from pathlib import Path +from imgui_bundle import imgui # ------------------------------------------------------------------ palettes