did ai's job

This commit is contained in:
2026-05-10 19:57:37 -04:00
parent 9f9d1d42ee
commit 3eacfc39e6
2 changed files with 282 additions and 162 deletions
+130
View File
@@ -0,0 +1,130 @@
# Manual Slop Edit Tool Workflow
## The Problem
The `manual-slop_edit_file` tool requires **exact string matches** (character-for-character). Whitespace differences cause failures. The Python file uses **1-space indentation**.
## The Rules
### 1. ALWAYS Use Small, Incremental Edits
**WRONG:** Replace large blocks (50+ lines)
**RIGHT:** Replace 3-10 lines at a time, verify, repeat
### 2. Verify Before Editing
Before ANY edit to a function you haven't touched recently:
```
1. Run: git checkout -- src/gui_2.py
2. Run: py_check_syntax on src/gui_2.py
3. Get current state with get_file_slice
```
### 3. Reading Before Editing (CRITICAL)
- Use `get_file_slice` to get the EXACT text including all whitespace
- Copy text directly from the tool output - do NOT reformat
- If using get_definition, verify the text matches before editing
### 4. The Edit Tool Parameters (snake_case)
```python
{
"path": "src/gui_2.py", # Required: file path
"old_string": "exact text", # Required: must match EXACTLY
"new_string": "replacement", # Required: replacement text
"replace_all": false # Optional: replace all occurrences
}
```
### 5. 1-Space Indentation in Python
- Class methods: ` def` (0 spaces, then 1)
- Method body: ` ` (2 spaces total)
- Nested blocks: ` ` (3 spaces total)
- NO 4-space indentation anywhere in this file
## Step-by-Step Workflow for gui_2.py
### Before ANY edit:
```powershell
git checkout -- src/gui_2.py
```
### Check current state:
```powershell
py_check_syntax path=src/gui_2.py
get_file_slice path=src/gui_2.py start_line=X end_line=Y
```
### For each edit:
1. Make the smallest possible change (3-10 lines)
2. Run `py_check_syntax` to verify
3. If syntax error, immediately `git checkout -- src/gui_2.py`
4. Only proceed if syntax is OK
### If edit fails with "old_string not found":
- The text you're trying to replace doesn't EXACTLY match
- Use `get_file_slice` to get the exact text
- Copy it character-for-character including whitespace
- Try again with exact match
### If syntax error after edit:
```powershell
git checkout -- src/gui_2.py
```
Then try again with smaller edit.
## Alternative: Update Definition Approach
For large function rewrites, use `py_update_definition`:
```
name: function_name
path: src/gui_2.py
new_content: complete new function source
```
This replaces the entire function at once using AST detection.
## Context Composition Requirements
### Current Broken State
Files & Media works. Context Composition needs:
1. Add state tracking at start of function:
```python
if not hasattr(self, 'ctx_files_open'):
self.ctx_files_open = True
if not hasattr(self, 'ctx_shots_open'):
self.ctx_shots_open = True
```
2. Files section with collapsing header and child window:
```python
if imgui.collapsing_header("Files", self.ctx_files_open):
imgui.begin_child("ctx_files_child", imgui.ImVec2(-1, 200), True)
# table code here
imgui.end_child()
```
3. Screenshots section with collapsing header and child window:
```python
if imgui.collapsing_header("Screenshots", self.ctx_shots_open):
imgui.begin_child("ctx_shots_child", imgui.ImVec2(-1, 100), True)
# screenshot list here
imgui.end_child()
```
4. Fixed presets bar with push_item_width(150) on the combo
5. Remove the batch action bar entirely (Full/Agg/Sig/Def/None/Sel All/Del buttons)
## Key Files
- `src/gui_2.py` - Main GUI (1-space indentation, CRLF)
- `src/models.py` - Data models including FileItem
- Context Composition function: line ~2748
## Test Command
```powershell
uv run sloppy.py
```
## If Everything Goes Wrong
```powershell
git checkout -- src/gui_2.py
git checkout -- src/models.py
```
+152 -162
View File
@@ -2747,185 +2747,175 @@ class App:
imgui.text_disabled("Message & Response panels are detached.") imgui.text_disabled("Message & Response panels are detached.")
def _render_context_composition_panel(self) -> None: def _render_context_composition_panel(self) -> None:
imgui.text("Context Composition") if imgui.collapsing_header("Context Composition"):
imgui.separator() #region: Batch Action Bar
imgui.text("Batch:")
def _batch_helper(name: str, attr: str):
imgui.same_line() imgui.same_line()
if imgui.button(name + "##batch"): if imgui.button("Full##batch"):
for f in self.files: for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f) f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files: if f_path in self.ui_selected_context_files:
f.force_full = True f.force_full = True
f.auto_aggregate = False f.auto_aggregate = False
if hasattr(f, attr): if hasattr(f, "ast_signatures"):
f.ast_signatures = False f.ast_signatures = False
f.ast_definitions = False f.ast_definitions = False
imgui.same_line()
# Batch Action Bar if imgui.button("Agg##batch"):
imgui.text("Batch:") for f in self.files:
imgui.same_line() f_path = f.path if hasattr(f, "path") else str(f)
if imgui.button("Full##batch"): if f_path in self.ui_selected_context_files:
for f in self.files: f.auto_aggregate = True
f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files:
f.force_full = True
f.auto_aggregate = False
if hasattr(f, "ast_signatures"):
f.ast_signatures = False
f.ast_definitions = False
imgui.same_line()
if imgui.button("Agg##batch"):
for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files:
f.auto_aggregate = True
f.force_full = False
if hasattr(f, "ast_signatures"):
f.ast_signatures = False
f.ast_definitions = False
imgui.same_line()
if imgui.button("Sig##batch"):
for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files:
if hasattr(f, "ast_signatures"):
f.ast_signatures = True
f.force_full = False f.force_full = False
if hasattr(f, "ast_signatures"):
f.ast_signatures = False
f.ast_definitions = False
imgui.same_line()
if imgui.button("Sig##batch"):
for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files:
if hasattr(f, "ast_signatures"):
f.ast_signatures = True
f.force_full = False
f.auto_aggregate = False
f.ast_definitions = False
imgui.same_line()
if imgui.button("Def##batch"):
for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files:
if hasattr(f, "ast_definitions"):
f.ast_definitions = True
f.force_full = False
f.auto_aggregate = False
f.ast_signatures = False
imgui.same_line()
if imgui.button("None##batch"):
for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files:
f.auto_aggregate = False f.auto_aggregate = False
f.ast_definitions = False
imgui.same_line()
if imgui.button("Def##batch"):
for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f)
if f_path in self.ui_selected_context_files:
if hasattr(f, "ast_definitions"):
f.ast_definitions = True
f.force_full = False f.force_full = False
f.auto_aggregate = False if hasattr(f, "ast_signatures"):
f.ast_signatures = False f.ast_signatures = False
imgui.same_line() f.ast_definitions = False
if imgui.button("None##batch"): imgui.same_line()
for f in self.files: if imgui.button("Sel All##selall"):
f_path = f.path if hasattr(f, "path") else str(f) for f in self.files:
if f_path in self.ui_selected_context_files: f_path = f.path if hasattr(f, "path") else str(f)
f.auto_aggregate = False self.ui_selected_context_files.add(f_path)
f.force_full = False imgui.same_line()
if hasattr(f, "ast_signatures"): if imgui.button("Unsel All##unselall"):
f.ast_signatures = False self.ui_selected_context_files.clear()
f.ast_definitions = False imgui.same_line()
imgui.same_line() if imgui.button("Del##batch"):
if imgui.button("Sel All##selall"): new_files = []
for f in self.files: for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f) f_path = f.path if hasattr(f, "path") else str(f)
self.ui_selected_context_files.add(f_path) if f_path not in self.ui_selected_context_files:
imgui.same_line() new_files.append(f)
if imgui.button("Unsel All##unselall"): self.files = new_files
self.ui_selected_context_files.clear() self.ui_selected_context_files.clear()
imgui.same_line() #endregion: Batch Action Bar
if imgui.button("Del##batch"):
new_files = [] imgui.dummy(imgui.ImVec2(0, 4))
for f in self.files:
f_path = f.path if hasattr(f, "path") else str(f)
if f_path not in self.ui_selected_context_files:
new_files.append(f)
self.files = new_files
self.ui_selected_context_files.clear()
imgui.dummy(imgui.ImVec2(0, 4))
if imgui.begin_table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders): if imgui.begin_table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders):
imgui.table_setup_column("File", imgui.TableColumnFlags_.width_stretch) imgui.table_setup_column("File", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200) imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200)
imgui.table_headers_row() imgui.table_headers_row()
for i, f_item in enumerate(self.files): for i, f_item in enumerate(self.files):
imgui.table_next_row() imgui.table_next_row()
imgui.table_set_column_index(0) imgui.table_set_column_index(0)
# Checkbox for selection # Checkbox for selection
f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
is_sel = f_path in self.ui_selected_context_files
changed_sel, is_sel = imgui.checkbox(f"##sel{i}", is_sel)
if changed_sel:
if imgui.get_io().key_shift and self._last_selected_context_index != -1:
start = min(self._last_selected_context_index, i)
end = max(self._last_selected_context_index, i)
for idx in range(start, end + 1):
item = self.files[idx]
item_path = item.path if hasattr(item, "path") else str(item)
if is_sel:
self.ui_selected_context_files.add(item_path)
else:
self.ui_selected_context_files.discard(item_path)
else:
if is_sel:
self.ui_selected_context_files.add(f_path)
else:
self.ui_selected_context_files.discard(f_path)
self._last_selected_context_index = i
imgui.same_line()
imgui.text(f_path)
if f_path.lower().endswith(('.c', '.cpp', '.h', '.hpp', '.cxx', '.cc')):
imgui.same_line()
if imgui.button(f"[Inspect]##{i}"):
self.ui_inspecting_ast_file = f_item
imgui.open_popup('AST Inspector')
imgui.same_line()
if imgui.button(f"[Slices]##{i}"):
self.ui_editing_slices_file = f_item
f_path = f_item.path if hasattr(f_item, "path") else str(f_item) f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
self.text_viewer_title = f"Slices: {f_path}" is_sel = f_path in self.ui_selected_context_files
self.text_viewer_content = f_item.content or "" changed_sel, is_sel = imgui.checkbox(f"##sel{i}", is_sel)
self.text_viewer_type = 'cpp' if f_path.endswith(('.cpp', '.hpp', '.h')) else 'python' if f_path.endswith('.py') else 'text' if changed_sel:
self.show_text_viewer = True if imgui.get_io().key_shift and self._last_selected_context_index != -1:
start = min(self._last_selected_context_index, i)
imgui.table_set_column_index(1) end = max(self._last_selected_context_index, i)
if hasattr(f_item, "auto_aggregate"): for idx in range(start, end + 1):
changed_agg, f_item.auto_aggregate = imgui.checkbox(f"Agg##cc{i}", f_item.auto_aggregate) item = self.files[idx]
item_path = item.path if hasattr(item, "path") else str(item)
if is_sel:
self.ui_selected_context_files.add(item_path)
else:
self.ui_selected_context_files.discard(item_path)
else:
if is_sel:
self.ui_selected_context_files.add(f_path)
else:
self.ui_selected_context_files.discard(f_path)
self._last_selected_context_index = i
imgui.same_line() imgui.same_line()
changed_full, f_item.force_full = imgui.checkbox(f"Full##cc{i}", f_item.force_full) imgui.text(f_path)
if hasattr(f_item, "ast_signatures"):
if f_path.lower().endswith(('.c', '.cpp', '.h', '.hpp', '.cxx', '.cc')):
imgui.same_line() imgui.same_line()
_, f_item.ast_signatures = imgui.checkbox(f"Sig##cc{i}", f_item.ast_signatures) if imgui.button(f"[Inspect]##{i}"):
self.ui_inspecting_ast_file = f_item
imgui.open_popup('AST Inspector')
imgui.same_line()
if imgui.button(f"[Slices]##{i}"):
self.ui_editing_slices_file = f_item
f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
self.text_viewer_title = f"Slices: {f_path}"
self.text_viewer_content = f_item.content or ""
self.text_viewer_type = 'cpp' if f_path.endswith(('.cpp', '.hpp', '.h')) else 'python' if f_path.endswith('.py') else 'text'
self.show_text_viewer = True
imgui.table_set_column_index(1)
if hasattr(f_item, "auto_aggregate"):
changed_agg, f_item.auto_aggregate = imgui.checkbox(f"Agg##cc{i}", f_item.auto_aggregate)
imgui.same_line() imgui.same_line()
_, f_item.ast_definitions = imgui.checkbox(f"Def##cc{i}", f_item.ast_definitions) changed_full, f_item.force_full = imgui.checkbox(f"Full##cc{i}", f_item.force_full)
imgui.end_table() if hasattr(f_item, "ast_signatures"):
imgui.same_line()
_, f_item.ast_signatures = imgui.checkbox(f"Sig##cc{i}", f_item.ast_signatures)
imgui.same_line()
_, f_item.ast_definitions = imgui.checkbox(f"Def##cc{i}", f_item.ast_definitions)
imgui.end_table()
# Context Composition collasping header
imgui.separator() imgui.separator()
imgui.text("Screenshots")
for i, s in enumerate(self.screenshots): if imgui.collapsing_header("Screenshots"):
imgui.text(s) for i, s in enumerate(self.screenshots):
imgui.separator() imgui.text(s)
imgui.text("Presets") imgui.separator()
presets = self.controller.project.get('context_presets', {}) imgui.text("Presets")
preset_names = [""] + sorted(presets.keys()) presets = self.controller.project.get('context_presets', {})
active = getattr(self, "ui_active_context_preset", "") preset_names = [""] + sorted(presets.keys())
if active not in preset_names: active = getattr(self, "ui_active_context_preset", "")
active = "" if active not in preset_names:
try: active = ""
idx = preset_names.index(active) try:
except ValueError: idx = preset_names.index(active)
idx = 0 except ValueError:
ch, new_idx = imgui.combo("##ctx_preset", idx, preset_names) idx = 0
if ch: ch, new_idx = imgui.combo("##ctx_preset", idx, preset_names)
self.ui_active_context_preset = preset_names[new_idx] if ch:
if preset_names[new_idx]: self.ui_active_context_preset = preset_names[new_idx]
self.load_context_preset(preset_names[new_idx]) if preset_names[new_idx]:
imgui.same_line() self.load_context_preset(preset_names[new_idx])
changed, new_name = imgui.input_text("##new_preset", getattr(self, "ui_new_context_preset_name", "")) imgui.same_line()
if changed: changed, new_name = imgui.input_text("##new_preset", getattr(self, "ui_new_context_preset_name", ""))
self.ui_new_context_preset_name = new_name if changed:
imgui.same_line() self.ui_new_context_preset_name = new_name
if imgui.button("Save##ctx"): imgui.same_line()
if getattr(self, "ui_new_context_preset_name", "").strip(): if imgui.button("Save##ctx"):
self.save_context_preset(self.ui_new_context_preset_name.strip()) if getattr(self, "ui_new_context_preset_name", "").strip():
self.ui_new_context_preset_name = "" self.save_context_preset(self.ui_new_context_preset_name.strip())
imgui.same_line() self.ui_new_context_preset_name = ""
if imgui.button("Delete##ctx"): imgui.same_line()
if getattr(self, "ui_active_context_preset", ""): if imgui.button("Delete##ctx"):
self.delete_context_preset(self.ui_active_context_preset) if getattr(self, "ui_active_context_preset", ""):
self.ui_active_context_preset = "" self.delete_context_preset(self.ui_active_context_preset)
self.ui_active_context_preset = ""
def _render_snapshot_tab(self) -> None: def _render_snapshot_tab(self) -> None:
if imgui.begin_tab_bar("snapshot_tabs"): if imgui.begin_tab_bar("snapshot_tabs"):