WIP: Phase 6 review

This commit is contained in:
2026-05-10 15:14:54 -04:00
parent 7974f661b3
commit 2a71aff18c
10 changed files with 290 additions and 66 deletions
+32
View File
@@ -246,5 +246,37 @@
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1072\\test_auto_aggregate_skip0\\file1.txt": { "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1072\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" "summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
},
"C:\\projects\\manual_slop\\tests\\test_minimax_provider.py": {
"hash": "107ae6075993f4bac175c08dec0627b1c4f3ee187d516acdf48d7ee179cbd5e3",
"summary": "This file contains unit tests for the `minimax` AI provider integration within the `ai_client` module. It verifies the correct selection of models, default model behavior, listing of available models, and history bleed statistics.\n\n* Tests model selection and default model assignment.\n* Verifies the listing of supported Minimax models.\n* Checks history bleed statistics and provider integration.\n* Confirms Minimax provider is recognized in system lists.\n\n**Outline:**\n**Python** \u2014 41 lines\nimports: src, unittest\nfunctions: test_minimax_model_selection, test_minimax_default_model, test_minimax_list_models, test_minimax_history_bleed_stats, test_minimax_in_providers_list, test_minimax_in_app_controller_providers, test_minimax_credentials_template"
},
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1079\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose is to present this specific content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
},
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1087\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document contains a single line of text, \"content1\". Its purpose is to present this specific content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
},
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1095\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
},
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1096\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
},
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1097\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
},
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1099\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document contains a single line of text, \"content1\". Its purpose and key takeaways are limited to this singular piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
},
"C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1100\\test_auto_aggregate_skip0\\file1.txt": {
"hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa",
"summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```"
} }
} }
+1 -1
View File
@@ -34,7 +34,7 @@ This file tracks all major tracks for the project. Each track has its own detail
*Link: [./tracks/interactive_ast_tree_masking_20260510/](./tracks/interactive_ast_tree_masking_20260510/)* *Link: [./tracks/interactive_ast_tree_masking_20260510/](./tracks/interactive_ast_tree_masking_20260510/)*
*Goal: Inspect C/C++ ASTs in the GUI and mask individual classes/functions as Def, Sig, or Hide.* *Goal: Inspect C/C++ ASTs in the GUI and mask individual classes/functions as Def, Sig, or Hide.*
7. [ ] **Track: Phase 6 Review and Regression Verification** 7. [~] **Track: Phase 6 Review and Regression Verification**
*Link: [./tracks/phase6_review_20260510/](./tracks/phase6_review_20260510/)* *Link: [./tracks/phase6_review_20260510/](./tracks/phase6_review_20260510/)*
*Goal: Review Phase 6 implementation, perform full-suite batch regression testing, and expand test coverage for new context curation features.* *Goal: Review Phase 6 implementation, perform full-suite batch regression testing, and expand test coverage for new context curation features.*
@@ -1,13 +1,13 @@
# Implementation Plan: Phase 6 Review and Regression Verification # Implementation Plan: Phase 6 Review and Regression Verification
## Phase 1: Regression Testing (Batch Execution) ## Phase 1: Regression Testing (Batch Execution)
- [ ] Task: Execute the full test suite in batches of 4 test files. - [x] Task: Execute the full test suite in batches of 4 test files.
- [ ] Task: Document any failures and categorize by root cause. - [x] Task: Document any failures and categorize by root cause. (Minimax regression, Snapshot crash).
- [ ] Task: Fix regressions introduced by Phase 6 (if any). - [x] Task: Fix regressions introduced by Phase 6 (if any).
- [ ] Task: Conductor - User Manual Verification 'Phase 1' (Protocol in workflow.md) - [x] Task: Conductor - User Manual Verification 'Phase 1' (Protocol in workflow.md)
## Phase 2: Feature Coverage (Core Logic) ## Phase 2: Feature Coverage (Core Logic)
- [ ] Task: Write unit tests for C++ AST Tree Masking logic using samples from `gencpp/base/components`. - [~] Task: Write unit tests for C++ AST Tree Masking logic using samples from `gencpp/base/components`.
- [ ] Task: Write unit tests for 'Fuzzy Anchor' resolution across simulated file edits. - [ ] Task: Write unit tests for 'Fuzzy Anchor' resolution across simulated file edits.
- [ ] Task: Write unit tests for `HistoryManager` context snapshot roundtrips. - [ ] Task: Write unit tests for `HistoryManager` context snapshot roundtrips.
- [ ] Task: Conductor - User Manual Verification 'Phase 2' (Protocol in workflow.md) - [ ] Task: Conductor - User Manual Verification 'Phase 2' (Protocol in workflow.md)
+13
View File
@@ -0,0 +1,13 @@
from src.file_cache import ASTParser
from pathlib import Path
parser = ASTParser('cpp')
code = Path('tests/assets/gencpp_samples/ast.hpp').read_text()
tree = parser.get_cached_tree(None, code)
code_bytes = code.encode('utf8')
def find_node(node):
start = node.start_point.row + 1
if start == 296:
print(f'LINE 296 NODE: {node.type}, NAME: {parser._get_name(node, code_bytes)}, TEXT: {code_bytes[node.start_byte:node.end_byte].decode("utf8")[:50].strip()}...')
for child in node.children:
find_node(child)
find_node(tree.root_node)
+44
View File
@@ -0,0 +1,44 @@
# manual_slop.toml - GenCpp Project Configuration
[project]
name = "gencpp"
git_dir = ".git"
main_context = "GenCpp: A C++ library for agenic code generation and analysis."
word_wrap = true
[paths]
logs_dir = ".manual_slop/logs"
scripts_dir = ".manual_slop/scripts"
[files]
base_dir = "."
paths = []
[screenshots]
base_dir = ".manual_slop/screenshots"
paths = []
[output]
output_dir = ".manual_slop/md_gen"
[conductor]
dir = ".manual_slop/conductor"
[agent]
roles = ["User", "AI", "Vendor API", "System", "Reasoning", "Context"]
[agent.tools]
read_file = true
list_directory = true
search_files = true
grep_search = true
run_shell_command = true
# ... other tools ...
[discussion]
active = "main"
auto_add = true
[discussion.discussions.main]
history = []
context_snapshot = []
+23
View File
@@ -120,3 +120,26 @@ ERROR: file not found: C:\Users\Ed\AppData\Local\Temp\tmpbtllj21_\final_test_2.p
What makes RAG great? What makes RAG great?
------------------ ------------------
--- MOCK INVOKED ---
ARGS: ['tests/mock_gemini_cli.py']
PROMPT:
PATH: Epic Initialization — please produce tracks
------------------
--- MOCK INVOKED ---
ARGS: ['tests/mock_gemini_cli.py']
PROMPT:
Please generate the implementation tickets for this track.
------------------
--- MOCK INVOKED ---
ARGS: ['tests/mock_gemini_cli.py']
PROMPT:
Please read test.txt
You are assigned to Ticket T1.
Task Description: do something
------------------
--- MOCK INVOKED ---
ARGS: ['tests/mock_gemini_cli.py']
PROMPT:
role: tool
Here are the results: {"content": "done"}
------------------
+1 -1
View File
@@ -9,5 +9,5 @@ active = "main"
[discussions.main] [discussions.main]
git_commit = "" git_commit = ""
last_updated = "2026-05-10T12:15:18" last_updated = "2026-05-10T14:38:49"
history = [] history = []
+127 -59
View File
@@ -106,18 +106,29 @@ class ASTParser:
if name_node: if name_node:
return code_bytes[name_node.start_byte:name_node.end_byte].decode("utf8", errors="replace") return code_bytes[name_node.start_byte:name_node.end_byte].decode("utf8", errors="replace")
if node.type in ("function_definition", "field_declaration"): if node.type in ("function_definition", "field_declaration", "declaration"):
def find_id(n: tree_sitter.Node) -> str: def find_id(n: tree_sitter.Node) -> str:
if n.type in ("identifier", "field_identifier", "qualified_identifier", "destructor_name"): # In C++, prefer function_declarator or operator_name
if self.language_name in ("cpp", "c"):
# Look for declarator field or child
decl = n.child_by_field_name("declarator")
if decl:
# If it's a function_declarator, the name is inside its 'declarator' or 'name'
if decl.type == "function_declarator":
nested_decl = decl.child_by_field_name("declarator")
if nested_decl:
return find_id(nested_decl)
elif decl.type == "pointer_declarator":
return find_id(decl)
elif decl.type in ("identifier", "field_identifier", "qualified_identifier", "operator_name", "destructor_name"):
return code_bytes[decl.start_byte:decl.end_byte].decode("utf8", errors="replace")
if n.type in ("identifier", "field_identifier", "qualified_identifier", "operator_name", "destructor_name"):
return code_bytes[n.start_byte:n.end_byte].decode("utf8", errors="replace") return code_bytes[n.start_byte:n.end_byte].decode("utf8", errors="replace")
# Try field name 'declarator' first
d = n.child_by_field_name("declarator") # Fallback to children, but avoid bodies and types
if d:
res = find_id(d)
if res: return res
# Fallback to all children
for child in n.children: for child in n.children:
if child.type == "compound_statement": continue # Don't look in body if child.type in ("compound_statement", "field_declaration_list", "class_body", "declaration_list", "enum_body", "type_identifier", "primitive_type"): continue
res = find_id(child) res = find_id(child)
if res: return res if res: return res
return "" return ""
@@ -125,12 +136,12 @@ class ASTParser:
if node.type == "template_declaration": if node.type == "template_declaration":
for child in node.children: for child in node.children:
if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "field_declaration"): if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "field_declaration", "declaration"):
return self._get_name(child, code_bytes) return self._get_name(child, code_bytes)
if node.type in ("struct_specifier", "class_specifier", "class_definition", "enum_specifier", "enum_definition", "namespace_definition"): if node.type in ("struct_specifier", "class_specifier", "class_definition", "enum_specifier", "enum_definition", "namespace_definition"):
for child in node.children: for child in node.children:
if child.type in ("type_identifier", "identifier", "namespace_identifier"): if child.type in ("type_identifier", "identifier", "namespace_identifier", "qualified_identifier"):
return code_bytes[child.start_byte:child.end_byte].decode("utf8", errors="replace") return code_bytes[child.start_byte:child.end_byte].decode("utf8", errors="replace")
return "" return ""
@@ -442,12 +453,11 @@ class ASTParser:
parts = re.split(r'::|\.', name) parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if not target_parts: if not target_parts:
return None return None
target = target_parts[0] target = target_parts[0]
best_match = None
for child in node.children: for child in node.children:
# If it's a field_declaration, it might wrap a class/struct/enum definition # If it's a field_declaration, it might wrap a class/struct/enum definition
check_node = child check_node = child
@@ -457,12 +467,16 @@ class ASTParser:
check_node = sub check_node = sub
break break
is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration") is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration", "declaration")
if is_interesting: if is_interesting:
node_name = self._get_name(check_node, code_bytes) node_name = self._get_name(check_node, code_bytes)
if node_name == target: if node_name == target:
if len(target_parts) == 1: if len(target_parts) == 1:
return check_node if child.type != "field_declaration" else child match = check_node if child.type != "field_declaration" else child
if match.child_by_field_name("body"):
return match
if not best_match:
best_match = match
next_parts = target_parts[1:] next_parts = target_parts[1:]
else: else:
next_parts = target_parts next_parts = target_parts
@@ -475,28 +489,44 @@ class ASTParser:
break break
if body: if body:
found = walk(body, next_parts) found = walk(body, next_parts)
if found: return found if found:
if found.child_by_field_name("body"): return found
if not best_match: best_match = found
for sub in check_node.children: for sub in check_node.children:
if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"): if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"):
found = walk(sub, next_parts) found = walk(sub, next_parts)
if found: return found if found:
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): if found.child_by_field_name("body"): return found
if not best_match: best_match = found
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body", "preproc_if", "preproc_ifdef", "preproc_else", "preproc_elif"):
found = walk(child, target_parts) found = walk(child, target_parts)
if found: return found if found:
return None if found.child_by_field_name("body"): return found
if not best_match: best_match = found
return best_match
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration"): best = None
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "declaration"):
if self._get_name(node, code_bytes) == target: if self._get_name(node, code_bytes) == target:
return node if node.child_by_field_name("body"):
return node
best = node
for child in node.children: for child in node.children:
res = deep_search(child, target) res = deep_search(child, target)
if res: return res if res:
return None if res.child_by_field_name("body"):
return res
if not best:
best = res
return best
found_node = walk(tree.root_node, parts) found_node = walk(tree.root_node, parts)
if not found_node: if not found_node or not found_node.child_by_field_name("body"):
found_node = deep_search(tree.root_node, name) alt = deep_search(tree.root_node, name)
if alt:
if not found_node or alt.child_by_field_name("body"):
found_node = alt
if found_node: if found_node:
return code_bytes[found_node.start_byte:found_node.end_byte].decode("utf8", errors="replace") return code_bytes[found_node.start_byte:found_node.end_byte].decode("utf8", errors="replace")
@@ -515,12 +545,11 @@ class ASTParser:
parts = re.split(r'::|\.', name) parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if not target_parts: if not target_parts:
return None return None
target = target_parts[0] target = target_parts[0]
best_match = None
for child in node.children: for child in node.children:
# If it's a field_declaration, it might wrap a class/struct/enum definition # If it's a field_declaration, it might wrap a class/struct/enum definition
check_node = child check_node = child
@@ -530,12 +559,16 @@ class ASTParser:
check_node = sub check_node = sub
break break
is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration") is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration", "declaration")
if is_interesting: if is_interesting:
node_name = self._get_name(check_node, code_bytes) node_name = self._get_name(check_node, code_bytes)
if node_name == target: if node_name == target:
if len(target_parts) == 1: if len(target_parts) == 1:
return check_node if child.type != "field_declaration" else child match = check_node if child.type != "field_declaration" else child
if match.child_by_field_name("body"):
return match
if not best_match:
best_match = match
next_parts = target_parts[1:] next_parts = target_parts[1:]
else: else:
next_parts = target_parts next_parts = target_parts
@@ -548,28 +581,44 @@ class ASTParser:
break break
if body: if body:
found = walk(body, next_parts) found = walk(body, next_parts)
if found: return found if found:
if found.child_by_field_name("body"): return found
if not best_match: best_match = found
for sub in check_node.children: for sub in check_node.children:
if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"): if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"):
found = walk(sub, next_parts) found = walk(sub, next_parts)
if found: return found if found:
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): if found.child_by_field_name("body"): return found
if not best_match: best_match = found
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body", "preproc_if", "preproc_ifdef", "preproc_else", "preproc_elif"):
found = walk(child, target_parts) found = walk(child, target_parts)
if found: return found if found:
return None if found.child_by_field_name("body"): return found
if not best_match: best_match = found
return best_match
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
if node.type in ("function_definition", "template_declaration"): best = None
if node.type in ("function_definition", "template_declaration", "declaration"):
if self._get_name(node, code_bytes) == target: if self._get_name(node, code_bytes) == target:
return node if node.child_by_field_name("body"):
return node
best = node
for child in node.children: for child in node.children:
res = deep_search(child, target) res = deep_search(child, target)
if res: return res if res:
return None if res.child_by_field_name("body"):
return res
if not best:
best = res
return best
found_node = walk(tree.root_node, parts) found_node = walk(tree.root_node, parts)
if not found_node: if not found_node or not found_node.child_by_field_name("body"):
found_node = deep_search(tree.root_node, name) alt = deep_search(tree.root_node, name)
if alt:
if not found_node or alt.child_by_field_name("body"):
found_node = alt
if found_node: if found_node:
target_node = found_node target_node = found_node
@@ -639,12 +688,11 @@ class ASTParser:
parts = re.split(r'::|\.', name) parts = re.split(r'::|\.', name)
def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]:
"""
[C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python]
"""
if not target_parts: if not target_parts:
return None return None
target = target_parts[0] target = target_parts[0]
best_match = None
for child in node.children: for child in node.children:
# If it's a field_declaration, it might wrap a class/struct/enum definition # If it's a field_declaration, it might wrap a class/struct/enum definition
check_node = child check_node = child
@@ -654,12 +702,16 @@ class ASTParser:
check_node = sub check_node = sub
break break
is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration") is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration", "declaration")
if is_interesting: if is_interesting:
node_name = self._get_name(check_node, code_bytes) node_name = self._get_name(check_node, code_bytes)
if node_name == target: if node_name == target:
if len(target_parts) == 1: if len(target_parts) == 1:
return check_node if child.type != "field_declaration" else child match = check_node if child.type != "field_declaration" else child
if match.child_by_field_name("body"):
return match
if not best_match:
best_match = match
next_parts = target_parts[1:] next_parts = target_parts[1:]
else: else:
next_parts = target_parts next_parts = target_parts
@@ -672,28 +724,44 @@ class ASTParser:
break break
if body: if body:
found = walk(body, next_parts) found = walk(body, next_parts)
if found: return found if found:
if found.child_by_field_name("body"): return found
if not best_match: best_match = found
for sub in check_node.children: for sub in check_node.children:
if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"): if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"):
found = walk(sub, next_parts) found = walk(sub, next_parts)
if found: return found if found:
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): if found.child_by_field_name("body"): return found
if not best_match: best_match = found
elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body", "preproc_if", "preproc_ifdef", "preproc_else", "preproc_elif"):
found = walk(child, target_parts) found = walk(child, target_parts)
if found: return found if found:
return None if found.child_by_field_name("body"): return found
if not best_match: best_match = found
return best_match
def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]:
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration"): best = None
if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "declaration"):
if self._get_name(node, code_bytes) == target: if self._get_name(node, code_bytes) == target:
return node if node.child_by_field_name("body"):
return node
best = node
for child in node.children: for child in node.children:
res = deep_search(child, target) res = deep_search(child, target)
if res: return res if res:
return None if res.child_by_field_name("body"):
return res
if not best:
best = res
return best
found_node = walk(tree.root_node, parts) found_node = walk(tree.root_node, parts)
if not found_node: if not found_node or not found_node.child_by_field_name("body"):
found_node = deep_search(tree.root_node, name) alt = deep_search(tree.root_node, name)
if alt:
if not found_node or alt.child_by_field_name("body"):
found_node = alt
if found_node: if found_node:
code_bytearray = bytearray(code_bytes) code_bytearray = bytearray(code_bytes)
BIN
View File
Binary file not shown.
+44
View File
@@ -0,0 +1,44 @@
import sys
import os
from pathlib import Path
sys.path.append(str(Path(__file__).parent.parent))
from src.mcp_client import ts_cpp_get_definition, ts_cpp_get_signature, ts_cpp_get_code_outline
from src.aggregate import build_tier3_context
def test_ast_masking_gencpp_samples():
ast_hpp = "tests/assets/gencpp_samples/ast.hpp"
# 1. Verify we can get a complex definition despite forward declarations
# Currently it might return the forward declaration, let's check
defn = ts_cpp_get_definition(ast_hpp, "AST")
print(f"--- AST Definition (len {len(defn)}) ---")
print(defn[:100] + "...")
# 2. Test Masking logic in aggregation
item = {
"path": Path(ast_hpp),
"entry": ast_hpp,
"content": Path(ast_hpp).read_text(encoding="utf-8"),
"ast_mask": {"AST": "def", "Code::append": "sig"},
"auto_aggregate": True,
"force_full": False,
"tier": None
}
res = build_tier3_context([item], Path("."), [], [], [])
print("--- MASKED AGGREGATION RESULT ---")
# Search for Code::append signature in result
if "forceinline void append(Code other)" in res:
print("SUCCESS: Code::append signature found!")
else:
print("FAILURE: Code::append signature NOT found!")
# Print a bit of what IS there
print(res[2800:3500])
assert "(Masked)" in res
assert "struct AST" in res
assert "forceinline void append(Code other)" in res
if __name__ == "__main__":
test_ast_masking_gencpp_samples()