Private
Public Access
remove outdated scripts
This commit is contained in:
@@ -1,131 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
import tokenize
|
|
||||||
import io
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
if not source:
|
|
||||||
return ""
|
|
||||||
try:
|
|
||||||
tokens = list(tokenize.generate_tokens(io.StringIO(source).readline))
|
|
||||||
except tokenize.TokenError:
|
|
||||||
return source # Return as-is if it's not valid python (e.g. template files)
|
|
||||||
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 = []
|
|
||||||
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()
|
|
||||||
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)
|
|
||||||
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
|
|
||||||
original_line = lines[i-1]
|
|
||||||
indent = line_indent.get(i, 0)
|
|
||||||
stripped = original_line.lstrip()
|
|
||||||
is_def_start = stripped.startswith(('def ', 'class ', 'async def ', '@'))
|
|
||||||
if is_def_start and output and consecutive_blanks == 0:
|
|
||||||
prev_line = output[-1].strip()
|
|
||||||
if prev_line and not prev_line.endswith(':') and not prev_line.startswith('@'):
|
|
||||||
output.append("\n")
|
|
||||||
consecutive_blanks += 1
|
|
||||||
consecutive_blanks = 0
|
|
||||||
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)
|
|
||||||
|
|
||||||
def process_file(file_path: str, write: bool) -> None:
|
|
||||||
try:
|
|
||||||
with open(file_path, "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
formatted = format_code(content)
|
|
||||||
if write:
|
|
||||||
if formatted != content:
|
|
||||||
with open(file_path, "w", encoding="utf-8") as f:
|
|
||||||
f.write(formatted)
|
|
||||||
print(f"Formatted: {file_path}")
|
|
||||||
else:
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8')
|
|
||||||
sys.stdout.write(formatted)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error processing {file_path}: {e}")
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
import argparse
|
|
||||||
parser = argparse.ArgumentParser(description="AI-optimized Python code formatter.")
|
|
||||||
parser.add_argument("paths", nargs="+", help="Files or directories to format.")
|
|
||||||
parser.add_argument("--write", action="store_true", help="Write changes back to files.")
|
|
||||||
parser.add_argument("--exclude", nargs="*", default=[".venv", "__pycache__", ".git"], help="Directories to exclude.")
|
|
||||||
args = parser.parse_args()
|
|
||||||
for path in args.paths:
|
|
||||||
if os.path.isfile(path):
|
|
||||||
process_file(path, args.write)
|
|
||||||
elif os.path.isdir(path):
|
|
||||||
for root, dirs, files in os.walk(path):
|
|
||||||
dirs[:] = [d for d in dirs if d not in args.exclude]
|
|
||||||
for file in files:
|
|
||||||
if file.endswith(".py"):
|
|
||||||
process_file(os.path.join(root, file), args.write)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,361 +0,0 @@
|
|||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Symbols to check (extracted from previous step)
|
|
||||||
# This is a representative sample, we will process all 327.
|
|
||||||
# For the actual implementation, I'll read the output of the previous command.
|
|
||||||
raw_symbols = r"""
|
|
||||||
find_next_increment|src\aggregate.py
|
|
||||||
is_absolute_with_drive|src\aggregate.py
|
|
||||||
resolve_paths|src\aggregate.py
|
|
||||||
build_discussion_section|src\aggregate.py
|
|
||||||
build_files_section|src\aggregate.py
|
|
||||||
build_screenshots_section|src\aggregate.py
|
|
||||||
build_file_items|src\aggregate.py
|
|
||||||
build_summary_section|src\aggregate.py
|
|
||||||
build_beads_section|src\aggregate.py
|
|
||||||
build_markdown_from_items|src\aggregate.py
|
|
||||||
build_markdown_no_history|src\aggregate.py
|
|
||||||
build_discussion_text|src\aggregate.py
|
|
||||||
build_tier1_context|src\aggregate.py
|
|
||||||
build_tier2_context|src\aggregate.py
|
|
||||||
build_tier3_context|src\aggregate.py
|
|
||||||
build_markdown|src\aggregate.py
|
|
||||||
run|src\aggregate.py
|
|
||||||
main|src\aggregate.py
|
|
||||||
set_model_params|src\ai_client.py
|
|
||||||
get_history_trunc_limit|src\ai_client.py
|
|
||||||
set_history_trunc_limit|src\ai_client.py
|
|
||||||
get_current_tier|src\ai_client.py
|
|
||||||
set_current_tier|src\ai_client.py
|
|
||||||
get_comms_log_callback|src\ai_client.py
|
|
||||||
set_comms_log_callback|src\ai_client.py
|
|
||||||
set_custom_system_prompt|src\ai_client.py
|
|
||||||
set_base_system_prompt|src\ai_client.py
|
|
||||||
set_use_default_base_prompt|src\ai_client.py
|
|
||||||
set_project_context_marker|src\ai_client.py
|
|
||||||
get_combined_system_prompt|src\ai_client.py
|
|
||||||
get_comms_log|src\ai_client.py
|
|
||||||
clear_comms_log|src\ai_client.py
|
|
||||||
get_credentials_path|src\ai_client.py
|
|
||||||
ProviderError|src\ai_client.py
|
|
||||||
set_provider|src\ai_client.py
|
|
||||||
get_provider|src\ai_client.py
|
|
||||||
cleanup|src\ai_client.py
|
|
||||||
reset_session|src\ai_client.py
|
|
||||||
get_gemini_cache_stats|src\ai_client.py
|
|
||||||
list_models|src\ai_client.py
|
|
||||||
set_agent_tools|src\ai_client.py
|
|
||||||
set_tool_preset|src\ai_client.py
|
|
||||||
set_bias_profile|src\ai_client.py
|
|
||||||
get_bias_profile|src\ai_client.py
|
|
||||||
run_tier4_analysis|src\ai_client.py
|
|
||||||
run_tier4_patch_callback|src\ai_client.py
|
|
||||||
run_tier4_patch_generation|src\ai_client.py
|
|
||||||
get_token_stats|src\ai_client.py
|
|
||||||
send|src\ai_client.py
|
|
||||||
get_history_bleed_stats|src\ai_client.py
|
|
||||||
run_subagent_summarization|src\ai_client.py
|
|
||||||
HookServerInstance|src\api_hooks.py
|
|
||||||
HookHandler|src\api_hooks.py
|
|
||||||
HookServer|src\api_hooks.py
|
|
||||||
WebSocketServer|src\api_hooks.py
|
|
||||||
ApiHookClient|src\api_hook_client.py
|
|
||||||
hide_tk_root|src\app_controller.py
|
|
||||||
parse_symbols|src\app_controller.py
|
|
||||||
get_symbol_definition|src\app_controller.py
|
|
||||||
GenerateRequest|src\app_controller.py
|
|
||||||
ConfirmRequest|src\app_controller.py
|
|
||||||
ConfirmDialog|src\app_controller.py
|
|
||||||
MMAApprovalDialog|src\app_controller.py
|
|
||||||
MMASpawnApprovalDialog|src\app_controller.py
|
|
||||||
AppController|src\app_controller.py
|
|
||||||
Bead|src\beads_client.py
|
|
||||||
BeadsClient|src\beads_client.py
|
|
||||||
BackgroundShader|src\bg_shader.py
|
|
||||||
get_bg|src\bg_shader.py
|
|
||||||
generate_tickets|src\conductor_tech_lead.py
|
|
||||||
topological_sort|src\conductor_tech_lead.py
|
|
||||||
estimate_cost|src\cost_tracker.py
|
|
||||||
TrackDAG|src\dag_engine.py
|
|
||||||
ExecutionEngine|src\dag_engine.py
|
|
||||||
DiffHunk|src\diff_viewer.py
|
|
||||||
DiffFile|src\diff_viewer.py
|
|
||||||
parse_diff_header|src\diff_viewer.py
|
|
||||||
parse_hunk_header|src\diff_viewer.py
|
|
||||||
parse_diff|src\diff_viewer.py
|
|
||||||
format_diff_for_display|src\diff_viewer.py
|
|
||||||
get_line_color|src\diff_viewer.py
|
|
||||||
render_diff_text_immediate|src\diff_viewer.py
|
|
||||||
create_backup|src\diff_viewer.py
|
|
||||||
apply_patch_to_file|src\diff_viewer.py
|
|
||||||
restore_from_backup|src\diff_viewer.py
|
|
||||||
cleanup_backup|src\diff_viewer.py
|
|
||||||
EventEmitter|src\events.py
|
|
||||||
AsyncEventQueue|src\events.py
|
|
||||||
UserRequestEvent|src\events.py
|
|
||||||
ExternalEditorLauncher|src\external_editor.py
|
|
||||||
auto_detect_vscode|src\external_editor.py
|
|
||||||
get_default_launcher|src\external_editor.py
|
|
||||||
resolve_project_editor_override|src\external_editor.py
|
|
||||||
create_temp_modified_file|src\external_editor.py
|
|
||||||
ASTParser|src\file_cache.py
|
|
||||||
reset_client|src\file_cache.py
|
|
||||||
content_block_type|src\file_cache.py
|
|
||||||
get_file_id|src\file_cache.py
|
|
||||||
evict|src\file_cache.py
|
|
||||||
list_cached|src\file_cache.py
|
|
||||||
GeminiCliAdapter|src\gemini_cli_adapter.py
|
|
||||||
hide_tk_root|src\gui_2.py
|
|
||||||
vec4|src\gui_2.py
|
|
||||||
truncate_entries|src\gui_2.py
|
|
||||||
GenerateRequest|src\gui_2.py
|
|
||||||
ConfirmRequest|src\gui_2.py
|
|
||||||
App|src\gui_2.py
|
|
||||||
main|src\gui_2.py
|
|
||||||
UISnapshot|src\history.py
|
|
||||||
HistoryEntry|src\history.py
|
|
||||||
HistoryManager|src\history.py
|
|
||||||
LogPruner|src\log_pruner.py
|
|
||||||
LogRegistry|src\log_registry.py
|
|
||||||
MarkdownRenderer|src\markdown_helper.py
|
|
||||||
get_renderer|src\markdown_helper.py
|
|
||||||
render|src\markdown_helper.py
|
|
||||||
render_unindented|src\markdown_helper.py
|
|
||||||
render_code|src\markdown_helper.py
|
|
||||||
configure|src\mcp_client.py
|
|
||||||
read_file|src\mcp_client.py
|
|
||||||
list_directory|src\mcp_client.py
|
|
||||||
search_files|src\mcp_client.py
|
|
||||||
get_file_summary|src\mcp_client.py
|
|
||||||
py_get_skeleton|src\mcp_client.py
|
|
||||||
ts_c_get_skeleton|src\mcp_client.py
|
|
||||||
ts_cpp_get_skeleton|src\mcp_client.py
|
|
||||||
py_get_code_outline|src\mcp_client.py
|
|
||||||
ts_c_get_code_outline|src\mcp_client.py
|
|
||||||
ts_cpp_get_code_outline|src\mcp_client.py
|
|
||||||
ts_c_get_definition|src\mcp_client.py
|
|
||||||
ts_cpp_get_definition|src\mcp_client.py
|
|
||||||
ts_c_get_signature|src\mcp_client.py
|
|
||||||
ts_cpp_get_signature|src\mcp_client.py
|
|
||||||
ts_c_update_definition|src\mcp_client.py
|
|
||||||
ts_cpp_update_definition|src\mcp_client.py
|
|
||||||
get_file_slice|src\mcp_client.py
|
|
||||||
set_file_slice|src\mcp_client.py
|
|
||||||
edit_file|src\mcp_client.py
|
|
||||||
py_get_symbol_info|src\mcp_client.py
|
|
||||||
py_get_definition|src\mcp_client.py
|
|
||||||
py_update_definition|src\mcp_client.py
|
|
||||||
py_get_signature|src\mcp_client.py
|
|
||||||
py_set_signature|src\mcp_client.py
|
|
||||||
py_get_class_summary|src\mcp_client.py
|
|
||||||
py_get_var_declaration|src\mcp_client.py
|
|
||||||
py_set_var_declaration|src\mcp_client.py
|
|
||||||
get_git_diff|src\mcp_client.py
|
|
||||||
py_find_usages|src\mcp_client.py
|
|
||||||
py_get_imports|src\mcp_client.py
|
|
||||||
py_check_syntax|src\mcp_client.py
|
|
||||||
py_get_hierarchy|src\mcp_client.py
|
|
||||||
py_get_docstring|src\mcp_client.py
|
|
||||||
get_tree|src\mcp_client.py
|
|
||||||
derive_code_path|src\mcp_client.py
|
|
||||||
web_search|src\mcp_client.py
|
|
||||||
fetch_url|src\mcp_client.py
|
|
||||||
get_ui_performance|src\mcp_client.py
|
|
||||||
StdioMCPServer|src\mcp_client.py
|
|
||||||
ExternalMCPManager|src\mcp_client.py
|
|
||||||
get_external_mcp_manager|src\mcp_client.py
|
|
||||||
dispatch|src\mcp_client.py
|
|
||||||
async_dispatch|src\mcp_client.py
|
|
||||||
get_tool_schemas|src\mcp_client.py
|
|
||||||
load_config|src\models.py
|
|
||||||
save_config|src\models.py
|
|
||||||
parse_history_entries|src\models.py
|
|
||||||
ThinkingSegment|src\models.py
|
|
||||||
Ticket|src\models.py
|
|
||||||
Track|src\models.py
|
|
||||||
WorkerContext|src\models.py
|
|
||||||
Metadata|src\models.py
|
|
||||||
TextEditorConfig|src\models.py
|
|
||||||
ExternalEditorConfig|src\models.py
|
|
||||||
TrackState|src\models.py
|
|
||||||
FileItem|src\models.py
|
|
||||||
Preset|src\models.py
|
|
||||||
Tool|src\models.py
|
|
||||||
ToolPreset|src\models.py
|
|
||||||
BiasProfile|src\models.py
|
|
||||||
Persona|src\models.py
|
|
||||||
MCPServerConfig|src\models.py
|
|
||||||
MCPConfiguration|src\models.py
|
|
||||||
VectorStoreConfig|src\models.py
|
|
||||||
RAGConfig|src\models.py
|
|
||||||
WorkspaceProfile|src\models.py
|
|
||||||
load_mcp_config|src\models.py
|
|
||||||
WorkerPool|src\multi_agent_conductor.py
|
|
||||||
ConductorEngine|src\multi_agent_conductor.py
|
|
||||||
confirm_execution|src\multi_agent_conductor.py
|
|
||||||
confirm_spawn|src\multi_agent_conductor.py
|
|
||||||
run_worker_lifecycle|src\multi_agent_conductor.py
|
|
||||||
read_plan|src\native_orchestrator.py
|
|
||||||
write_plan|src\native_orchestrator.py
|
|
||||||
parse_plan_tasks|src\native_orchestrator.py
|
|
||||||
read_metadata|src\native_orchestrator.py
|
|
||||||
write_metadata|src\native_orchestrator.py
|
|
||||||
get_track_dir|src\native_orchestrator.py
|
|
||||||
get_archive_dir|src\native_orchestrator.py
|
|
||||||
NativeOrchestrator|src\native_orchestrator.py
|
|
||||||
get_track_history_summary|src\orchestrator_pm.py
|
|
||||||
generate_tracks|src\orchestrator_pm.py
|
|
||||||
CodeOutliner|src\outline_tool.py
|
|
||||||
get_outline|src\outline_tool.py
|
|
||||||
PendingPatch|src\patch_modal.py
|
|
||||||
PatchModalManager|src\patch_modal.py
|
|
||||||
get_patch_modal_manager|src\patch_modal.py
|
|
||||||
reset_patch_modal_manager|src\patch_modal.py
|
|
||||||
get_config_path|src\paths.py
|
|
||||||
get_global_presets_path|src\paths.py
|
|
||||||
get_project_presets_path|src\paths.py
|
|
||||||
get_global_tool_presets_path|src\paths.py
|
|
||||||
get_project_tool_presets_path|src\paths.py
|
|
||||||
get_global_personas_path|src\paths.py
|
|
||||||
get_project_personas_path|src\paths.py
|
|
||||||
get_global_workspace_profiles_path|src\paths.py
|
|
||||||
get_project_workspace_profiles_path|src\paths.py
|
|
||||||
get_conductor_dir|src\paths.py
|
|
||||||
get_logs_dir|src\paths.py
|
|
||||||
get_scripts_dir|src\paths.py
|
|
||||||
get_tracks_dir|src\paths.py
|
|
||||||
get_track_state_dir|src\paths.py
|
|
||||||
get_archive_dir|src\paths.py
|
|
||||||
get_full_path_info|src\paths.py
|
|
||||||
reset_resolved|src\paths.py
|
|
||||||
PerformanceScope|src\performance_monitor.py
|
|
||||||
get_monitor|src\performance_monitor.py
|
|
||||||
PerformanceMonitor|src\performance_monitor.py
|
|
||||||
PersonaManager|src\personas.py
|
|
||||||
PresetManager|src\presets.py
|
|
||||||
now_ts|src\project_manager.py
|
|
||||||
parse_ts|src\project_manager.py
|
|
||||||
entry_to_str|src\project_manager.py
|
|
||||||
str_to_entry|src\project_manager.py
|
|
||||||
get_git_commit|src\project_manager.py
|
|
||||||
get_git_log|src\project_manager.py
|
|
||||||
default_discussion|src\project_manager.py
|
|
||||||
default_project|src\project_manager.py
|
|
||||||
get_history_path|src\project_manager.py
|
|
||||||
load_project|src\project_manager.py
|
|
||||||
load_history|src\project_manager.py
|
|
||||||
clean_nones|src\project_manager.py
|
|
||||||
save_project|src\project_manager.py
|
|
||||||
migrate_from_legacy_config|src\project_manager.py
|
|
||||||
flat_config|src\project_manager.py
|
|
||||||
save_track_state|src\project_manager.py
|
|
||||||
load_track_state|src\project_manager.py
|
|
||||||
load_track_history|src\project_manager.py
|
|
||||||
save_track_history|src\project_manager.py
|
|
||||||
get_all_tracks|src\project_manager.py
|
|
||||||
calculate_track_progress|src\project_manager.py
|
|
||||||
branch_discussion|src\project_manager.py
|
|
||||||
promote_take|src\project_manager.py
|
|
||||||
BaseEmbeddingProvider|src\rag_engine.py
|
|
||||||
LocalEmbeddingProvider|src\rag_engine.py
|
|
||||||
GeminiEmbeddingProvider|src\rag_engine.py
|
|
||||||
RAGEngine|src\rag_engine.py
|
|
||||||
open_session|src\session_logger.py
|
|
||||||
close_session|src\session_logger.py
|
|
||||||
reset_session|src\session_logger.py
|
|
||||||
log_api_hook|src\session_logger.py
|
|
||||||
log_comms|src\session_logger.py
|
|
||||||
log_tool_call|src\session_logger.py
|
|
||||||
log_tool_output|src\session_logger.py
|
|
||||||
log_cli_call|src\session_logger.py
|
|
||||||
draw_soft_shadow|src\shaders.py
|
|
||||||
apply_faux_acrylic_glass|src\shaders.py
|
|
||||||
ShaderManager|src\shader_manager.py
|
|
||||||
run_powershell|src\shell_runner.py
|
|
||||||
summarise_file|src\summarize.py
|
|
||||||
summarise_items|src\summarize.py
|
|
||||||
build_summary_markdown|src\summarize.py
|
|
||||||
get_file_hash|src\summary_cache.py
|
|
||||||
SummaryCache|src\summary_cache.py
|
|
||||||
format_takes_diff|src\synthesis_formatter.py
|
|
||||||
get_palette_names|src\theme.py
|
|
||||||
get_current_palette|src\theme.py
|
|
||||||
get_current_font_path|src\theme.py
|
|
||||||
get_current_font_size|src\theme.py
|
|
||||||
get_current_scale|src\theme.py
|
|
||||||
get_shader_config|src\theme.py
|
|
||||||
get_window_frame_config|src\theme.py
|
|
||||||
get_palette_colours|src\theme.py
|
|
||||||
apply|src\theme.py
|
|
||||||
apply_font|src\theme.py
|
|
||||||
set_scale|src\theme.py
|
|
||||||
save_to_config|src\theme.py
|
|
||||||
load_from_config|src\theme.py
|
|
||||||
is_nerv_active|src\theme_2.py
|
|
||||||
get_transparency|src\theme_2.py
|
|
||||||
set_transparency|src\theme_2.py
|
|
||||||
get_child_transparency|src\theme_2.py
|
|
||||||
set_child_transparency|src\theme_2.py
|
|
||||||
apply_current|src\theme_2.py
|
|
||||||
get_font_loading_params|src\theme_2.py
|
|
||||||
get_tweaked_theme|src\theme_2.py
|
|
||||||
apply_nerv|src\theme_nerv.py
|
|
||||||
CRTFilter|src\theme_nerv_fx.py
|
|
||||||
StatusFlicker|src\theme_nerv_fx.py
|
|
||||||
AlertPulsing|src\theme_nerv_fx.py
|
|
||||||
parse_thinking_trace|src\thinking_parser.py
|
|
||||||
ToolBiasEngine|src\tool_bias.py
|
|
||||||
ToolPresetManager|src\tool_presets.py
|
|
||||||
WorkspaceManager|src\workspace_manager.py
|
|
||||||
AISettingsSimulation|simulation\sim_ai_settings.py
|
|
||||||
BaseSimulation|simulation\sim_base.py
|
|
||||||
run_sim|simulation\sim_base.py
|
|
||||||
ContextSimulation|simulation\sim_context.py
|
|
||||||
ExecutionSimulation|simulation\sim_execution.py
|
|
||||||
ToolsSimulation|simulation\sim_tools.py
|
|
||||||
UserSimAgent|simulation\user_agent.py
|
|
||||||
WorkflowSimulator|simulation\workflow_sim.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
symbols = []
|
|
||||||
for line in raw_symbols.strip().split("\n"):
|
|
||||||
if "|" in line:
|
|
||||||
name, path = line.split("|")
|
|
||||||
symbols.append((name.strip(), path.strip()))
|
|
||||||
|
|
||||||
# Load all project source
|
|
||||||
source_files = {}
|
|
||||||
for root in ['src', 'simulation']:
|
|
||||||
for p in Path(root).rglob('*.py'):
|
|
||||||
try:
|
|
||||||
source_files[str(p)] = p.read_text(encoding='utf-8')
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
unused = []
|
|
||||||
for name, path in symbols:
|
|
||||||
# Skip generic names that might give false positives in a simple regex search
|
|
||||||
if name in ["run", "main", "apply", "save_config", "load_config", "configure"]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
pattern = re.compile(r'\b' + re.escape(name) + r'\b')
|
|
||||||
count = 0
|
|
||||||
for fpath, code in source_files.items():
|
|
||||||
if fpath == path:
|
|
||||||
# Only count if it appears more than once (one for definition)
|
|
||||||
# This is a bit naive for methods vs standalone functions but a good start
|
|
||||||
matches = pattern.findall(code)
|
|
||||||
if len(matches) > 1:
|
|
||||||
count += (len(matches) - 1)
|
|
||||||
else:
|
|
||||||
matches = pattern.findall(code)
|
|
||||||
count += len(matches)
|
|
||||||
|
|
||||||
if count == 0:
|
|
||||||
unused.append((name, path))
|
|
||||||
|
|
||||||
print(f"TOTAL_UNUSED:{len(unused)}")
|
|
||||||
for name, path in unused:
|
|
||||||
print(f"UNUSED:{name}|PATH:{path}")
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
files = ['ai_client.py', 'aggregate.py', 'mcp_client.py', 'shell_runner.py']
|
|
||||||
for file_path in files:
|
|
||||||
print(f"Checking {file_path}...")
|
|
||||||
with open(file_path, 'r', encoding='utf-8') as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if line.strip().startswith('def '):
|
|
||||||
if '->' not in line:
|
|
||||||
# Check next line if it's a multiline def
|
|
||||||
if '):' not in line:
|
|
||||||
full_def = line
|
|
||||||
j = i + 1
|
|
||||||
while j < len(lines) and '):' not in lines[j-1]:
|
|
||||||
full_def += lines[j]
|
|
||||||
j += 1
|
|
||||||
if '->' not in full_def:
|
|
||||||
print(f" Missing hint at line {i+1}: {line.strip()}")
|
|
||||||
else:
|
|
||||||
print(f" Missing hint at line {i+1}: {line.strip()}")
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
|
|
||||||
file_path = Path('src/app_controller.py')
|
|
||||||
code = file_path.read_text(encoding='utf-8')
|
|
||||||
|
|
||||||
# Remove the garbage tail
|
|
||||||
code = re.sub(r'\n= \[\]\s*$', '', code)
|
|
||||||
code = code.strip() + "\n"
|
|
||||||
|
|
||||||
file_path.write_text(code, encoding='utf-8')
|
|
||||||
print("Tail cleanup complete.")
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
|
|
||||||
file_path = Path('src/app_controller.py')
|
|
||||||
code = file_path.read_text(encoding='utf-8')
|
|
||||||
|
|
||||||
# 1. Inject show_windows
|
|
||||||
code = re.sub(
|
|
||||||
r'(self\._pending_gui_tasks_lock: threading\.Lock = threading\.Lock\(\))',
|
|
||||||
r'\1\n self.show_windows: Dict[str, bool] = {}',
|
|
||||||
code
|
|
||||||
)
|
|
||||||
|
|
||||||
# 2. Inject ui_active_persona and ui_active_bias_profile
|
|
||||||
code = re.sub(
|
|
||||||
r'(self\.ui_ai_input: str = "")',
|
|
||||||
r'self.ui_active_persona: str = ""\n self.ui_active_bias_profile: str | None = None\n \1',
|
|
||||||
code
|
|
||||||
)
|
|
||||||
|
|
||||||
# 3. Add ui_active_persona to _settable_fields
|
|
||||||
code = re.sub(
|
|
||||||
r"('disc_new_role_input': 'ui_disc_new_role_input',)",
|
|
||||||
r"\1\n 'active_persona': 'ui_active_persona',",
|
|
||||||
code
|
|
||||||
)
|
|
||||||
|
|
||||||
file_path.write_text(code, encoding='utf-8')
|
|
||||||
print("Precision injection complete.")
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import ast
|
|
||||||
import sys
|
|
||||||
import pathlib
|
|
||||||
|
|
||||||
class ImportCollector(ast.NodeTransformer):
|
|
||||||
def __init__(self):
|
|
||||||
self.collected_imports = []
|
|
||||||
|
|
||||||
def visit_Import(self, node):
|
|
||||||
self.collected_imports.append(node)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def visit_ImportFrom(self, node):
|
|
||||||
self.collected_imports.append(node)
|
|
||||||
return None
|
|
||||||
|
|
||||||
class PassFiller(ast.NodeTransformer):
|
|
||||||
"""Ensures that blocks that became empty after import removal have a 'pass' statement."""
|
|
||||||
def generic_visit(self, node):
|
|
||||||
super().generic_visit(node)
|
|
||||||
if hasattr(node, 'body') and isinstance(node.body, list) and not node.body:
|
|
||||||
if not isinstance(node, ast.Module):
|
|
||||||
node.body.append(ast.Pass())
|
|
||||||
return node
|
|
||||||
|
|
||||||
def fix_imports(file_path):
|
|
||||||
path = pathlib.Path(file_path)
|
|
||||||
if not path.exists():
|
|
||||||
print(f"File not found: {file_path}")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
content = path.read_text(encoding='utf-8')
|
|
||||||
tree = ast.parse(content)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error parsing {file_path}: {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
collector = ImportCollector()
|
|
||||||
tree = collector.visit(tree)
|
|
||||||
|
|
||||||
# Fill empty bodies with pass
|
|
||||||
tree = PassFiller().visit(tree)
|
|
||||||
|
|
||||||
if not collector.collected_imports:
|
|
||||||
print(f"No imports to move in {file_path}")
|
|
||||||
return
|
|
||||||
|
|
||||||
# De-duplicate while preserving order
|
|
||||||
unique_imports = {}
|
|
||||||
for node in collector.collected_imports:
|
|
||||||
try:
|
|
||||||
# We use unparse to identify identical imports
|
|
||||||
code = ast.unparse(node).strip()
|
|
||||||
if code not in unique_imports:
|
|
||||||
unique_imports[code] = node
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not unique_imports:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Sort: __future__ first, then others
|
|
||||||
future_imports = []
|
|
||||||
other_imports = []
|
|
||||||
for code, node in unique_imports.items():
|
|
||||||
if isinstance(node, ast.ImportFrom) and node.module == '__future__':
|
|
||||||
future_imports.append(node)
|
|
||||||
else:
|
|
||||||
other_imports.append(node)
|
|
||||||
|
|
||||||
all_to_insert = future_imports + other_imports
|
|
||||||
|
|
||||||
# Find insertion point (after initial docstring)
|
|
||||||
insertion_idx = 0
|
|
||||||
if tree.body:
|
|
||||||
first = tree.body[0]
|
|
||||||
if (isinstance(first, ast.Expr) and
|
|
||||||
isinstance(first.value, ast.Constant) and
|
|
||||||
isinstance(first.value.value, str)):
|
|
||||||
insertion_idx = 1
|
|
||||||
|
|
||||||
# Insert imports
|
|
||||||
for i, node in enumerate(all_to_insert):
|
|
||||||
tree.body.insert(insertion_idx + i, node)
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_code = ast.unparse(tree)
|
|
||||||
# Basic check to avoid unnecessary writes
|
|
||||||
if new_code.strip() != content.strip():
|
|
||||||
path.write_text(new_code, encoding='utf-8')
|
|
||||||
print(f"Updated {file_path}")
|
|
||||||
else:
|
|
||||||
print(f"No changes for {file_path}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error unparsing or writing {file_path}: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage: python scripts/fix_imports.py <file1> <file2> ...")
|
|
||||||
else:
|
|
||||||
for arg in sys.argv[1:]:
|
|
||||||
fix_imports(arg)
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Fix AppController
|
|
||||||
file_ac = Path('src/app_controller.py')
|
|
||||||
code_ac = file_ac.read_text(encoding='utf-8')
|
|
||||||
# Robust non-recursive __getattr__
|
|
||||||
new_getattr_ac = """ def __getattr__(self, name: str) -> Any:
|
|
||||||
if name == '_app':
|
|
||||||
raise AttributeError(name)
|
|
||||||
# Check if _app exists in self without triggering __getattr__
|
|
||||||
try:
|
|
||||||
app = object.__getattribute__(self, '_app')
|
|
||||||
except AttributeError:
|
|
||||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
||||||
|
|
||||||
if app is not None and name in app.__dict__:
|
|
||||||
return getattr(app, name)
|
|
||||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
||||||
"""
|
|
||||||
# Replace all instances of __getattr__ in AppController (in case of duplication)
|
|
||||||
code_ac = re.sub(r'def __getattr__\(self, name: str\) -> Any:.*?raise AttributeError\(.*?\)', new_getattr_ac, code_ac, flags=re.DOTALL)
|
|
||||||
file_ac.write_text(code_ac, encoding='utf-8')
|
|
||||||
|
|
||||||
# Fix App
|
|
||||||
file_gui = Path('src/gui_2.py')
|
|
||||||
code_gui = file_gui.read_text(encoding='utf-8')
|
|
||||||
|
|
||||||
new_getattr_gui = """ def __getattr__(self, name: str) -> Any:
|
|
||||||
if name == 'controller':
|
|
||||||
raise AttributeError(name)
|
|
||||||
try:
|
|
||||||
ctrl = object.__getattribute__(self, 'controller')
|
|
||||||
except AttributeError:
|
|
||||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
||||||
|
|
||||||
if ctrl is not None and name in ctrl.__dict__:
|
|
||||||
return getattr(ctrl, name)
|
|
||||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
||||||
"""
|
|
||||||
code_gui = re.sub(r'def __getattr__\(self, name: str\) -> Any:.*?raise AttributeError\(.*?\)', new_getattr_gui, code_gui, flags=re.DOTALL)
|
|
||||||
|
|
||||||
new_setattr_gui = """ def __setattr__(self, name: str, value: Any) -> None:
|
|
||||||
if name == 'controller':
|
|
||||||
object.__setattr__(self, name, value)
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
ctrl = object.__getattribute__(self, 'controller')
|
|
||||||
except AttributeError:
|
|
||||||
ctrl = None
|
|
||||||
|
|
||||||
if ctrl is not None and name in ctrl.__dict__:
|
|
||||||
setattr(ctrl, name, value)
|
|
||||||
else:
|
|
||||||
object.__setattr__(self, name, value)
|
|
||||||
"""
|
|
||||||
code_gui = re.sub(r'def __setattr__\(self, name: str, value: Any\) -> None:.*?object\.__setattr__\(self, name, value\)', new_setattr_gui, code_gui, flags=re.DOTALL)
|
|
||||||
|
|
||||||
file_gui.write_text(code_gui, encoding='utf-8')
|
|
||||||
|
|
||||||
print('Delegation fix complete.')
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import tokenize
|
|
||||||
|
|
||||||
import io
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def force_1space(path):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
with open(path, 'rb') as f:
|
|
||||||
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
tokens = list(tokenize.tokenize(io.BytesIO(content).readline))
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
col_to_level = {0: 0}
|
|
||||||
|
|
||||||
level = 0
|
|
||||||
|
|
||||||
for tok in tokens:
|
|
||||||
|
|
||||||
if tok.type == tokenize.INDENT:
|
|
||||||
|
|
||||||
level += 1
|
|
||||||
|
|
||||||
col_to_level[tok.end[1]] = level
|
|
||||||
|
|
||||||
elif tok.type == tokenize.DEDENT:
|
|
||||||
|
|
||||||
level -= 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
new_content = []
|
|
||||||
|
|
||||||
level = 0
|
|
||||||
|
|
||||||
last_line = -1
|
|
||||||
|
|
||||||
last_end = (1, 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for tok in tokens:
|
|
||||||
|
|
||||||
if tok.type == tokenize.ENCODING:
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
if tok.type == tokenize.ENDMARKER:
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
import re
|
|
||||||
|
|
||||||
with open('mcp_client.py', 'r', encoding='utf-8') as f:
|
|
||||||
content: str = f.read()
|
|
||||||
# 1. Add import os if not there
|
|
||||||
if 'import os' not in content:
|
|
||||||
content: str = content.replace('import summarize', 'import os\nimport summarize')
|
|
||||||
# 2. Add the functions before "# ------------------------------------------------------------------ web tools"
|
|
||||||
functions_code: str = r'''
|
|
||||||
def py_find_usages(path: str, name: str) -> str:
|
|
||||||
"""Finds exact string matches of a symbol in a given file or directory."""
|
|
||||||
p, err = _resolve_and_check(path)
|
|
||||||
if err: return err
|
|
||||||
try:
|
|
||||||
import re
|
|
||||||
pattern = re.compile(r"\b" + re.escape(name) + r"\b")
|
|
||||||
results = []
|
|
||||||
def _search_file(fp):
|
|
||||||
if fp.name == "history.toml" or fp.name.endswith("_history.toml"): return
|
|
||||||
if not _is_allowed(fp): return
|
|
||||||
try:
|
|
||||||
text = fp.read_text(encoding="utf-8")
|
|
||||||
lines = text.splitlines()
|
|
||||||
for i, line in enumerate(lines, 1):
|
|
||||||
if pattern.search(line):
|
|
||||||
rel = fp.relative_to(_primary_base_dir if _primary_base_dir else Path.cwd())
|
|
||||||
results.append(f"{rel}:{i}: {line.strip()[:100]}")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if p.is_file():
|
|
||||||
_search_file(p)
|
|
||||||
else:
|
|
||||||
for root, dirs, files in os.walk(p):
|
|
||||||
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ('__pycache__', 'venv', 'env')]
|
|
||||||
for file in files:
|
|
||||||
if file.endswith(('.py', '.md', '.toml', '.txt', '.json')):
|
|
||||||
_search_file(Path(root) / file)
|
|
||||||
|
|
||||||
if not results:
|
|
||||||
return f"No usages found for '{name}' in {p}"
|
|
||||||
if len(results) > 100:
|
|
||||||
return "\n".join(results[:100]) + f"\n... (and {len(results)-100} more)"
|
|
||||||
return "\n".join(results)
|
|
||||||
except Exception as e:
|
|
||||||
return f"ERROR finding usages for '{name}': {e}"
|
|
||||||
|
|
||||||
def py_get_imports(path: str) -> str:
|
|
||||||
"""Parses a file's AST and returns a strict list of its dependencies."""
|
|
||||||
p, err = _resolve_and_check(path)
|
|
||||||
if err: return err
|
|
||||||
if not p.is_file() or p.suffix != ".py": return f"ERROR: not a python file: {path}"
|
|
||||||
try:
|
|
||||||
import ast
|
|
||||||
code = p.read_text(encoding="utf-8")
|
|
||||||
tree = ast.parse(code)
|
|
||||||
imports = []
|
|
||||||
for node in tree.body:
|
|
||||||
if isinstance(node, ast.Import):
|
|
||||||
for alias in node.names:
|
|
||||||
imports.append(alias.name)
|
|
||||||
elif isinstance(node, ast.ImportFrom):
|
|
||||||
module = node.module or ""
|
|
||||||
for alias in node.names:
|
|
||||||
imports.append(f"{module}.{alias.name}" if module else alias.name)
|
|
||||||
if not imports: return "No imports found."
|
|
||||||
return "Imports:\n" + "\n".join(f" - {i}" for i in imports)
|
|
||||||
except Exception as e:
|
|
||||||
return f"ERROR getting imports for '{path}': {e}"
|
|
||||||
|
|
||||||
def py_check_syntax(path: str) -> str:
|
|
||||||
"""Runs a quick syntax check on a Python file."""
|
|
||||||
p, err = _resolve_and_check(path)
|
|
||||||
if err: return err
|
|
||||||
if not p.is_file() or p.suffix != ".py": return f"ERROR: not a python file: {path}"
|
|
||||||
try:
|
|
||||||
import ast
|
|
||||||
code = p.read_text(encoding="utf-8")
|
|
||||||
ast.parse(code)
|
|
||||||
return f"Syntax OK: {path}"
|
|
||||||
except SyntaxError as e:
|
|
||||||
return f"SyntaxError in {path} at line {e.lineno}, offset {e.offset}: {e.msg}\n{e.text}"
|
|
||||||
except Exception as e:
|
|
||||||
return f"ERROR checking syntax for '{path}': {e}"
|
|
||||||
|
|
||||||
def py_get_hierarchy(path: str, class_name: str) -> str:
|
|
||||||
"""Scans the project to find subclasses of a given class."""
|
|
||||||
p, err = _resolve_and_check(path)
|
|
||||||
if err: return err
|
|
||||||
import ast
|
|
||||||
subclasses = []
|
|
||||||
|
|
||||||
def _search_file(fp):
|
|
||||||
if not _is_allowed(fp): return
|
|
||||||
try:
|
|
||||||
code = fp.read_text(encoding="utf-8")
|
|
||||||
tree = ast.parse(code)
|
|
||||||
for node in ast.walk(tree):
|
|
||||||
if isinstance(node, ast.ClassDef):
|
|
||||||
for base in node.bases:
|
|
||||||
if isinstance(base, ast.Name) and base.id == class_name:
|
|
||||||
subclasses.append(f"{fp.name}: class {node.name}({class_name})")
|
|
||||||
elif isinstance(base, ast.Attribute) and base.attr == class_name:
|
|
||||||
subclasses.append(f"{fp.name}: class {node.name}({base.value.id}.{class_name})")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
if p.is_file():
|
|
||||||
_search_file(p)
|
|
||||||
else:
|
|
||||||
for root, dirs, files in os.walk(p):
|
|
||||||
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ('__pycache__', 'venv', 'env')]
|
|
||||||
for file in files:
|
|
||||||
if file.endswith('.py'):
|
|
||||||
_search_file(Path(root) / file)
|
|
||||||
|
|
||||||
if not subclasses:
|
|
||||||
return f"No subclasses of '{class_name}' found in {p}"
|
|
||||||
return f"Subclasses of '{class_name}':\n" + "\n".join(f" - {s}" for s in subclasses)
|
|
||||||
except Exception as e:
|
|
||||||
return f"ERROR finding subclasses of '{class_name}': {e}"
|
|
||||||
|
|
||||||
def py_get_docstring(path: str, name: str) -> str:
|
|
||||||
"""Extracts the docstring for a specific module, class, or function."""
|
|
||||||
p, err = _resolve_and_check(path)
|
|
||||||
if err: return err
|
|
||||||
if not p.is_file() or p.suffix != ".py": return f"ERROR: not a python file: {path}"
|
|
||||||
try:
|
|
||||||
import ast
|
|
||||||
code = p.read_text(encoding="utf-8")
|
|
||||||
tree = ast.parse(code)
|
|
||||||
if not name or name == "module":
|
|
||||||
doc = ast.get_docstring(tree)
|
|
||||||
return doc if doc else "No module docstring found."
|
|
||||||
|
|
||||||
node = _get_symbol_node(tree, name)
|
|
||||||
if not node: return f"ERROR: could not find symbol '{name}' in {path}"
|
|
||||||
doc = ast.get_docstring(node)
|
|
||||||
return doc if doc else f"No docstring found for '{name}'."
|
|
||||||
except Exception as e:
|
|
||||||
return f"ERROR getting docstring for '{name}': {e}"
|
|
||||||
|
|
||||||
def get_tree(path: str, max_depth: int = 2) -> str:
|
|
||||||
"""Returns a directory structure up to a max depth."""
|
|
||||||
p, err = _resolve_and_check(path)
|
|
||||||
if err: return err
|
|
||||||
if not p.is_dir(): return f"ERROR: not a directory: {path}"
|
|
||||||
|
|
||||||
try:
|
|
||||||
max_depth = int(max_depth)
|
|
||||||
def _build_tree(dir_path, current_depth, prefix=""):
|
|
||||||
if current_depth > max_depth: return []
|
|
||||||
lines = []
|
|
||||||
try:
|
|
||||||
entries = sorted(dir_path.iterdir(), key=lambda e: (e.is_file(), e.name.lower()))
|
|
||||||
except PermissionError:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# Filter
|
|
||||||
entries = [e for e in entries if not e.name.startswith('.') and e.name not in ('__pycache__', 'venv', 'env') and e.name != "history.toml" and not e.name.endswith("_history.toml")]
|
|
||||||
|
|
||||||
for i, entry in enumerate(entries):
|
|
||||||
is_last = (i == len(entries) - 1)
|
|
||||||
connector = "└── " if is_last else "├── "
|
|
||||||
lines.append(f"{prefix}{connector}{entry.name}")
|
|
||||||
if entry.is_dir():
|
|
||||||
extension = " " if is_last else "│ "
|
|
||||||
lines.extend(_build_tree(entry, current_depth + 1, prefix + extension))
|
|
||||||
return lines
|
|
||||||
|
|
||||||
tree_lines = [f"{p.name}/"] + _build_tree(p, 1)
|
|
||||||
return "\n".join(tree_lines)
|
|
||||||
except Exception as e:
|
|
||||||
return f"ERROR generating tree for '{path}': {e}"
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------ web tools'''
|
|
||||||
|
|
||||||
content: str = content.replace('# ------------------------------------------------------------------ web tools', functions_code)
|
|
||||||
|
|
||||||
# 3. Update TOOL_NAMES
|
|
||||||
old_tool_names_match: re.Match | None = re.search(r'TOOL_NAMES\s*=\s*\{([^}]*)\}', content)
|
|
||||||
if old_tool_names_match:
|
|
||||||
old_names: str = old_tool_names_match.group(1)
|
|
||||||
new_names: str = old_names + ', "py_find_usages", "py_get_imports", "py_check_syntax", "py_get_hierarchy", "py_get_docstring", "get_tree"'
|
|
||||||
content: str = content.replace(old_tool_names_match.group(0), f'TOOL_NAMES = {{{new_names}}}')
|
|
||||||
# 4. Update dispatch
|
|
||||||
dispatch_additions: str = r'''
|
|
||||||
if tool_name == "py_find_usages":
|
|
||||||
return py_find_usages(tool_input.get("path", ""), tool_input.get("name", ""))
|
|
||||||
if tool_name == "py_get_imports":
|
|
||||||
return py_get_imports(tool_input.get("path", ""))
|
|
||||||
if tool_name == "py_check_syntax":
|
|
||||||
return py_check_syntax(tool_input.get("path", ""))
|
|
||||||
if tool_name == "py_get_hierarchy":
|
|
||||||
return py_get_hierarchy(tool_input.get("path", ""), tool_input.get("class_name", ""))
|
|
||||||
if tool_name == "py_get_docstring":
|
|
||||||
return py_get_docstring(tool_input.get("path", ""), tool_input.get("name", ""))
|
|
||||||
if tool_name == "get_tree":
|
|
||||||
return get_tree(tool_input.get("path", ""), tool_input.get("max_depth", 2))
|
|
||||||
return f"ERROR: unknown MCP tool '{tool_name}'"
|
|
||||||
'''
|
|
||||||
content: str = re.sub(
|
|
||||||
r' return f"ERROR: unknown MCP tool \'{tool_name}\'"', dispatch_additions.strip(), content)
|
|
||||||
|
|
||||||
# 5. Update MCP_TOOL_SPECS
|
|
||||||
mcp_tool_specs_addition: str = r'''
|
|
||||||
{
|
|
||||||
"name": "py_find_usages",
|
|
||||||
"description": "Finds exact string matches of a symbol in a given file or directory.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": { "type": "string", "description": "Path to file or directory to search." },
|
|
||||||
"name": { "type": "string", "description": "The symbol/string to search for." }
|
|
||||||
},
|
|
||||||
"required": ["path", "name"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "py_get_imports",
|
|
||||||
"description": "Parses a file's AST and returns a strict list of its dependencies.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": { "type": "string", "description": "Path to the .py file." }
|
|
||||||
},
|
|
||||||
"required": ["path"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "py_check_syntax",
|
|
||||||
"description": "Runs a quick syntax check on a Python file.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": { "type": "string", "description": "Path to the .py file." }
|
|
||||||
},
|
|
||||||
"required": ["path"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "py_get_hierarchy",
|
|
||||||
"description": "Scans the project to find subclasses of a given class.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": { "type": "string", "description": "Directory path to search in." },
|
|
||||||
"class_name": { "type": "string", "description": "Name of the base class." }
|
|
||||||
},
|
|
||||||
"required": ["path", "class_name"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "py_get_docstring",
|
|
||||||
"description": "Extracts the docstring for a specific module, class, or function.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": { "type": "string", "description": "Path to the .py file." },
|
|
||||||
"name": { "type": "string", "description": "Name of symbol or 'module' for the file docstring." }
|
|
||||||
},
|
|
||||||
"required": ["path", "name"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "get_tree",
|
|
||||||
"description": "Returns a directory structure up to a max depth.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": { "type": "string", "description": "Directory path." },
|
|
||||||
"max_depth": { "type": "integer", "description": "Maximum depth to recurse (default 2)." }
|
|
||||||
},
|
|
||||||
"required": ["path"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
'''
|
|
||||||
|
|
||||||
content: str = re.sub(
|
|
||||||
r'\]\s*$', mcp_tool_specs_addition.strip(), content)
|
|
||||||
|
|
||||||
with open('mcp_client.py', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
print("Injected new tools.")
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def insert_method(file_path):
|
|
||||||
with open(file_path, 'r', encoding='utf-8', newline='') as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
|
|
||||||
target_line = -1
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if 'def _render_heavy_text' in line:
|
|
||||||
# Find the end of this method
|
|
||||||
for j in range(i + 1, len(lines)):
|
|
||||||
if lines[j].startswith(' def '):
|
|
||||||
target_line = j
|
|
||||||
break
|
|
||||||
if target_line != -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
if target_line == -1:
|
|
||||||
print("Could not find insertion point")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
new_method = [
|
|
||||||
'\n',
|
|
||||||
' def _render_selectable_label(self, label: str, value: str, width: float = 0.0, multiline: bool = False, height: float = 0.0, color: Optional[imgui.ImVec4] = None) -> None:\n',
|
|
||||||
' imgui.push_id(label + str(hash(value)))\n',
|
|
||||||
' pops = 2\n',
|
|
||||||
' imgui.push_style_color(imgui.Col_.frame_bg, vec4(0, 0, 0, 0))\n',
|
|
||||||
' imgui.push_style_color(imgui.Col_.border, vec4(0, 0, 0, 0))\n',
|
|
||||||
' if color:\n',
|
|
||||||
' imgui.push_style_color(imgui.Col_.text, color)\n',
|
|
||||||
' pops += 1\n',
|
|
||||||
' if multiline:\n',
|
|
||||||
' imgui.input_text_multiline("##" + label, value, imgui.ImVec2(width, height), imgui.InputTextFlags_.read_only)\n',
|
|
||||||
' else:\n',
|
|
||||||
' if width > 0: imgui.set_next_item_width(width)\n',
|
|
||||||
' imgui.input_text("##" + label, value, imgui.InputTextFlags_.read_only)\n',
|
|
||||||
' imgui.pop_style_color(pops)\n',
|
|
||||||
' imgui.pop_id()\n'
|
|
||||||
]
|
|
||||||
|
|
||||||
lines[target_line:target_line] = new_method
|
|
||||||
|
|
||||||
with open(file_path, 'w', encoding='utf-8', newline='') as f:
|
|
||||||
f.writelines(lines)
|
|
||||||
print("Successfully inserted _render_selectable_label")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
insert_method("src/gui_2.py")
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
from src import models
|
|
||||||
from src.paths import get_config_path, get_global_presets_path, get_project_presets_path
|
|
||||||
from src.presets import PresetManager
|
|
||||||
from src.personas import PersonaManager
|
|
||||||
|
|
||||||
def migrate():
|
|
||||||
print("Starting Persona Migration...")
|
|
||||||
|
|
||||||
config_path = get_config_path()
|
|
||||||
try:
|
|
||||||
with open(config_path, "rb") as f:
|
|
||||||
import tomllib
|
|
||||||
config = tomllib.load(f)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Could not load config: {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
ai_cfg = config.get("ai", {})
|
|
||||||
provider = ai_cfg.get("provider")
|
|
||||||
model = ai_cfg.get("model")
|
|
||||||
|
|
||||||
global_presets_path = get_global_presets_path()
|
|
||||||
preset_manager = PresetManager()
|
|
||||||
|
|
||||||
persona_manager = PersonaManager()
|
|
||||||
|
|
||||||
# Migrate global presets
|
|
||||||
if global_presets_path.exists():
|
|
||||||
global_data = preset_manager._load_file(global_presets_path)
|
|
||||||
for name, data in global_data.get("presets", {}).items():
|
|
||||||
preset = models.Preset.from_dict(name, data)
|
|
||||||
persona = models.Persona(
|
|
||||||
name=name,
|
|
||||||
preferred_models=[{"provider": provider, "model": model}],
|
|
||||||
system_prompt=preset.system_prompt
|
|
||||||
)
|
|
||||||
persona_manager.save_persona(persona, scope="global")
|
|
||||||
print(f"Migrated global preset to persona: {name}")
|
|
||||||
|
|
||||||
# Create Initial Legacy Persona from config if not in presets
|
|
||||||
active_preset = ai_cfg.get("active_preset")
|
|
||||||
if active_preset and active_preset not in persona_manager.load_all():
|
|
||||||
persona = models.Persona(
|
|
||||||
name=active_preset,
|
|
||||||
preferred_models=[{
|
|
||||||
"provider": provider,
|
|
||||||
"model": model,
|
|
||||||
"temperature": ai_cfg.get("temperature"),
|
|
||||||
"max_output_tokens": ai_cfg.get("max_tokens")
|
|
||||||
}],
|
|
||||||
system_prompt=ai_cfg.get("system_prompt", "")
|
|
||||||
)
|
|
||||||
persona_manager.save_persona(persona, scope="global")
|
|
||||||
print(f"Created Initial Legacy persona from active_preset: {active_preset}")
|
|
||||||
|
|
||||||
print("Migration complete.")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
migrate()
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
|
|
||||||
def patch_gui(file_path):
|
|
||||||
with open(file_path, 'r', encoding='utf-8', newline='') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
# 1. Patch _render_provider_panel Session ID
|
|
||||||
content = content.replace(
|
|
||||||
' imgui.text(f"Session ID: {sid}")',
|
|
||||||
' imgui.text("Session ID:"); imgui.same_line(); self._render_selectable_label("gemini_cli_sid", sid, width=200)'
|
|
||||||
)
|
|
||||||
|
|
||||||
# 2. Patch _render_token_budget_panel Session Telemetry
|
|
||||||
content = content.replace(
|
|
||||||
' imgui.text_colored(C_RES, f"Tokens: {total:,} (In: {usage[\'input_tokens\']:,} Out: {usage[\'output_tokens\']:,})")',
|
|
||||||
' self._render_selectable_label("session_telemetry_tokens", f"Tokens: {total:,} (In: {usage[\'input_tokens\']:,} Out: {usage[\'output_tokens\']:,})", width=-1, color=C_RES)'
|
|
||||||
)
|
|
||||||
|
|
||||||
# 3. Patch _render_token_budget_panel MMA Tier Costs table
|
|
||||||
# This is trickier, let's find the loop
|
|
||||||
tier_table_pattern = re.compile(
|
|
||||||
r'(for tier, stats in self\.mma_tier_usage\.items\(\):\s+.*?imgui\.table_set_column_index\(0\); )imgui\.text\(tier\)(\s+imgui\.table_set_column_index\(1\); )imgui\.text\(model\.split\(\'-\'\)\[0\]\)(\s+imgui\.table_set_column_index\(2\); )imgui\.text\(f"\{tokens:,\}"\)(\s+imgui\.table_set_column_index\(3\); )imgui\.text_colored\(imgui\.ImVec4\(0\.2, 0\.9, 0\.2, 1\), f"\$\{cost:\.4f\}"\)',
|
|
||||||
re.DOTALL
|
|
||||||
)
|
|
||||||
|
|
||||||
def tier_replacement(match):
|
|
||||||
return (match.group(1) + 'self._render_selectable_label(f"tier_{tier}", tier, width=-1)' +
|
|
||||||
match.group(2) + 'self._render_selectable_label(f"model_{tier}", model.split("-")[0], width=-1)' +
|
|
||||||
match.group(3) + 'self._render_selectable_label(f"tokens_{tier}", f"{tokens:,}", width=-1)' +
|
|
||||||
match.group(4) + 'self._render_selectable_label(f"cost_{tier}", f"${cost:.4f}", width=-1, color=imgui.ImVec4(0.2, 0.9, 0.2, 1))')
|
|
||||||
|
|
||||||
content = tier_table_pattern.sub(tier_replacement, content)
|
|
||||||
|
|
||||||
# 4. Patch _render_token_budget_panel Session Total
|
|
||||||
content = content.replace(
|
|
||||||
' imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"Session Total: ${tier_total:.4f}")',
|
|
||||||
' self._render_selectable_label("session_total_cost", f"Session Total: ${tier_total:.4f}", width=-1, color=imgui.ImVec4(0, 1, 0, 1))'
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(file_path, 'w', encoding='utf-8', newline='') as f:
|
|
||||||
f.write(content)
|
|
||||||
print("Successfully patched src/gui_2.py for selectable metrics")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
patch_gui("src/gui_2.py")
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
with open("src/gui_2.py", "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
# 1. In _render_provider_panel, remove Fetch Models
|
|
||||||
old_fetch = """ imgui.text("Model")
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button("Fetch Models"):
|
|
||||||
self._fetch_models(self.current_provider)
|
|
||||||
if imgui.begin_list_box("##models", imgui.ImVec2(-1, 120)):"""
|
|
||||||
new_fetch = """ imgui.text("Model")
|
|
||||||
if imgui.begin_list_box("##models", imgui.ImVec2(-1, 120)):"""
|
|
||||||
content = content.replace(old_fetch, new_fetch)
|
|
||||||
|
|
||||||
# 2. Extract Persona block
|
|
||||||
# We need to find the start of 'imgui.text("Persona")' and end of 'self._editing_persona_is_new = True'
|
|
||||||
# Let's be very careful.
|
|
||||||
old_persona_block = """ imgui.text("Persona")
|
|
||||||
if not hasattr(self, 'ui_active_persona'):
|
|
||||||
self.ui_active_persona = ""
|
|
||||||
personas = getattr(self.controller, 'personas', {})
|
|
||||||
if imgui.begin_combo("##persona", self.ui_active_persona or "None"):
|
|
||||||
if imgui.selectable("None", not self.ui_active_persona)[0]:
|
|
||||||
self.ui_active_persona = ""
|
|
||||||
for pname in sorted(personas.keys()):
|
|
||||||
if imgui.selectable(pname, pname == self.ui_active_persona)[0]:
|
|
||||||
self.ui_active_persona = pname
|
|
||||||
if pname in personas:
|
|
||||||
persona = personas[pname]
|
|
||||||
self._editing_persona_name = persona.name
|
|
||||||
self._editing_persona_provider = persona.provider or ""
|
|
||||||
self._editing_persona_model = persona.model or ""
|
|
||||||
self._editing_persona_system_prompt = persona.system_prompt or ""
|
|
||||||
self._editing_persona_temperature = persona.temperature or 0.7
|
|
||||||
self._editing_persona_max_tokens = persona.max_output_tokens or 4096
|
|
||||||
self._editing_persona_tool_preset_id = persona.tool_preset or ""
|
|
||||||
self._editing_persona_bias_profile_id = persona.bias_profile or ""
|
|
||||||
import json
|
|
||||||
self._editing_persona_preferred_models = json.dumps(persona.preferred_models) if persona.preferred_models else "[]"
|
|
||||||
self._editing_persona_is_new = False
|
|
||||||
if persona.provider and persona.provider in self.controller.PROVIDERS:
|
|
||||||
self.current_provider = persona.provider
|
|
||||||
if persona.model:
|
|
||||||
self.current_model = persona.model
|
|
||||||
if persona.temperature is not None:
|
|
||||||
ai_client.temperature = persona.temperature
|
|
||||||
if persona.max_output_tokens:
|
|
||||||
ai_client.max_output_tokens = persona.max_output_tokens
|
|
||||||
if persona.system_prompt:
|
|
||||||
ai_client.system_instruction = persona.system_prompt
|
|
||||||
if persona.tool_preset:
|
|
||||||
self.ui_active_tool_preset = persona.tool_preset
|
|
||||||
ai_client.set_tool_preset(persona.tool_preset)
|
|
||||||
if persona.bias_profile:
|
|
||||||
self.ui_active_bias_profile = persona.bias_profile
|
|
||||||
ai_client.set_bias_profile(persona.bias_profile)
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button("Manage Personas"):
|
|
||||||
self.show_persona_editor_window = True
|
|
||||||
if self.ui_active_persona and self.ui_active_persona in personas:
|
|
||||||
persona = personas[self.ui_active_persona]
|
|
||||||
self._editing_persona_name = persona.name
|
|
||||||
self._editing_persona_provider = persona.provider or ""
|
|
||||||
self._editing_persona_model = persona.model or ""
|
|
||||||
self._editing_persona_system_prompt = persona.system_prompt or ""
|
|
||||||
self._editing_persona_temperature = persona.temperature if persona.temperature is not None else 0.7
|
|
||||||
self._editing_persona_max_tokens = persona.max_output_tokens if persona.max_output_tokens is not None else 4096
|
|
||||||
self._editing_persona_tool_preset_id = persona.tool_preset or ""
|
|
||||||
self._editing_persona_bias_profile_id = persona.bias_profile or ""
|
|
||||||
self._editing_persona_preferred_models_list = list(persona.preferred_models) if persona.preferred_models else []
|
|
||||||
self._editing_persona_scope = self.controller.persona_manager.get_persona_scope(persona.name)
|
|
||||||
self._editing_persona_is_new = False
|
|
||||||
else:
|
|
||||||
self._editing_persona_name = ""
|
|
||||||
self._editing_persona_provider = self.current_provider
|
|
||||||
self._editing_persona_model = self.current_model
|
|
||||||
self._editing_persona_system_prompt = ""
|
|
||||||
self._editing_persona_temperature = 0.7
|
|
||||||
self._editing_persona_max_tokens = 4096
|
|
||||||
self._editing_persona_tool_preset_id = ""
|
|
||||||
self._editing_persona_bias_profile_id = ""
|
|
||||||
self._editing_persona_preferred_models_list = []
|
|
||||||
self._editing_persona_scope = "project"
|
|
||||||
self._editing_persona_is_new = True"""
|
|
||||||
|
|
||||||
# We need to extract the bias profile block as well
|
|
||||||
old_bias_block = """ imgui.text("Bias Profile")
|
|
||||||
if imgui.begin_combo("##bias", self.ui_active_bias_profile or "None"):
|
|
||||||
if imgui.selectable("None", not self.ui_active_bias_profile)[0]:
|
|
||||||
self.ui_active_bias_profile = ""
|
|
||||||
ai_client.set_bias_profile(None)
|
|
||||||
for bname in sorted(self.bias_profiles.keys()):
|
|
||||||
if imgui.selectable(bname, bname == self.ui_active_bias_profile)[0]:
|
|
||||||
self.ui_active_bias_profile = bname
|
|
||||||
ai_client.set_bias_profile(bname)
|
|
||||||
imgui.end_combo()"""
|
|
||||||
|
|
||||||
# Remove them from their original spots
|
|
||||||
content = content.replace(old_bias_block, "")
|
|
||||||
content = content.replace(old_persona_block, "")
|
|
||||||
|
|
||||||
# Insert Persona block at the top of _render_provider_panel
|
|
||||||
old_provider_start = """ def _render_provider_panel(self) -> None:
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_provider_panel")
|
|
||||||
imgui.text("Provider")"""
|
|
||||||
new_provider_start = f""" def _render_provider_panel(self) -> None:
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_provider_panel")
|
|
||||||
{old_persona_block}
|
|
||||||
imgui.separator()
|
|
||||||
imgui.text("Provider")"""
|
|
||||||
content = content.replace(old_provider_start, new_provider_start)
|
|
||||||
|
|
||||||
# Update _render_agent_tools_panel
|
|
||||||
old_agent_tools_start = """ def _render_agent_tools_panel(self) -> None:
|
|
||||||
imgui.text_colored(C_LBL, 'Active Tool Preset')"""
|
|
||||||
new_agent_tools_start = f""" def _render_agent_tools_panel(self) -> None:
|
|
||||||
if imgui.collapsing_header("Active Tool Presets & Biases", imgui.TreeNodeFlags_.default_open):
|
|
||||||
imgui.text("Tool Preset")"""
|
|
||||||
content = content.replace(old_agent_tools_start, new_agent_tools_start)
|
|
||||||
|
|
||||||
# Wait, if I do collapsing header, I need to indent the rest of the function.
|
|
||||||
# Instead of indenting the whole function, I can just use the header.
|
|
||||||
# But wait, ImGui collapsing_header doesn't require indenting, it just returns true if open.
|
|
||||||
# So I should write it properly:
|
|
||||||
old_agent_tools_func = """ def _render_agent_tools_panel(self) -> None:
|
|
||||||
imgui.text_colored(C_LBL, 'Active Tool Preset')
|
|
||||||
presets = self.controller.tool_presets
|
|
||||||
preset_names = [""] + sorted(list(presets.keys()))
|
|
||||||
|
|
||||||
# Gracefully handle None or missing preset
|
|
||||||
active = getattr(self, "ui_active_tool_preset", "")
|
|
||||||
if active is None: active = ""
|
|
||||||
try:
|
|
||||||
idx = preset_names.index(active)
|
|
||||||
except ValueError:
|
|
||||||
idx = 0
|
|
||||||
|
|
||||||
ch, new_idx = imgui.combo("##tool_preset_select", idx, preset_names)
|
|
||||||
if ch:
|
|
||||||
self.ui_active_tool_preset = preset_names[new_idx]
|
|
||||||
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button("Manage Presets##tools"):
|
|
||||||
self.show_tool_preset_manager_window = True
|
|
||||||
if imgui.is_item_hovered():
|
|
||||||
imgui.set_tooltip("Configure tool availability and default modes.")
|
|
||||||
|
|
||||||
imgui.dummy(imgui.ImVec2(0, 8))
|
|
||||||
active_name = self.ui_active_tool_preset
|
|
||||||
if active_name and active_name in presets:
|
|
||||||
preset = presets[active_name]
|
|
||||||
for cat_name, tools in preset.categories.items():
|
|
||||||
if imgui.tree_node(cat_name):
|
|
||||||
for tool in tools:
|
|
||||||
if tool.weight >= 5:
|
|
||||||
imgui.text_colored(vec4(255, 100, 100), "[HIGH]")
|
|
||||||
imgui.same_line()
|
|
||||||
elif tool.weight == 4:
|
|
||||||
imgui.text_colored(vec4(255, 255, 100), "[PREF]")
|
|
||||||
imgui.same_line()
|
|
||||||
elif tool.weight == 2:
|
|
||||||
imgui.text_colored(vec4(255, 150, 50), "[REJECT]")
|
|
||||||
imgui.same_line()
|
|
||||||
elif tool.weight <= 1:
|
|
||||||
imgui.text_colored(vec4(180, 180, 180), "[LOW]")
|
|
||||||
imgui.same_line()
|
|
||||||
|
|
||||||
imgui.text(tool.name)
|
|
||||||
imgui.same_line(180)
|
|
||||||
|
|
||||||
mode = tool.approval
|
|
||||||
if imgui.radio_button(f"Auto##{cat_name}_{tool.name}", mode == "auto"):
|
|
||||||
tool.approval = "auto"
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.radio_button(f"Ask##{cat_name}_{tool.name}", mode == "ask"):
|
|
||||||
tool.approval = "ask"
|
|
||||||
imgui.tree_pop()"""
|
|
||||||
|
|
||||||
new_agent_tools_func = """ def _render_agent_tools_panel(self) -> None:
|
|
||||||
if imgui.collapsing_header("Active Tool Presets & Biases", imgui.TreeNodeFlags_.default_open):
|
|
||||||
imgui.text("Tool Preset")
|
|
||||||
presets = self.controller.tool_presets
|
|
||||||
preset_names = [""] + sorted(list(presets.keys()))
|
|
||||||
|
|
||||||
# Gracefully handle None or missing preset
|
|
||||||
active = getattr(self, "ui_active_tool_preset", "")
|
|
||||||
if active is None: active = ""
|
|
||||||
try:
|
|
||||||
idx = preset_names.index(active)
|
|
||||||
except ValueError:
|
|
||||||
idx = 0
|
|
||||||
|
|
||||||
ch, new_idx = imgui.combo("##tool_preset_select", idx, preset_names)
|
|
||||||
if ch:
|
|
||||||
self.ui_active_tool_preset = preset_names[new_idx]
|
|
||||||
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button("Manage Tools##tools"):
|
|
||||||
self.show_tool_preset_manager_window = True
|
|
||||||
if imgui.is_item_hovered():
|
|
||||||
imgui.set_tooltip("Configure tool availability and default modes.")
|
|
||||||
|
|
||||||
imgui.dummy(imgui.ImVec2(0, 4))
|
|
||||||
""" + "\n ".join(old_bias_block.split("\n")) + """
|
|
||||||
|
|
||||||
imgui.dummy(imgui.ImVec2(0, 8))
|
|
||||||
active_name = self.ui_active_tool_preset
|
|
||||||
if active_name and active_name in presets:
|
|
||||||
preset = presets[active_name]
|
|
||||||
for cat_name, tools in preset.categories.items():
|
|
||||||
if imgui.tree_node(cat_name):
|
|
||||||
for tool in tools:
|
|
||||||
if tool.weight >= 5:
|
|
||||||
imgui.text_colored(vec4(255, 100, 100), "[HIGH]")
|
|
||||||
imgui.same_line()
|
|
||||||
elif tool.weight == 4:
|
|
||||||
imgui.text_colored(vec4(255, 255, 100), "[PREF]")
|
|
||||||
imgui.same_line()
|
|
||||||
elif tool.weight == 2:
|
|
||||||
imgui.text_colored(vec4(255, 150, 50), "[REJECT]")
|
|
||||||
imgui.same_line()
|
|
||||||
elif tool.weight <= 1:
|
|
||||||
imgui.text_colored(vec4(180, 180, 180), "[LOW]")
|
|
||||||
imgui.same_line()
|
|
||||||
|
|
||||||
imgui.text(tool.name)
|
|
||||||
imgui.same_line(180)
|
|
||||||
|
|
||||||
mode = tool.approval
|
|
||||||
if imgui.radio_button(f"Auto##{cat_name}_{tool.name}", mode == "auto"):
|
|
||||||
tool.approval = "auto"
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.radio_button(f"Ask##{cat_name}_{tool.name}", mode == "ask"):
|
|
||||||
tool.approval = "ask"
|
|
||||||
imgui.tree_pop()"""
|
|
||||||
content = content.replace(old_agent_tools_func, new_agent_tools_func)
|
|
||||||
|
|
||||||
# Fix cache text display in Usage Analytics
|
|
||||||
content = content.replace('self._gemini_cache_text = f"Gemini Caches: {count} ({size_bytes / 1024:.1f} KB)"', 'self._gemini_cache_text = f"Cache Usage: {count} ({size_bytes / 1024:.1f} KB)"')
|
|
||||||
content = content.replace('imgui.text_colored(C_LBL, f"Gemini Cache: ACTIVE | Age: {age:.0f}s / {ttl}s | Renews at: {ttl * 0.9:.0f}s")', 'imgui.text_colored(C_LBL, f"Cache Usage: ACTIVE | Age: {age:.0f}s / {ttl}s | Renews at: {ttl * 0.9:.0f}s")')
|
|
||||||
content = content.replace('imgui.text_disabled("Gemini Cache: INACTIVE")', 'imgui.text_disabled("Cache Usage: INACTIVE")')
|
|
||||||
|
|
||||||
# Also, user requested: "The persona should problably just mess with the project system prompt for now."
|
|
||||||
# Currently in persona selection: `ai_client.system_instruction = persona.system_prompt`
|
|
||||||
# Let's change that to `self.ui_project_system_prompt = persona.system_prompt` and remove ai_client direct injection
|
|
||||||
content = content.replace('ai_client.system_instruction = persona.system_prompt', 'self.ui_project_system_prompt = persona.system_prompt')
|
|
||||||
|
|
||||||
with open("src/gui_2.py", "w", encoding="utf-8") as f:
|
|
||||||
f.write(content)
|
|
||||||
print("done")
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
with open("src/gui_2.py", "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
# 1. Update _gui_func:
|
|
||||||
# Extract Persona out of Provider panel. I will create a new method _render_persona_selector_panel
|
|
||||||
old_gui_settings = """ if self.show_windows.get("AI Settings", False):
|
|
||||||
exp, opened = imgui.begin("AI Settings", self.show_windows["AI Settings"])
|
|
||||||
self.show_windows["AI Settings"] = bool(opened)
|
|
||||||
if exp:
|
|
||||||
if imgui.collapsing_header("Provider & Model"):
|
|
||||||
self._render_provider_panel()
|
|
||||||
if imgui.collapsing_header("System Prompts"):
|
|
||||||
self._render_system_prompts_panel()
|
|
||||||
self._render_agent_tools_panel()
|
|
||||||
self._render_cache_panel()
|
|
||||||
|
|
||||||
imgui.end()
|
|
||||||
if self.ui_separate_usage_analytics and self.show_windows.get("Usage Analytics", False):
|
|
||||||
exp, opened = imgui.begin("Usage Analytics", self.show_windows["Usage Analytics"])
|
|
||||||
self.show_windows["Usage Analytics"] = bool(opened)
|
|
||||||
if exp:
|
|
||||||
self._render_usage_analytics_panel()
|
|
||||||
imgui.end()"""
|
|
||||||
|
|
||||||
new_gui_settings = """ if self.show_windows.get("AI Settings", False):
|
|
||||||
exp, opened = imgui.begin("AI Settings", self.show_windows["AI Settings"])
|
|
||||||
self.show_windows["AI Settings"] = bool(opened)
|
|
||||||
if exp:
|
|
||||||
self._render_persona_selector_panel()
|
|
||||||
if imgui.collapsing_header("Provider & Model"):
|
|
||||||
self._render_provider_panel()
|
|
||||||
if imgui.collapsing_header("System Prompts"):
|
|
||||||
self._render_system_prompts_panel()
|
|
||||||
self._render_agent_tools_panel()
|
|
||||||
|
|
||||||
imgui.end()
|
|
||||||
if self.ui_separate_usage_analytics and self.show_windows.get("Usage Analytics", False):
|
|
||||||
exp, opened = imgui.begin("Usage Analytics", self.show_windows["Usage Analytics"])
|
|
||||||
self.show_windows["Usage Analytics"] = bool(opened)
|
|
||||||
if exp:
|
|
||||||
self._render_usage_analytics_panel()
|
|
||||||
imgui.end()"""
|
|
||||||
|
|
||||||
content = content.replace(old_gui_settings, new_gui_settings)
|
|
||||||
|
|
||||||
# Update _render_usage_analytics_panel
|
|
||||||
old_usage = """ def _render_usage_analytics_panel(self) -> None:
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_usage_analytics_panel")
|
|
||||||
self._render_token_budget_panel()
|
|
||||||
imgui.separator()
|
|
||||||
self._render_tool_analytics_panel()
|
|
||||||
imgui.separator()
|
|
||||||
self._render_session_insights_panel()
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_usage_analytics_panel")"""
|
|
||||||
|
|
||||||
new_usage = """ def _render_usage_analytics_panel(self) -> None:
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_usage_analytics_panel")
|
|
||||||
self._render_token_budget_panel()
|
|
||||||
imgui.separator()
|
|
||||||
self._render_cache_panel()
|
|
||||||
imgui.separator()
|
|
||||||
self._render_tool_analytics_panel()
|
|
||||||
imgui.separator()
|
|
||||||
self._render_session_insights_panel()
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_usage_analytics_panel")"""
|
|
||||||
content = content.replace(old_usage, new_usage)
|
|
||||||
|
|
||||||
# Remove the persona block from _render_provider_panel and put it in _render_persona_selector_panel
|
|
||||||
old_persona_block = """ def _render_provider_panel(self) -> None:
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_provider_panel")
|
|
||||||
imgui.text("Persona")
|
|
||||||
if not hasattr(self, 'ui_active_persona'):
|
|
||||||
self.ui_active_persona = ""
|
|
||||||
personas = getattr(self.controller, 'personas', {})
|
|
||||||
if imgui.begin_combo("##persona", self.ui_active_persona or "None"):
|
|
||||||
if imgui.selectable("None", not self.ui_active_persona)[0]:
|
|
||||||
self.ui_active_persona = ""
|
|
||||||
for pname in sorted(personas.keys()):
|
|
||||||
if imgui.selectable(pname, pname == self.ui_active_persona)[0]:
|
|
||||||
self.ui_active_persona = pname
|
|
||||||
if pname in personas:
|
|
||||||
persona = personas[pname]
|
|
||||||
self._editing_persona_name = persona.name
|
|
||||||
self._editing_persona_provider = persona.provider or ""
|
|
||||||
self._editing_persona_model = persona.model or ""
|
|
||||||
self._editing_persona_system_prompt = persona.system_prompt or ""
|
|
||||||
self._editing_persona_temperature = persona.temperature or 0.7
|
|
||||||
self._editing_persona_max_tokens = persona.max_output_tokens or 4096
|
|
||||||
self._editing_persona_tool_preset_id = persona.tool_preset or ""
|
|
||||||
self._editing_persona_bias_profile_id = persona.bias_profile or ""
|
|
||||||
import json
|
|
||||||
self._editing_persona_preferred_models = json.dumps(persona.preferred_models) if persona.preferred_models else "[]"
|
|
||||||
self._editing_persona_is_new = False
|
|
||||||
if persona.provider and persona.provider in self.controller.PROVIDERS:
|
|
||||||
self.current_provider = persona.provider
|
|
||||||
if persona.model:
|
|
||||||
self.current_model = persona.model
|
|
||||||
if persona.temperature is not None:
|
|
||||||
ai_client.temperature = persona.temperature
|
|
||||||
if persona.max_output_tokens:
|
|
||||||
ai_client.max_output_tokens = persona.max_output_tokens
|
|
||||||
if persona.system_prompt:
|
|
||||||
self.ui_project_system_prompt = persona.system_prompt
|
|
||||||
if persona.tool_preset:
|
|
||||||
self.ui_active_tool_preset = persona.tool_preset
|
|
||||||
ai_client.set_tool_preset(persona.tool_preset)
|
|
||||||
if persona.bias_profile:
|
|
||||||
self.ui_active_bias_profile = persona.bias_profile
|
|
||||||
ai_client.set_bias_profile(persona.bias_profile)
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button("Manage Personas"):
|
|
||||||
self.show_persona_editor_window = True
|
|
||||||
if self.ui_active_persona and self.ui_active_persona in personas:
|
|
||||||
persona = personas[self.ui_active_persona]
|
|
||||||
self._editing_persona_name = persona.name
|
|
||||||
self._editing_persona_provider = persona.provider or ""
|
|
||||||
self._editing_persona_model = persona.model or ""
|
|
||||||
self._editing_persona_system_prompt = persona.system_prompt or ""
|
|
||||||
self._editing_persona_temperature = persona.temperature if persona.temperature is not None else 0.7
|
|
||||||
self._editing_persona_max_tokens = persona.max_output_tokens if persona.max_output_tokens is not None else 4096
|
|
||||||
self._editing_persona_tool_preset_id = persona.tool_preset or ""
|
|
||||||
self._editing_persona_bias_profile_id = persona.bias_profile or ""
|
|
||||||
self._editing_persona_preferred_models_list = list(persona.preferred_models) if persona.preferred_models else []
|
|
||||||
self._editing_persona_scope = self.controller.persona_manager.get_persona_scope(persona.name)
|
|
||||||
self._editing_persona_is_new = False
|
|
||||||
else:
|
|
||||||
self._editing_persona_name = ""
|
|
||||||
self._editing_persona_provider = self.current_provider
|
|
||||||
self._editing_persona_model = self.current_model
|
|
||||||
self._editing_persona_system_prompt = ""
|
|
||||||
self._editing_persona_temperature = 0.7
|
|
||||||
self._editing_persona_max_tokens = 4096
|
|
||||||
self._editing_persona_tool_preset_id = ""
|
|
||||||
self._editing_persona_bias_profile_id = ""
|
|
||||||
self._editing_persona_preferred_models_list = []
|
|
||||||
self._editing_persona_scope = "project"
|
|
||||||
self._editing_persona_is_new = True
|
|
||||||
imgui.separator()
|
|
||||||
imgui.text("Provider")"""
|
|
||||||
|
|
||||||
new_persona_block = """ def _render_persona_selector_panel(self) -> None:
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_persona_selector_panel")
|
|
||||||
imgui.text("Persona")
|
|
||||||
if not hasattr(self, 'ui_active_persona'):
|
|
||||||
self.ui_active_persona = ""
|
|
||||||
personas = getattr(self.controller, 'personas', {})
|
|
||||||
if imgui.begin_combo("##persona", self.ui_active_persona or "None"):
|
|
||||||
if imgui.selectable("None", not self.ui_active_persona)[0]:
|
|
||||||
self.ui_active_persona = ""
|
|
||||||
for pname in sorted(personas.keys()):
|
|
||||||
if imgui.selectable(pname, pname == self.ui_active_persona)[0]:
|
|
||||||
self.ui_active_persona = pname
|
|
||||||
if pname in personas:
|
|
||||||
persona = personas[pname]
|
|
||||||
self._editing_persona_name = persona.name
|
|
||||||
self._editing_persona_system_prompt = persona.system_prompt or ""
|
|
||||||
self._editing_persona_tool_preset_id = persona.tool_preset or ""
|
|
||||||
self._editing_persona_bias_profile_id = persona.bias_profile or ""
|
|
||||||
import copy
|
|
||||||
self._editing_persona_preferred_models_list = copy.deepcopy(persona.preferred_models) if persona.preferred_models else []
|
|
||||||
self._editing_persona_is_new = False
|
|
||||||
|
|
||||||
# Apply persona to current state immediately
|
|
||||||
if persona.preferred_models and len(persona.preferred_models) > 0:
|
|
||||||
first_model = persona.preferred_models[0]
|
|
||||||
if first_model.get("provider"):
|
|
||||||
self.current_provider = first_model.get("provider")
|
|
||||||
if first_model.get("model"):
|
|
||||||
self.current_model = first_model.get("model")
|
|
||||||
if first_model.get("temperature") is not None:
|
|
||||||
ai_client.temperature = first_model.get("temperature")
|
|
||||||
self.temperature = first_model.get("temperature")
|
|
||||||
if first_model.get("max_output_tokens"):
|
|
||||||
ai_client.max_output_tokens = first_model.get("max_output_tokens")
|
|
||||||
self.max_tokens = first_model.get("max_output_tokens")
|
|
||||||
if first_model.get("history_trunc_limit"):
|
|
||||||
self.history_trunc_limit = first_model.get("history_trunc_limit")
|
|
||||||
|
|
||||||
if persona.system_prompt:
|
|
||||||
self.ui_project_system_prompt = persona.system_prompt
|
|
||||||
if persona.tool_preset:
|
|
||||||
self.ui_active_tool_preset = persona.tool_preset
|
|
||||||
ai_client.set_tool_preset(persona.tool_preset)
|
|
||||||
if persona.bias_profile:
|
|
||||||
self.ui_active_bias_profile = persona.bias_profile
|
|
||||||
ai_client.set_bias_profile(persona.bias_profile)
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button("Manage Personas"):
|
|
||||||
self.show_persona_editor_window = True
|
|
||||||
if self.ui_active_persona and self.ui_active_persona in personas:
|
|
||||||
persona = personas[self.ui_active_persona]
|
|
||||||
self._editing_persona_name = persona.name
|
|
||||||
self._editing_persona_system_prompt = persona.system_prompt or ""
|
|
||||||
self._editing_persona_tool_preset_id = persona.tool_preset or ""
|
|
||||||
self._editing_persona_bias_profile_id = persona.bias_profile or ""
|
|
||||||
import copy
|
|
||||||
self._editing_persona_preferred_models_list = copy.deepcopy(persona.preferred_models) if persona.preferred_models else []
|
|
||||||
self._editing_persona_scope = self.controller.persona_manager.get_persona_scope(persona.name)
|
|
||||||
self._editing_persona_is_new = False
|
|
||||||
else:
|
|
||||||
self._editing_persona_name = ""
|
|
||||||
self._editing_persona_system_prompt = ""
|
|
||||||
self._editing_persona_tool_preset_id = ""
|
|
||||||
self._editing_persona_bias_profile_id = ""
|
|
||||||
self._editing_persona_preferred_models_list = [{
|
|
||||||
"provider": self.current_provider,
|
|
||||||
"model": self.current_model,
|
|
||||||
"temperature": getattr(self, "temperature", 0.7),
|
|
||||||
"max_output_tokens": getattr(self, "max_tokens", 4096),
|
|
||||||
"history_trunc_limit": getattr(self, "history_trunc_limit", 900000)
|
|
||||||
}]
|
|
||||||
self._editing_persona_scope = "project"
|
|
||||||
self._editing_persona_is_new = True
|
|
||||||
imgui.separator()
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_persona_selector_panel")
|
|
||||||
|
|
||||||
def _render_provider_panel(self) -> None:
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_provider_panel")
|
|
||||||
imgui.text("Provider")"""
|
|
||||||
content = content.replace(old_persona_block, new_persona_block)
|
|
||||||
|
|
||||||
with open("src/gui_2.py", "w", encoding="utf-8") as f:
|
|
||||||
f.write(content)
|
|
||||||
print("done gui updates")
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import sys
|
|
||||||
with open('config.toml', 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
BOM = bytes([0xef, 0xbb, 0xbf])
|
|
||||||
if data.startswith(BOM):
|
|
||||||
with open('config.toml', 'wb') as f:
|
|
||||||
f.write(data[3:])
|
|
||||||
print('BOM removed')
|
|
||||||
else:
|
|
||||||
print('No BOM found')
|
|
||||||
sys.exit(0)
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
|
|
||||||
file_path = Path('src/app_controller.py')
|
|
||||||
lines = file_path.read_text(encoding='utf-8').splitlines()
|
|
||||||
|
|
||||||
# We know the issue starts around line 1405 where `def cb_exit_prior_session(self):` is indented with 2 spaces.
|
|
||||||
# We will unindent everything that has 2 or more spaces back by 1 space,
|
|
||||||
# until we hit a line that is correctly 1-space indented (like `def start_services` or similar, if it exists).
|
|
||||||
# Actually, let's just use a state machine: find `def cb_exit_prior_session` and unindent everything until `def start_services` or end of file.
|
|
||||||
|
|
||||||
in_bad_block = False
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if re.match(r'^ def cb_exit_prior_session\(self\):', line):
|
|
||||||
in_bad_block = True
|
|
||||||
|
|
||||||
if in_bad_block:
|
|
||||||
if line.startswith(' '):
|
|
||||||
lines[i] = line[1:]
|
|
||||||
elif line.startswith(' '):
|
|
||||||
pass # Keep it if it's already 1 space
|
|
||||||
|
|
||||||
# Stop if we hit a properly indented class method
|
|
||||||
if in_bad_block and re.match(r'^ def start_services', line):
|
|
||||||
in_bad_block = False
|
|
||||||
|
|
||||||
file_path.write_text('\n'.join(lines) + '\n', encoding='utf-8')
|
|
||||||
print("Scope repair complete.")
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
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
|
|
||||||
original_line = lines[i-1]
|
|
||||||
indent = line_indent.get(i, 0)
|
|
||||||
stripped = original_line.lstrip()
|
|
||||||
# Enforce a 1-line gap before definitions/classes
|
|
||||||
is_def_start = stripped.startswith(('def ', 'class ', 'async def ', '@'))
|
|
||||||
if is_def_start and output and consecutive_blanks == 0:
|
|
||||||
prev_line = output[-1].strip()
|
|
||||||
# Don't add a gap if immediately following a block opener or another decorator
|
|
||||||
if prev_line and not prev_line.endswith(':') and not prev_line.startswith('@'):
|
|
||||||
output.append("\n")
|
|
||||||
consecutive_blanks += 1
|
|
||||||
consecutive_blanks = 0
|
|
||||||
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)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
from imgui_bundle import hello_imgui, imgui
|
|
||||||
|
|
||||||
def on_gui():
|
|
||||||
imgui.text("Hello world")
|
|
||||||
|
|
||||||
params = hello_imgui.RunnerParams()
|
|
||||||
params.app_window_params.borderless = True
|
|
||||||
params.app_window_params.borderless_movable = True
|
|
||||||
params.app_window_params.borderless_resizable = True
|
|
||||||
params.app_window_params.borderless_closable = True
|
|
||||||
|
|
||||||
hello_imgui.run(params)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
from imgui_bundle import imgui, hello_imgui
|
|
||||||
|
|
||||||
def test_font_config():
|
|
||||||
config = imgui.ImFontConfig()
|
|
||||||
config.oversample_h = 3
|
|
||||||
config.oversample_v = 3
|
|
||||||
print(f"Oversample H: {config.oversample_h}")
|
|
||||||
print(f"Oversample V: {config.oversample_v}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_font_config()
|
|
||||||
Reference in New Issue
Block a user