WIP: Phase 6 review
This commit is contained in:
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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 = []
|
||||||
@@ -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"}
|
||||||
|
------------------
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
Binary file not shown.
@@ -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()
|
||||||
Reference in New Issue
Block a user