chore(conductor): Complete Phase 1 of AI style refactor

This commit is contained in:
2026-02-27 23:52:06 -05:00
parent c75b926c45
commit db65162bbf
4 changed files with 556 additions and 335 deletions

View File

@@ -1,12 +1,12 @@
# Implementation Plan: AI-Optimized Python Style Refactor # Implementation Plan: AI-Optimized Python Style Refactor
## Phase 1: Research and Pilot Tooling ## Phase 1: Research and Pilot Tooling
- [ ] Task: Conductor - Define and Test Style Transformation Logic. (Develop or adapt a tool to perform 1-space indentation and newline reduction safely). - [x] Task: Conductor - Define and Test Style Transformation Logic. (Develop or adapt a tool to perform 1-space indentation and newline reduction safely). [c75b926]
- [ ] Task: Conductor - Run Style Pilot on a Representative Module (e.g., `theme.py`). - [x] Task: Conductor - Run Style Pilot on a Representative Module (e.g., `theme.py`). [13c15ed]
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Pilot and Tooling' (Protocol in workflow.md) - [x] Task: Conductor - User Manual Verification 'Phase 1: Pilot and Tooling' (Protocol in workflow.md) [checkpoint: Phase1]
## Phase 2: Core Refactor - Indentation and Newlines ## Phase 2: Core Refactor - Indentation and Newlines
- [ ] Task: Conductor - Refactor Primary Engine Modules (`ai_client.py`, `aggregate.py`, `mcp_client.py`, `shell_runner.py`). - [~] Task: Conductor - Refactor Primary Engine Modules (`ai_client.py`, `aggregate.py`, `mcp_client.py`, `shell_runner.py`).
- [ ] Task: Conductor - Refactor Project & Session Management Modules (`project_manager.py`, `session_logger.py`). - [ ] Task: Conductor - Refactor Project & Session Management Modules (`project_manager.py`, `session_logger.py`).
- [ ] Task: Conductor - Refactor UI Modules (`gui_2.py`, `gui_legacy.py`, `theme.py`, `theme_2.py`). - [ ] Task: Conductor - Refactor UI Modules (`gui_2.py`, `gui_legacy.py`, `theme.py`, `theme_2.py`).
- [ ] Task: Conductor - Refactor Remaining Utility and Support Modules (`events.py`, `file_cache.py`, `models.py`, `mma_prompts.py`). - [ ] Task: Conductor - Refactor Remaining Utility and Support Modules (`events.py`, `file_cache.py`, `models.py`, `mma_prompts.py`).

View File

@@ -0,0 +1,125 @@
import tokenize
import io
def format_code(source: str) -> str:
"""
Formats Python code to use exactly 1 space for indentation (including continuations),
max 1 blank line between top-level definitions, and 0 blank lines inside
function/method bodies.
Args:
source: The Python source code to format.
Returns:
The formatted source code.
"""
if not source:
return ""
tokens = list(tokenize.generate_tokens(io.StringIO(source).readline))
lines = source.splitlines(keepends=True)
num_lines = len(lines)
block_level = 0
paren_level = 0
in_function_stack = []
expecting_function_indent = False
line_indent = {}
line_is_blank = {i: True for i in range(1, num_lines + 2)}
line_is_string_interior = {i: False for i in range(1, num_lines + 2)}
line_seen = set()
pending_blank_lines = []
for tok in tokens:
t_type = tok.type
t_string = tok.string
start_line, _ = tok.start
end_line, _ = tok.end
if t_type == tokenize.STRING:
for l in range(start_line + 1, end_line + 1):
line_is_string_interior[l] = True
if t_type not in (tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT, tokenize.ENDMARKER):
for l in range(start_line, end_line + 1):
line_is_blank[l] = False
pending_blank_lines = [] # Real content seen, clear pending blanks
# State updates that affect CURRENT line
if t_type == tokenize.INDENT:
block_level += 1
if expecting_function_indent:
in_function_stack.append(block_level)
expecting_function_indent = False
elif t_type == tokenize.DEDENT:
block_level -= 1
if in_function_stack and block_level < in_function_stack[-1]:
in_function_stack.pop()
# Retroactively update pending blank lines to the current (outer) level
for l in pending_blank_lines:
line_indent[l] = block_level + paren_level
if t_string in (')', ']', '}'):
paren_level -= 1
if start_line not in line_seen:
line_indent[start_line] = block_level + paren_level
if t_type not in (tokenize.INDENT, tokenize.DEDENT):
line_seen.add(start_line)
if t_type in (tokenize.NL, tokenize.NEWLINE):
pending_blank_lines.append(start_line)
# State updates that affect FUTURE lines/tokens
if t_type == tokenize.NAME and t_string == 'def':
expecting_function_indent = True
if t_string in ('(', '[', '{'):
paren_level += 1
output = []
consecutive_blanks = 0
for i in range(1, num_lines + 1):
if line_is_string_interior[i]:
output.append(lines[i-1])
continue
if line_is_blank[i]:
indent = line_indent.get(i, 0)
if indent > 0:
continue
else:
if consecutive_blanks < 1:
output.append("\n")
consecutive_blanks += 1
continue
consecutive_blanks = 0
original_line = lines[i-1]
indent = line_indent.get(i, 0)
stripped = original_line.lstrip()
output.append(" " * indent + stripped)
if not stripped.endswith('\n') and i < num_lines:
output[-1] += '\n'
if output and not output[-1].endswith('\n'):
output[-1] += '\n'
return "".join(output)
if __name__ == "__main__":
import sys
import os
if len(sys.argv) > 1:
file_path = sys.argv[1]
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
formatted = format_code(content)
if len(sys.argv) > 2 and sys.argv[2] == "--write":
with open(file_path, "w", encoding="utf-8") as f:
f.write(formatted)
else:
sys.stdout.reconfigure(encoding='utf-8')
sys.stdout.write(formatted)

View File

@@ -0,0 +1,122 @@
import pytest
import textwrap
from scripts.ai_style_formatter import format_code
def test_basic_indentation():
source = textwrap.dedent("""\
def hello():
print("world")
if True:
print("nested")
""")
expected = (
"def hello():\n"
" print(\"world\")\n"
" if True:\n"
" print(\"nested\")\n"
)
assert format_code(source) == expected
def test_top_level_blank_lines():
source = textwrap.dedent("""\
def a():
pass
def b():
pass
""")
expected = (
"def a():\n"
" pass\n"
"\n"
"def b():\n"
" pass\n"
)
assert format_code(source) == expected
def test_inner_blank_lines():
source = textwrap.dedent("""\
def a():
print("start")
print("end")
""")
expected = (
"def a():\n"
" print(\"start\")\n"
" print(\"end\")\n"
)
assert format_code(source) == expected
def test_multiline_string_safety():
source = textwrap.dedent("""\
def a():
'''
This is a multiline
string that should
not be reformatted
inside.
'''
pass
""")
# Note: the indentation of the ''' itself becomes 1 space.
# The content inside remains exactly as in source.
# textwrap.dedent will remove the common leading whitespace from the source.
# The source's ''' is at 4 spaces. Content is at 4 spaces.
# After dedent:
# def a():
# '''
# This is a...
result = format_code(source)
assert " This is a multiline" in result
assert result.startswith("def a():\n '''")
def test_continuation_indentation():
source = textwrap.dedent("""\
def long_func(
a,
b
):
return (
a +
b
)
""")
expected = (
"def long_func(\n"
" a,\n"
" b\n"
"):\n"
" return (\n"
" a +\n"
" b\n"
" )\n"
)
assert format_code(source) == expected
def test_multiple_top_level_definitions():
source = textwrap.dedent("""\
class MyClass:
def __init__(self):
self.x = 1
def method(self):
pass
def top_level():
pass
""")
expected = (
"class MyClass:\n"
" def __init__(self):\n"
" self.x = 1\n"
" def method(self):\n"
" pass\n"
"\n"
"def top_level():\n"
" pass\n"
)
assert format_code(source) == expected

View File

@@ -29,9 +29,7 @@ from pathlib import Path
# Only keys that differ from DPG defaults need to be listed. # Only keys that differ from DPG defaults need to be listed.
_PALETTES: dict[str, dict] = { _PALETTES: dict[str, dict] = {
"DPG Default": {}, # empty = reset to DPG built-in defaults "DPG Default": {}, # empty = reset to DPG built-in defaults
"10x Dark": { "10x Dark": {
# Window / frame chrome # Window / frame chrome
"WindowBg": ( 34, 32, 28), "WindowBg": ( 34, 32, 28),
@@ -100,7 +98,6 @@ _PALETTES: dict[str, dict] = {
"NavWindowingDimBg": ( 20, 20, 20, 80), "NavWindowingDimBg": ( 20, 20, 20, 80),
"ModalWindowDimBg": ( 10, 10, 10, 100), "ModalWindowDimBg": ( 10, 10, 10, 100),
}, },
"Nord Dark": { "Nord Dark": {
"WindowBg": ( 36, 41, 49), "WindowBg": ( 36, 41, 49),
"ChildBg": ( 30, 34, 42), "ChildBg": ( 30, 34, 42),
@@ -151,7 +148,6 @@ _PALETTES: dict[str, dict] = {
"NavHighlight": (136, 192, 208), "NavHighlight": (136, 192, 208),
"ModalWindowDimBg": ( 10, 12, 16, 100), "ModalWindowDimBg": ( 10, 12, 16, 100),
}, },
"Monokai": { "Monokai": {
"WindowBg": ( 39, 40, 34), "WindowBg": ( 39, 40, 34),
"ChildBg": ( 34, 35, 29), "ChildBg": ( 34, 35, 29),
@@ -272,34 +268,27 @@ _current_font_path: str = ""
_current_font_size: float = 14.0 _current_font_size: float = 14.0
_current_scale: float = 1.0 _current_scale: float = 1.0
# ------------------------------------------------------------------ public API # ------------------------------------------------------------------ public API
def get_palette_names() -> list[str]: def get_palette_names() -> list[str]:
return list(_PALETTES.keys()) return list(_PALETTES.keys())
def get_current_palette() -> str: def get_current_palette() -> str:
return _current_palette return _current_palette
def get_current_font_path() -> str: def get_current_font_path() -> str:
return _current_font_path return _current_font_path
def get_current_font_size() -> float: def get_current_font_size() -> float:
return _current_font_size return _current_font_size
def get_current_scale() -> float: def get_current_scale() -> float:
return _current_scale return _current_scale
def get_palette_colours(name: str) -> dict: def get_palette_colours(name: str) -> dict:
"""Return a copy of the colour dict for the named palette.""" """Return a copy of the colour dict for the named palette."""
return dict(_PALETTES.get(name, {})) return dict(_PALETTES.get(name, {}))
def apply(palette_name: str, overrides: dict | None = None): def apply(palette_name: str, overrides: dict | None = None):
""" """
Build a global DPG theme from the named palette plus optional per-colour Build a global DPG theme from the named palette plus optional per-colour
@@ -308,12 +297,10 @@ def apply(palette_name: str, overrides: dict | None = None):
overrides: {colour_key: (R,G,B) or (R,G,B,A)} — merged on top of palette. overrides: {colour_key: (R,G,B) or (R,G,B,A)} — merged on top of palette.
""" """
global _current_theme_tag, _current_palette global _current_theme_tag, _current_palette
_current_palette = palette_name _current_palette = palette_name
colours = dict(_PALETTES.get(palette_name, {})) colours = dict(_PALETTES.get(palette_name, {}))
if overrides: if overrides:
colours.update(overrides) colours.update(overrides)
# Delete the old theme if one exists # Delete the old theme if one exists
if _current_theme_tag is not None: if _current_theme_tag is not None:
try: try:
@@ -321,7 +308,6 @@ def apply(palette_name: str, overrides: dict | None = None):
except Exception: except Exception:
pass pass
_current_theme_tag = None _current_theme_tag = None
if palette_name == "DPG Default" and not overrides: if palette_name == "DPG Default" and not overrides:
# Bind an empty theme to reset to DPG defaults # Bind an empty theme to reset to DPG defaults
with dpg.theme() as t: with dpg.theme() as t:
@@ -330,7 +316,6 @@ def apply(palette_name: str, overrides: dict | None = None):
dpg.bind_theme(t) dpg.bind_theme(t)
_current_theme_tag = t _current_theme_tag = t
return return
with dpg.theme() as t: with dpg.theme() as t:
with dpg.theme_component(dpg.mvAll): with dpg.theme_component(dpg.mvAll):
for name, colour in colours.items(): for name, colour in colours.items():
@@ -344,11 +329,9 @@ def apply(palette_name: str, overrides: dict | None = None):
if len(colour) == 3: if len(colour) == 3:
colour = (*colour, 255) colour = (*colour, 255)
dpg.add_theme_color(const, colour) dpg.add_theme_color(const, colour)
dpg.bind_theme(t) dpg.bind_theme(t)
_current_theme_tag = t _current_theme_tag = t
def apply_font(font_path: str, size: float = 14.0): def apply_font(font_path: str, size: float = 14.0):
""" """
Load the TTF at font_path at the given point size and bind it globally. Load the TTF at font_path at the given point size and bind it globally.
@@ -357,21 +340,17 @@ def apply_font(font_path: str, size: float = 14.0):
resets to the DPG built-in font. resets to the DPG built-in font.
""" """
global _current_font_tag, _current_font_path, _current_font_size, _font_registry_tag global _current_font_tag, _current_font_path, _current_font_size, _font_registry_tag
_current_font_path = font_path _current_font_path = font_path
_current_font_size = size _current_font_size = size
if not font_path or not Path(font_path).exists(): if not font_path or not Path(font_path).exists():
# Reset to default built-in font # Reset to default built-in font
dpg.bind_font(0) dpg.bind_font(0)
_current_font_tag = None _current_font_tag = None
return return
# Create the registry once # Create the registry once
if _font_registry_tag is None or not dpg.does_item_exist(_font_registry_tag): if _font_registry_tag is None or not dpg.does_item_exist(_font_registry_tag):
with dpg.font_registry() as reg: with dpg.font_registry() as reg:
_font_registry_tag = reg _font_registry_tag = reg
# Delete previous custom font item only (not the registry) # Delete previous custom font item only (not the registry)
if _current_font_tag is not None: if _current_font_tag is not None:
try: try:
@@ -379,19 +358,16 @@ def apply_font(font_path: str, size: float = 14.0):
except Exception: except Exception:
pass pass
_current_font_tag = None _current_font_tag = None
font = dpg.add_font(font_path, size, parent=_font_registry_tag) font = dpg.add_font(font_path, size, parent=_font_registry_tag)
_current_font_tag = font _current_font_tag = font
dpg.bind_font(font) dpg.bind_font(font)
def set_scale(factor: float): def set_scale(factor: float):
"""Set the global Dear PyGui font/UI scale factor.""" """Set the global Dear PyGui font/UI scale factor."""
global _current_scale global _current_scale
_current_scale = factor _current_scale = factor
dpg.set_global_font_scale(factor) dpg.set_global_font_scale(factor)
def save_to_config(config: dict): def save_to_config(config: dict):
"""Persist theme settings into the config dict under [theme].""" """Persist theme settings into the config dict under [theme]."""
config.setdefault("theme", {}) config.setdefault("theme", {})
@@ -400,7 +376,6 @@ def save_to_config(config: dict):
config["theme"]["font_size"] = _current_font_size config["theme"]["font_size"] = _current_font_size
config["theme"]["scale"] = _current_scale config["theme"]["scale"] = _current_scale
def load_from_config(config: dict): def load_from_config(config: dict):
"""Read [theme] from config and apply everything.""" """Read [theme] from config and apply everything."""
t = config.get("theme", {}) t = config.get("theme", {})
@@ -408,7 +383,6 @@ def load_from_config(config: dict):
font_path = t.get("font_path", "") font_path = t.get("font_path", "")
font_size = float(t.get("font_size", 14.0)) font_size = float(t.get("font_size", 14.0))
scale = float(t.get("scale", 1.0)) scale = float(t.get("scale", 1.0))
apply(palette) apply(palette)
if font_path: if font_path:
apply_font(font_path, font_size) apply_font(font_path, font_size)