progress on context composition
This commit is contained in:
+71
-40
@@ -199,52 +199,83 @@ def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[
|
||||
mtime = path.stat().st_mtime
|
||||
error = False
|
||||
if not error and view_mode != "full":
|
||||
if view_mode == "summary":
|
||||
content = summarize.summarise_file(path, content)
|
||||
elif view_mode == "skeleton":
|
||||
if path.suffix == ".py":
|
||||
if not parser: parser = ASTParser("python")
|
||||
content = parser.get_skeleton(content, path=str(path))
|
||||
elif path.suffix in ['.c', '.h', '.cpp', '.hpp', '.cxx', '.cc']:
|
||||
from src import mcp_client
|
||||
if path.suffix in ['.c', '.h']: content = mcp_client.ts_c_get_skeleton(str(path))
|
||||
else: content = mcp_client.ts_cpp_get_skeleton(str(path))
|
||||
else:
|
||||
content = summarize.summarise_file(path, content)
|
||||
elif view_mode == "outline":
|
||||
if path.suffix == ".py":
|
||||
if not parser: parser = ASTParser("python")
|
||||
content = parser.get_code_outline(content, path=str(path))
|
||||
elif path.suffix in ['.c', '.h', '.cpp', '.hpp', '.cxx', '.cc']:
|
||||
from src import mcp_client
|
||||
if path.suffix in ['.c', '.h']: content = mcp_client.ts_c_get_code_outline(str(path))
|
||||
else: content = mcp_client.ts_cpp_get_code_outline(str(path))
|
||||
else:
|
||||
content = summarize.summarise_file(path, content)
|
||||
elif view_mode == "none":
|
||||
content = "(context excluded)"
|
||||
elif view_mode == "custom":
|
||||
if custom_slices:
|
||||
lines = content.splitlines()
|
||||
slices_text = []
|
||||
for s in custom_slices:
|
||||
start = s.get("start_line", 1)
|
||||
end = s.get("end_line", len(lines))
|
||||
tag = s.get("tag", "unnamed")
|
||||
comment = s.get("comment", "")
|
||||
s_idx = max(0, start - 1)
|
||||
e_idx = min(len(lines), end)
|
||||
chunk = "\n".join(lines[s_idx:e_idx])
|
||||
slices_text.append(f"---\n[Slice: {tag}] ({comment})\nLines {start}-{end}:\n{chunk}")
|
||||
content = "\n\n".join(slices_text)
|
||||
else:
|
||||
try:
|
||||
if view_mode == "summary":
|
||||
content = summarize.summarise_file(path, content)
|
||||
elif view_mode == "skeleton":
|
||||
suffix_lower = path.suffix.lower()
|
||||
if suffix_lower == ".py":
|
||||
if not parser: parser = ASTParser("python")
|
||||
content = parser.get_skeleton(content, path=str(path))
|
||||
elif suffix_lower in ['.c', '.h', '.cpp', '.hpp', '.cxx', '.cc']:
|
||||
from src import mcp_client
|
||||
if suffix_lower in ['.c', '.h']: content = mcp_client.ts_c_get_skeleton(str(path))
|
||||
else: content = mcp_client.ts_cpp_get_skeleton(str(path))
|
||||
else:
|
||||
content = summarize.summarise_file(path, content)
|
||||
elif view_mode == "outline":
|
||||
suffix_lower = path.suffix.lower()
|
||||
if suffix_lower == ".py":
|
||||
if not parser: parser = ASTParser("python")
|
||||
content = parser.get_code_outline(content, path=str(path))
|
||||
elif suffix_lower in ['.c', '.h', '.cpp', '.hpp', '.cxx', '.cc']:
|
||||
from src import mcp_client
|
||||
if suffix_lower in ['.c', '.h']: content = mcp_client.ts_c_get_code_outline(str(path))
|
||||
else: content = mcp_client.ts_cpp_get_code_outline(str(path))
|
||||
else:
|
||||
content = summarize.summarise_file(path, content)
|
||||
elif view_mode == "masked":
|
||||
suffix_lower = path.suffix.lower()
|
||||
if ast_mask:
|
||||
mask_sections = []
|
||||
from src import mcp_client
|
||||
for symbol, mode in ast_mask.items():
|
||||
if mode == "hide": continue
|
||||
res = ""
|
||||
if suffix_lower == ".py":
|
||||
res = mcp_client.py_get_definition(str(path), symbol) if mode == "def" else mcp_client.py_get_signature(str(path), symbol)
|
||||
elif suffix_lower in [".c", ".h", ".cpp", ".hpp", ".cxx", ".cc"]:
|
||||
is_cpp = any(ext in suffix_lower for ext in [".cpp", ".hpp", ".cxx", ".cc"])
|
||||
if mode == "def":
|
||||
res = mcp_client.ts_cpp_get_definition(str(path), symbol) if is_cpp else mcp_client.ts_c_get_definition(str(path), symbol)
|
||||
else:
|
||||
res = mcp_client.ts_cpp_get_signature(str(path), symbol) if is_cpp else mcp_client.ts_c_get_signature(str(path), symbol)
|
||||
if res: mask_sections.append(res)
|
||||
if mask_sections:
|
||||
content = "\n\n".join(mask_sections)
|
||||
else:
|
||||
content = "(no masked sections visible)"
|
||||
else:
|
||||
content = "(no ast mask defined)"
|
||||
elif view_mode == "none":
|
||||
content = "(context excluded)"
|
||||
elif view_mode == "custom":
|
||||
if custom_slices:
|
||||
lines = content.splitlines()
|
||||
slices_text = []
|
||||
for s in custom_slices:
|
||||
start = s.get("start_line", 1)
|
||||
end = s.get("end_line", len(lines))
|
||||
tag = s.get("tag", "unnamed")
|
||||
comment = s.get("comment", "")
|
||||
s_idx = max(0, start - 1)
|
||||
e_idx = min(len(lines), end)
|
||||
chunk = "\n".join(lines[s_idx:e_idx])
|
||||
slices_text.append(f"---\n[Slice: {tag}] ({comment})\nLines {start}-{end}:\n{chunk}")
|
||||
content = "\n\n".join(slices_text)
|
||||
else:
|
||||
content = summarize.summarise_file(path, content)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
content = f"ERROR in {view_mode} view mode for {path}:\n{traceback.format_exc()}"
|
||||
error = True
|
||||
except FileNotFoundError:
|
||||
content = f"ERROR: file not found: {path}"
|
||||
mtime = 0.0
|
||||
error = True
|
||||
except Exception as e:
|
||||
content = f"ERROR: {e}"
|
||||
import traceback
|
||||
content = f"ERROR reading {path}:\n{traceback.format_exc()}"
|
||||
mtime = 0.0
|
||||
error = True
|
||||
items.append({"path": path, "entry": entry, "content": content, "error": error, "mtime": mtime, "tier": tier, "auto_aggregate": auto_aggregate, "force_full": force_full, "view_mode": view_mode, "ast_signatures": ast_signatures, "ast_definitions": ast_definitions, "ast_mask": ast_mask, "custom_slices": custom_slices})
|
||||
|
||||
+25
-26
@@ -2929,29 +2929,20 @@ class AppController:
|
||||
if name not in presets:
|
||||
raise KeyError(f"Context preset '{name}' not found.")
|
||||
preset = presets[name]
|
||||
# Apply it to the current state
|
||||
self.ui_file_paths = [f.path for f in preset.files]
|
||||
self.screenshots = list(preset.screenshots)
|
||||
self._save_active_project()
|
||||
# We need to tell gui_2 to populate the full FileItem state from the preset,
|
||||
# which it does in _handle_refresh_from_project. But we also need to pass the detailed properties.
|
||||
# We will let the project_manager handle merging in the preset files via configuration next turn.
|
||||
# Wait, project manager doesn't load preset files into self.files automatically here.
|
||||
# Let's write the preset files into self.project["files"] directly.
|
||||
import copy
|
||||
self.project.setdefault("files", {})["paths"] = [
|
||||
{
|
||||
"path": f.path,
|
||||
"view_mode": f.view_mode,
|
||||
"custom_slices": copy.deepcopy(f.custom_slices),
|
||||
"ast_mask": copy.deepcopy(f.ast_mask),
|
||||
"ast_signatures": getattr(f, "ast_signatures", False),
|
||||
"ast_definitions": getattr(f, "ast_definitions", False)
|
||||
} for f in preset.files
|
||||
]
|
||||
self._save_active_project()
|
||||
return preset
|
||||
|
||||
# Update only temporary context state, not project files
|
||||
import copy
|
||||
self.context_files = []
|
||||
for f in preset.files:
|
||||
fi = models.FileItem(path=f.path, view_mode=f.view_mode)
|
||||
fi.custom_slices = copy.deepcopy(f.custom_slices) if hasattr(f, 'custom_slices') else []
|
||||
fi.ast_mask = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {}
|
||||
fi.ast_signatures = getattr(f, 'ast_signatures', False)
|
||||
fi.ast_definitions = getattr(f, 'ast_definitions', False)
|
||||
self.context_files.append(fi)
|
||||
|
||||
self.screenshots = list(preset.screenshots)
|
||||
return preset
|
||||
def _cb_load_track(self, track_id: str) -> None:
|
||||
"""
|
||||
[C: src/gui_2.py:App._render_mma_track_browser]
|
||||
@@ -3459,11 +3450,19 @@ class AppController:
|
||||
models.save_config(self.config)
|
||||
track_id = self.active_track.id if self.active_track else None
|
||||
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=track_id)
|
||||
flat.setdefault("files", {})["paths"] = self.context_files
|
||||
|
||||
import copy
|
||||
flat["files"] = copy.copy(flat.get("files", {}))
|
||||
flat["files"]["paths"] = self.context_files
|
||||
|
||||
# Configure MCP so that aggregate.py can fetch skeletons for external files (e.g. gencpp)
|
||||
file_dicts = [f.to_dict() if hasattr(f, 'to_dict') else {"path": str(f)} for f in self.context_files]
|
||||
mcp_client.configure(file_dicts, [self.active_project_root] if self.active_project_root else None)
|
||||
import os
|
||||
file_dicts = []
|
||||
for f in self.context_files:
|
||||
p = f.path if hasattr(f, 'path') else str(f)
|
||||
if not os.path.isabs(p):
|
||||
p = os.path.join(self.ui_files_base_dir, p)
|
||||
file_dicts.append({"path": p})
|
||||
mcp_client.configure(file_dicts, [self.ui_files_base_dir])
|
||||
|
||||
persona = self.personas.get(self.ui_active_persona)
|
||||
strategy = persona.aggregation_strategy if persona else "auto"
|
||||
|
||||
+92
-40
@@ -627,7 +627,16 @@ class App:
|
||||
[C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset]
|
||||
"""
|
||||
preset = self.controller.load_context_preset(name)
|
||||
self.context_files = [models.FileItem(path=f.path, view_mode=f.view_mode) for f in preset.files]
|
||||
from src import models
|
||||
import copy
|
||||
self.context_files = []
|
||||
for f in preset.files:
|
||||
fi = models.FileItem(path=f.path, view_mode=f.view_mode)
|
||||
fi.custom_slices = copy.deepcopy(f.custom_slices) if hasattr(f, 'custom_slices') else []
|
||||
fi.ast_mask = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {}
|
||||
fi.ast_signatures = getattr(f, 'ast_signatures', False)
|
||||
fi.ast_definitions = getattr(f, 'ast_definitions', False)
|
||||
self.context_files.append(fi)
|
||||
self.screenshots = list(preset.screenshots)
|
||||
self.ui_file_paths = [f.path for f in preset.files]
|
||||
self.ui_screenshot_paths = list(preset.screenshots)
|
||||
@@ -3017,7 +3026,6 @@ def render_ast_inspector_modal(app: App) -> None:
|
||||
if imgui.is_item_hovered():
|
||||
app._hovered_ast_node = full_path
|
||||
|
||||
# Calculate space left and align radio buttons to the right
|
||||
btn_width = 150 # Estimated width of the 3 radio buttons
|
||||
avail_width = imgui.get_content_region_avail().x
|
||||
if avail_width > btn_width:
|
||||
@@ -3255,7 +3263,6 @@ def render_context_files_table(app: App) -> None:
|
||||
imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]")
|
||||
|
||||
def render_context_presets(app: App) -> None:
|
||||
imgui.text("Presets")
|
||||
presets = app.controller.project.get('context_presets', {})
|
||||
preset_names = [""] + sorted(presets.keys())
|
||||
active = getattr(app, "ui_active_context_preset", "")
|
||||
@@ -3264,47 +3271,86 @@ def render_context_presets(app: App) -> None:
|
||||
idx = preset_names.index(active)
|
||||
except ValueError:
|
||||
idx = 0
|
||||
ch, new_idx = imgui.combo("##ctx_preset", idx, preset_names)
|
||||
if ch:
|
||||
app.ui_active_context_preset = preset_names[new_idx]
|
||||
if preset_names[new_idx]: app.load_context_preset(preset_names[new_idx])
|
||||
imgui.same_line()
|
||||
changed, new_name = imgui.input_text("##new_preset", getattr(app, "ui_new_context_preset_name", ""))
|
||||
if changed: app.ui_new_context_preset_name = new_name
|
||||
imgui.same_line()
|
||||
if imgui.button("Save##ctx") or getattr(app, "_pending_save_ctx_click", False):
|
||||
app._pending_save_ctx_click = False
|
||||
name = getattr(app, "ui_new_context_preset_name", "").strip()
|
||||
if name:
|
||||
missing = []
|
||||
root = app.controller.active_project_root
|
||||
for f in app.context_files:
|
||||
path = f.path if hasattr(f, "path") else str(f)
|
||||
if not os.path.isabs(path):
|
||||
full_path = os.path.join(root, path)
|
||||
else:
|
||||
full_path = path
|
||||
if not os.path.exists(full_path):
|
||||
missing.append(path)
|
||||
|
||||
if missing:
|
||||
app.missing_context_files = missing
|
||||
app.show_missing_files_modal = True
|
||||
app.target_context_preset_name = name
|
||||
else:
|
||||
|
||||
with imscope.table("ctx_presets_layout", 2, imgui.TableFlags_.none):
|
||||
imgui.table_next_column()
|
||||
imgui.set_next_item_width(-1)
|
||||
ch, new_idx = imgui.combo("##ctx_preset", idx, preset_names)
|
||||
if ch:
|
||||
app.ui_active_context_preset = preset_names[new_idx]
|
||||
if preset_names[new_idx]:
|
||||
app.controller.load_context_preset(preset_names[new_idx])
|
||||
app.controller._refresh_from_project()
|
||||
app.context_files = list(app.controller.files)
|
||||
|
||||
imgui.table_next_column()
|
||||
if active:
|
||||
if imgui.button("Update##override", imgui.ImVec2(-1, 0)):
|
||||
preset_files = []
|
||||
for f in app.context_files:
|
||||
import copy
|
||||
from src import models
|
||||
p = f.path if hasattr(f, 'path') else str(f)
|
||||
vm = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm))
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(app.screenshots))
|
||||
slc = copy.deepcopy(f.custom_slices) if hasattr(f, 'custom_slices') else []
|
||||
msk = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {}
|
||||
sig = f.ast_signatures if hasattr(f, 'ast_signatures') else False
|
||||
dfn = f.ast_definitions if hasattr(f, 'ast_definitions') else False
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm, custom_slices=slc, ast_mask=msk, ast_signatures=sig, ast_definitions=dfn))
|
||||
preset = models.ContextPreset(name=active, files=preset_files, screenshots=list(app.screenshots))
|
||||
app.controller.save_context_preset(preset)
|
||||
app.ui_new_context_preset_name = ""
|
||||
imgui.same_line()
|
||||
if imgui.button("Delete##ctx"):
|
||||
if getattr(app, "ui_active_context_preset", ""):
|
||||
app.delete_context_preset(app.ui_active_context_preset)
|
||||
app.ui_active_context_preset = ""
|
||||
else:
|
||||
imgui.text_disabled("No active preset")
|
||||
|
||||
imgui.table_next_row()
|
||||
imgui.table_next_column()
|
||||
imgui.set_next_item_width(-1)
|
||||
changed, new_name = imgui.input_text("##new_preset", getattr(app, "ui_new_context_preset_name", ""))
|
||||
if changed: app.ui_new_context_preset_name = new_name
|
||||
|
||||
imgui.table_next_column()
|
||||
if imgui.button("Save As##ctx", imgui.ImVec2(-1, 0)) or getattr(app, "_pending_save_ctx_click", False):
|
||||
app._pending_save_ctx_click = False
|
||||
name = getattr(app, "ui_new_context_preset_name", "").strip()
|
||||
if name:
|
||||
missing = []
|
||||
root = app.controller.active_project_root
|
||||
for f in app.context_files:
|
||||
path = f.path if hasattr(f, "path") else str(f)
|
||||
if not os.path.isabs(path):
|
||||
full_path = os.path.join(root, path)
|
||||
else:
|
||||
full_path = path
|
||||
if not os.path.exists(full_path):
|
||||
missing.append(path)
|
||||
|
||||
if missing:
|
||||
app.missing_context_files = missing
|
||||
app.show_missing_files_modal = True
|
||||
app.target_context_preset_name = name
|
||||
else:
|
||||
preset_files = []
|
||||
for f in app.context_files:
|
||||
import copy
|
||||
from src import models
|
||||
p = f.path if hasattr(f, 'path') else str(f)
|
||||
vm = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
slc = copy.deepcopy(f.custom_slices) if hasattr(f, 'custom_slices') else []
|
||||
msk = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {}
|
||||
sig = f.ast_signatures if hasattr(f, 'ast_signatures') else False
|
||||
dfn = f.ast_definitions if hasattr(f, 'ast_definitions') else False
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm, custom_slices=slc, ast_mask=msk, ast_signatures=sig, ast_definitions=dfn))
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(app.screenshots))
|
||||
app.controller.save_context_preset(preset)
|
||||
app.ui_new_context_preset_name = ""
|
||||
|
||||
if active:
|
||||
imgui.table_next_row()
|
||||
imgui.table_next_column()
|
||||
imgui.table_next_column()
|
||||
if imgui.button("Delete Active", imgui.ImVec2(-1, 0)):
|
||||
app.delete_context_preset(active)
|
||||
app.ui_active_context_preset = ""
|
||||
|
||||
def render_snapshot_tab(app: App) -> None:
|
||||
if imgui.begin_tab_bar("snapshot_tabs"):
|
||||
@@ -5380,9 +5426,15 @@ def render_context_modals(app: App) -> None:
|
||||
name = app.target_context_preset_name
|
||||
preset_files = []
|
||||
for f in app.context_files:
|
||||
import copy
|
||||
from src import models
|
||||
p = f.path if hasattr(f, 'path') else str(f)
|
||||
vm = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm))
|
||||
slc = copy.deepcopy(f.custom_slices) if hasattr(f, 'custom_slices') else []
|
||||
msk = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {}
|
||||
sig = f.ast_signatures if hasattr(f, 'ast_signatures') else False
|
||||
dfn = f.ast_definitions if hasattr(f, 'ast_definitions') else False
|
||||
preset_files.append(models.ContextFileEntry(path=p, view_mode=vm, custom_slices=slc, ast_mask=msk, ast_signatures=sig, ast_definitions=dfn))
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(app.screenshots))
|
||||
app.controller.save_context_preset(preset)
|
||||
app.ui_new_context_preset_name = ""
|
||||
|
||||
+6
-13
@@ -22,7 +22,6 @@ For each file, extracts structural information:
|
||||
Returns a compact markdown string per file, suitable for use as a low-token
|
||||
context block that replaces full file contents in the initial <context> send.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import re
|
||||
from pathlib import Path
|
||||
@@ -42,7 +41,6 @@ def _summarise_python(path: Path, content: str) -> str:
|
||||
except SyntaxError as e:
|
||||
parts.append(f"_Parse error: {e}_")
|
||||
return "\n".join(parts)
|
||||
# Imports
|
||||
imports = []
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
@@ -54,7 +52,6 @@ def _summarise_python(path: Path, content: str) -> str:
|
||||
if imports:
|
||||
unique_imports = sorted(set(imports))
|
||||
parts.append(f"imports: {', '.join(unique_imports)}")
|
||||
# Top-level constants (ALL_CAPS assignments)
|
||||
constants = []
|
||||
for node in ast.iter_child_nodes(tree):
|
||||
if isinstance(node, ast.Assign):
|
||||
@@ -66,7 +63,6 @@ def _summarise_python(path: Path, content: str) -> str:
|
||||
constants.append(node.target.id)
|
||||
if constants:
|
||||
parts.append(f"constants: {', '.join(constants)}")
|
||||
# Classes + their methods
|
||||
for node in ast.iter_child_nodes(tree):
|
||||
if isinstance(node, ast.ClassDef):
|
||||
methods = [
|
||||
@@ -77,7 +73,6 @@ def _summarise_python(path: Path, content: str) -> str:
|
||||
parts.append(f"class {node.name}: {', '.join(methods)}")
|
||||
else:
|
||||
parts.append(f"class {node.name}")
|
||||
# Top-level functions
|
||||
top_fns = [
|
||||
node.name for node in ast.iter_child_nodes(tree)
|
||||
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
||||
@@ -90,7 +85,6 @@ def _summarise_toml(path: Path, content: str) -> str:
|
||||
lines = content.splitlines()
|
||||
line_count = len(lines)
|
||||
parts = [f"**TOML** — {line_count} lines"]
|
||||
# Extract top-level table headers [key] and [[key]]
|
||||
table_pat = re.compile(r"^\s*\[{1,2}([^\[\]]+)\]{1,2}")
|
||||
tables = []
|
||||
for line in lines:
|
||||
@@ -99,7 +93,6 @@ def _summarise_toml(path: Path, content: str) -> str:
|
||||
tables.append(m.group(1).strip())
|
||||
if tables:
|
||||
parts.append(f"tables: {', '.join(tables)}")
|
||||
# Top-level key = value (not inside a [table])
|
||||
kv_pat = re.compile(r"^([a-zA-Z_][a-zA-Z0-9_]*)\s*=")
|
||||
in_table = False
|
||||
top_keys = []
|
||||
@@ -140,7 +133,6 @@ def _summarise_generic(path: Path, content: str) -> str:
|
||||
if preview:
|
||||
parts.append("preview:\n```\n" + "\n".join(preview) + "\n```")
|
||||
return "\n".join(parts)
|
||||
# ------------------------------------------------------------------ dispatch
|
||||
|
||||
_SUMMARISERS: dict[str, Callable[[Path, str], str]] = {
|
||||
".py": _summarise_python,
|
||||
@@ -148,6 +140,10 @@ _SUMMARISERS: dict[str, Callable[[Path, str], str]] = {
|
||||
".md": _summarise_markdown,
|
||||
".ini": _summarise_generic,
|
||||
".txt": _summarise_generic,
|
||||
".c": _summarise_generic,
|
||||
".h": _summarise_generic,
|
||||
".cpp": _summarise_generic,
|
||||
".hpp": _summarise_generic,
|
||||
".ps1": _summarise_generic,
|
||||
}
|
||||
|
||||
@@ -163,19 +159,17 @@ def summarise_file(path: Path, content: str) -> str:
|
||||
cached = _summary_cache.get_summary(str(path), content_hash)
|
||||
if cached:
|
||||
return cached
|
||||
|
||||
suffix = path.suffix.lower() if hasattr(path, "suffix") else ""
|
||||
fn = _SUMMARISERS.get(suffix, _summarise_generic)
|
||||
try:
|
||||
heuristic_outline = fn(path, content)
|
||||
|
||||
# Smart AI Summarization
|
||||
is_code = suffix in [".py", ".ps1", ".js", ".ts", ".cpp", ".c", ".h", ".cs", ".go", ".rs", ".lua"]
|
||||
try:
|
||||
from src import ai_client
|
||||
smart_summary = ai_client.run_subagent_summarization(
|
||||
file_path=str(path),
|
||||
content=content[:10000], # Cap content to 10k chars for summarization
|
||||
content=content[:10000],
|
||||
is_code=is_code,
|
||||
outline=heuristic_outline
|
||||
)
|
||||
@@ -184,8 +178,7 @@ def summarise_file(path: Path, content: str) -> str:
|
||||
else:
|
||||
summary = heuristic_outline
|
||||
except Exception:
|
||||
summary = heuristic_outline # Fallback
|
||||
|
||||
summary = heuristic_outline
|
||||
_summary_cache.set_summary(str(path), content_hash, summary)
|
||||
return summary
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user