feat(aggregate): support dictionary-based file entries with optional tiers
This commit is contained in:
29
aggregate.py
29
aggregate.py
@@ -64,9 +64,14 @@ def build_discussion_section(history: list[str]) -> str:
|
||||
sections.append(f"### Discussion Excerpt {i}\n\n{paste.strip()}")
|
||||
return "\n\n---\n\n".join(sections)
|
||||
|
||||
def build_files_section(base_dir: Path, files: list[str]) -> str:
|
||||
def build_files_section(base_dir: Path, files: list[str | dict]) -> str:
|
||||
sections = []
|
||||
for entry in files:
|
||||
for entry_raw in files:
|
||||
if isinstance(entry_raw, dict):
|
||||
entry = entry_raw.get("path")
|
||||
else:
|
||||
entry = entry_raw
|
||||
|
||||
paths = resolve_paths(base_dir, entry)
|
||||
if not paths:
|
||||
sections.append(f"### `{entry}`\n\n```text\nERROR: no files matched: {entry}\n```")
|
||||
@@ -100,7 +105,7 @@ def build_screenshots_section(base_dir: Path, screenshots: list[str]) -> str:
|
||||
return "\n\n---\n\n".join(sections)
|
||||
|
||||
|
||||
def build_file_items(base_dir: Path, files: list[str]) -> list[dict]:
|
||||
def build_file_items(base_dir: Path, files: list[str | dict]) -> list[dict]:
|
||||
"""
|
||||
Return a list of dicts describing each file, for use by ai_client when it
|
||||
wants to upload individual files rather than inline everything as markdown.
|
||||
@@ -111,12 +116,20 @@ def build_file_items(base_dir: Path, files: list[str]) -> list[dict]:
|
||||
content : str (file text, or error string)
|
||||
error : bool
|
||||
mtime : float (last modification time, for skip-if-unchanged optimization)
|
||||
tier : int | None (optional tier for context management)
|
||||
"""
|
||||
items = []
|
||||
for entry in files:
|
||||
for entry_raw in files:
|
||||
if isinstance(entry_raw, dict):
|
||||
entry = entry_raw.get("path")
|
||||
tier = entry_raw.get("tier")
|
||||
else:
|
||||
entry = entry_raw
|
||||
tier = None
|
||||
|
||||
paths = resolve_paths(base_dir, entry)
|
||||
if not paths:
|
||||
items.append({"path": None, "entry": entry, "content": f"ERROR: no files matched: {entry}", "error": True, "mtime": 0.0})
|
||||
items.append({"path": None, "entry": entry, "content": f"ERROR: no files matched: {entry}", "error": True, "mtime": 0.0, "tier": tier})
|
||||
continue
|
||||
for path in paths:
|
||||
try:
|
||||
@@ -131,10 +144,10 @@ def build_file_items(base_dir: Path, files: list[str]) -> list[dict]:
|
||||
content = f"ERROR: {e}"
|
||||
mtime = 0.0
|
||||
error = True
|
||||
items.append({"path": path, "entry": entry, "content": content, "error": error, "mtime": mtime})
|
||||
items.append({"path": path, "entry": entry, "content": content, "error": error, "mtime": mtime, "tier": tier})
|
||||
return items
|
||||
|
||||
def build_summary_section(base_dir: Path, files: list[str]) -> str:
|
||||
def build_summary_section(base_dir: Path, files: list[str | dict]) -> str:
|
||||
"""
|
||||
Build a compact summary section using summarize.py — one short block per file.
|
||||
Used as the initial <context> block instead of full file contents.
|
||||
@@ -279,7 +292,7 @@ def build_tier3_context(file_items: list[dict], screenshot_base_dir: Path, scree
|
||||
return "\n\n---\n\n".join(parts)
|
||||
|
||||
|
||||
def build_markdown(base_dir: Path, files: list[str], screenshot_base_dir: Path, screenshots: list[str], history: list[str], summary_only: bool = False) -> str:
|
||||
def build_markdown(base_dir: Path, files: list[str | dict], screenshot_base_dir: Path, screenshots: list[str], history: list[str], summary_only: bool = False) -> str:
|
||||
parts = []
|
||||
# STATIC PREFIX: Files and Screenshots must go first to maximize Cache Hits
|
||||
if files:
|
||||
|
||||
@@ -66,3 +66,44 @@ def test_build_tier3_context_exists():
|
||||
# Let's check for the header
|
||||
assert "other.py" in result
|
||||
assert "AST Skeleton" in result
|
||||
|
||||
def test_build_file_items_with_tiers(tmp_path):
|
||||
from aggregate import build_file_items
|
||||
|
||||
# Create some dummy files
|
||||
file1 = tmp_path / "file1.txt"
|
||||
file1.write_text("content1")
|
||||
file2 = tmp_path / "file2.txt"
|
||||
file2.write_text("content2")
|
||||
|
||||
files_config = [
|
||||
"file1.txt",
|
||||
{"path": "file2.txt", "tier": 3}
|
||||
]
|
||||
|
||||
items = build_file_items(tmp_path, files_config)
|
||||
|
||||
assert len(items) == 2
|
||||
|
||||
item1 = next(i for i in items if i["entry"] == "file1.txt")
|
||||
assert item1["content"] == "content1"
|
||||
assert "tier" in item1
|
||||
assert item1["tier"] is None
|
||||
|
||||
item2 = next(i for i in items if i["entry"] == "file2.txt")
|
||||
assert item2["content"] == "content2"
|
||||
assert item2["tier"] == 3
|
||||
|
||||
def test_build_files_section_with_dicts(tmp_path):
|
||||
from aggregate import build_files_section
|
||||
|
||||
file1 = tmp_path / "file1.txt"
|
||||
file1.write_text("content1")
|
||||
|
||||
files_config = [
|
||||
{"path": str(file1)}
|
||||
]
|
||||
|
||||
result = build_files_section(tmp_path, files_config)
|
||||
assert "content1" in result
|
||||
assert "file1.txt" in result
|
||||
|
||||
Reference in New Issue
Block a user