From de816867ee25f983f4d54811df0e9112c23839b6 Mon Sep 17 00:00:00 2001 From: AI Christianson Date: Wed, 11 Dec 2024 17:07:14 -0500 Subject: [PATCH] tech debt tool --- ra_aid/__main__.py | 51 +++++++++++++++++++++++-- ra_aid/prompts.py | 30 ++++++++++++++- ra_aid/tools/__init__.py | 4 +- ra_aid/tools/note_tech_debt.py | 68 ++++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 ra_aid/tools/note_tech_debt.py diff --git a/ra_aid/__main__.py b/ra_aid/__main__.py index e735564..51f8cb9 100644 --- a/ra_aid/__main__.py +++ b/ra_aid/__main__.py @@ -1,5 +1,6 @@ import sqlite3 import argparse +import glob import os import sys import shutil @@ -11,7 +12,7 @@ from ra_aid.tools import ( ask_expert, run_shell_command, run_programming_task, emit_research_notes, emit_plan, emit_related_files, emit_task, emit_expert_context, get_memory_value, emit_key_facts, delete_key_facts, - emit_key_snippets, delete_key_snippets, + emit_key_snippets, delete_key_snippets, note_tech_debt, request_implementation, read_file_tool, emit_research_subtask, fuzzy_find_project_files, ripgrep_search, list_directory_tree ) @@ -63,9 +64,9 @@ planning_memory = MemorySaver() implementation_memory = MemorySaver() # Define tool sets for each stage -research_tools = [list_directory_tree, emit_research_subtask, run_shell_command, emit_expert_context, ask_expert, emit_research_notes, emit_related_files, emit_key_facts, delete_key_facts, emit_key_snippets, delete_key_snippets, request_implementation, read_file_tool, fuzzy_find_project_files, ripgrep_search] -planning_tools = [list_directory_tree, emit_expert_context, ask_expert, emit_plan, emit_task, emit_related_files, emit_key_facts, delete_key_facts, emit_key_snippets, delete_key_snippets, read_file_tool, fuzzy_find_project_files, ripgrep_search] -implementation_tools = [list_directory_tree, run_shell_command, emit_expert_context, ask_expert, run_programming_task, emit_related_files, emit_key_facts, delete_key_facts, emit_key_snippets, delete_key_snippets, read_file_tool, fuzzy_find_project_files, ripgrep_search] +research_tools = [list_directory_tree, emit_research_subtask, run_shell_command, emit_expert_context, ask_expert, emit_research_notes, emit_related_files, emit_key_facts, delete_key_facts, emit_key_snippets, delete_key_snippets, note_tech_debt, request_implementation, read_file_tool, fuzzy_find_project_files, ripgrep_search] +planning_tools = [list_directory_tree, emit_expert_context, ask_expert, emit_plan, emit_task, emit_related_files, emit_key_facts, delete_key_facts, emit_key_snippets, delete_key_snippets, note_tech_debt, read_file_tool, fuzzy_find_project_files, ripgrep_search] +implementation_tools = [list_directory_tree, run_shell_command, emit_expert_context, ask_expert, run_programming_task, emit_related_files, emit_key_facts, delete_key_facts, emit_key_snippets, delete_key_snippets, note_tech_debt, read_file_tool, fuzzy_find_project_files, ripgrep_search] # Create stage-specific agents with individual memory objects research_agent = create_react_agent(model, research_tools, checkpointer=research_memory) @@ -236,6 +237,47 @@ def validate_environment(): print(f"- {error}", file=sys.stderr) sys.exit(1) +def review_tech_debt(model) -> None: + """Review any technical debt notes collected during execution.""" + tech_debt_dir = '.ra-aid/tech-debt' + tech_debt_files = glob.glob(os.path.join(tech_debt_dir, '*.md')) + + if not tech_debt_files: + return + + print_stage_header("Technical Debt Review") + + # Read the contents of all tech debt notes + tech_debt_contents = [] + for file_path in tech_debt_files: + with open(file_path, 'r') as file: + content = file.read() + tech_debt_contents.append(content) + + # Analyze the tech debt notes + prompt = f"""Review the following technical debt notes collected during program execution: + +{chr(10).join(tech_debt_contents)} + +Please provide a brief, focused analysis: +1. Group similar issues if any +2. Highlight high-impact items +3. Suggest a rough priority order +Keep the response concise and actionable. +""" + # Stream the analysis + while True: + try: + for chunk in model.stream( + {"messages": [HumanMessage(content=prompt)]}, + {"configurable": {"thread_id": "tech-debt"}, "recursion_limit": 100} + ): + print_agent_output(chunk) + break + except ChatAnthropic.InternalServerError as e: + print(f"Encountered Anthropic Internal Server Error: {e}. Retrying...") + continue + def main(): """Main entry point for the ra-aid command line tool.""" try: @@ -316,6 +358,7 @@ Be very thorough in your research and emit lots of snippets, key facts. If you t related_files ) except TaskCompletedException: + review_tech_debt(model) sys.exit(0) if __name__ == "__main__": diff --git a/ra_aid/prompts.py b/ra_aid/prompts.py index 3dd2680..940bb67 100644 --- a/ra_aid/prompts.py +++ b/ra_aid/prompts.py @@ -92,6 +92,18 @@ Thoroughness and Completeness Carefully report what you found, including all directories and files. Do not move on until you are certain you have a complete picture of the codebase structure. +Technical Debt Detection: + While exploring the codebase, use note_tech_debt to record any technical issues you discover, such as: + - Inconsistencies in code organization or naming + - Apparent dead or duplicated code + - Out-of-date dependencies or documentation + - Anti-patterns or code smells + - Confusing or poorly documented sections + When calling note_tech_debt, include: + - Clear description of the issue + - Specific file location where the issue was found + Focus on observation only - do not suggest fixes + Decision on Implementation After completing your factual enumeration and description, decide: @@ -143,6 +155,15 @@ Guidelines: Therefore, use as few tasks as needed, but no fewer. Keep tasks organized as semantic divisions of the overall work, rather than a series of steps. + Technical Debt Management: + While analyzing implementation approaches, use note_tech_debt to record discovered issues: + - Architectural inconsistencies that need future attention + - Technical limitations that may need addressing + - Dependencies that could cause future problems + - Potential scalability or maintenance concerns + - API design issues or inconsistencies + This helps maintain a record of technical concerns while staying focused on the current task. + When planning the implementation: Break the overall work into sub-tasks that are as detailed as necessary, but no more. Each sub-task should be clear and unambiguous, and should fully describe what needs to be done, including: @@ -158,7 +179,7 @@ Guidelines: Use emit_plan to store the high-level implementation plan. For each sub-task, use emit_task to store a step-by-step description. The description should be only as detailed as warranted by the complexity of the request. - + Do not implement anything yet. """ @@ -221,6 +242,13 @@ Important Notes: - Use delete_key_facts to remove facts that become outdated, irrelevant, or duplicated. - Use emit_key_snippets to manage code sections before and after modifications in batches. - Regularly remove outdated snippets with delete_key_snippets. +- While implementing, use note_tech_debt to record technical issues you encounter: + * Code organization problems requiring refactoring + * Areas needing better error handling or validation + * Performance concerns that should be addressed + * Security considerations for future hardening + * Testing gaps or coverage issues + Record these observations without deviating from your current task. Instructions: 1. Review the provided base task, plan, and key facts. diff --git a/ra_aid/tools/__init__.py b/ra_aid/tools/__init__.py index 56d4267..10d0cb6 100644 --- a/ra_aid/tools/__init__.py +++ b/ra_aid/tools/__init__.py @@ -5,6 +5,7 @@ from .read_file import read_file_tool from .fuzzy_find import fuzzy_find_project_files from .list_directory import list_directory_tree from .ripgrep import ripgrep_search +from .note_tech_debt import note_tech_debt from .memory import ( emit_research_notes, emit_plan, emit_task, get_memory_value, emit_key_facts, request_implementation, skip_implementation, delete_key_facts, emit_research_subtask, @@ -32,5 +33,6 @@ __all__ = [ 'skip_implementation', 'emit_research_subtask', 'fuzzy_find_project_files', - 'ripgrep_search' + 'ripgrep_search', + 'note_tech_debt' ] diff --git a/ra_aid/tools/note_tech_debt.py b/ra_aid/tools/note_tech_debt.py new file mode 100644 index 0000000..e1e4e89 --- /dev/null +++ b/ra_aid/tools/note_tech_debt.py @@ -0,0 +1,68 @@ +from pathlib import Path +from typing import Dict, Optional +import os +import glob +from langchain_core.tools import tool +from rich.console import Console +from rich.panel import Panel + +console = Console() + +@tool +def note_tech_debt( + description: str, + location: Optional[str] = None +) -> Dict[str, any]: + """Record a technical debt note for later review. + + Creates a markdown file in .ra-aid/tech-debt/ containing the technical debt note. + + Args: + description: Description of the technical debt issue + location: Optional file/location reference where the tech debt was found + + Returns: + Dict containing: + - success: Boolean indicating if note was saved + - note_path: Path to the created note file + - note_number: Sequential number of the note + """ + # Ensure base directory exists + base_dir = Path('.ra-aid/tech-debt') + base_dir.mkdir(parents=True, exist_ok=True) + + # Find next note number + existing_notes = glob.glob(str(base_dir / '*.md')) + next_num = 1 + if existing_notes: + numbers = [int(Path(note).stem) for note in existing_notes] + next_num = max(numbers) + 1 + + # Create note path + note_path = base_dir / f'{next_num}.md' + + # Format note content + content = [f'# Technical Debt Note {next_num}\n'] + content.append('## Description\n') + content.append(f'{description}\n') + if location: + content.append('\n## Location\n') + content.append(f'{location}\n') + + # Write note file + note_path.write_text(''.join(content)) + + # Display status panel + console.print( + Panel( + f"Created Tech Debt Note #{next_num} at {note_path}", + title="📝 Tech Debt Note", + border_style="bright_blue" + ) + ) + + return { + 'success': True, + 'note_path': str(note_path), + 'note_number': next_num + }